@santi020k/lumen-mcp 4.1.0 → 4.2.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/data/lumen-data.json +213 -81
- package/dist/contracts.d.ts +5 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +5 -1
- package/dist/data.d.ts +1 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/tools.d.ts +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +8 -3
- package/package.json +1 -1
package/data/lumen-data.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"AspectRatio": "87bf3256c5da7d2f0d00560564111c51bdd188801c28fe0e3f1520f300559911",
|
|
9
9
|
"Attachment": "9e477b26552bd9489f35e340ba53608c8e53a0cf7847745bce48f41a7ec062c4",
|
|
10
10
|
"AttachmentList": "faeb2df7207af925fe72b046dbe8735a27c6cb269ade04f1f8536ba0e00963ca",
|
|
11
|
-
"AttachmentPreview": "
|
|
11
|
+
"AttachmentPreview": "32a9fe485c4defae12e10d5faf1be9b7781c15a63bcecbfa592abe20e23cd1e2",
|
|
12
12
|
"Autocomplete": "7f17759d2ba590420283d16216c4fa12d7ab8296f4c4af259377646f61dc097f",
|
|
13
13
|
"Avatar": "60a22d4181f2a04178e43eba02c96be0a4f08d3f3e2c6693d030326749a63220",
|
|
14
14
|
"Backdrop": "69e54d576e43ebaecf5beec41c9594a8a1db1cbe8a3cb70f87a9b3da8b70eeed",
|
|
15
15
|
"BackToTop": "90d46ec1afc97a2e229d671f2da139de68c2cda2610b72231f2946f1674a73ce",
|
|
16
16
|
"Badge": "198c9577446e73721d27974d1950e98990034f63dc25fa2c01abd813945200c5",
|
|
17
|
-
"BarChart": "
|
|
17
|
+
"BarChart": "8194186ee59a53e92b719352d45ef21ac7e7ef12ab9f9f35ee9662a124b57365",
|
|
18
18
|
"Breadcrumb": "700d5860a964387089f8fc8918145bb8e82fca04f0e6c6d8fb2e20bd90cdb152",
|
|
19
19
|
"Bubble": "61d1f46dd7b7695e8c1906faf487b5b8dc12c769b170af87269db24f0b91d916",
|
|
20
20
|
"Button": "acfa846c121335fc3f7a67c8657eb5035aeef1b2fddfb4a1e1d65d7dde84ee56",
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
"CodeTabs": "61dcbf3a401afee952c7935a639452c40eae2bbac7974c748dd4de58f9e4603d",
|
|
37
37
|
"Combobox": "07e3fc8b0d0407064d8bc5d7110787bfbd94c8a122acee6917d524601f881fb8",
|
|
38
38
|
"Container": "537320bf21c9d1e522523d143c12cf1578806c9e7f68b562926adb682ee12e38",
|
|
39
|
-
"CopyButton": "
|
|
39
|
+
"CopyButton": "bfe6a15d112703cfc28d606549a9282b54afb26e57b129198ffbab0c52f14bb7",
|
|
40
40
|
"Command": "3ed71d3282ba1188018417b14b6c62e90d111eed37693d57e83ae47df2142567",
|
|
41
41
|
"ContextNavigation": "cb3b2b1ed4fa6e52f30d57b3217da7929ee218f8d00d1c31192d0604fed77e3f",
|
|
42
42
|
"ContextMenu": "8b9b5b009deb7c99fb367bf68216d9fe144163b8cb759c46154e3d0bbaed5048",
|
|
43
43
|
"ColorPicker": "c5c777fd5aa2c9d91d8eff7ef6386af457807ab01a1400cebd7204c40fdedaec",
|
|
44
|
-
"DataTable": "
|
|
45
|
-
"DatePicker": "
|
|
46
|
-
"DateRangePicker": "
|
|
44
|
+
"DataTable": "c062c4405dbb154d41c4f6c7fae477263c366e44bff5f499383c4e7a11bb05d2",
|
|
45
|
+
"DatePicker": "2654f1b189d001f04cd717a631d4a108324a7332a548d617d5bdaabc9c34a922",
|
|
46
|
+
"DateRangePicker": "b29018be38fab4dc0cf84956d030a39fd2034b1fa9a922ab9f68783e8b044151",
|
|
47
47
|
"Dialog": "2173bc40116da149c25d01f68b1d9cef7df3d1f0784dfae6775979bd420ea3a2",
|
|
48
48
|
"DialogHeader": "80d453ce7eed0a7953386108ca9f93ff2392af6c7d0df51d0ad84c07f5cef0f9",
|
|
49
49
|
"DialogTitle": "d7dd34901252475ae18b3e9be7def4e7ae4aee541aad4956298c39af19d33fbd",
|
|
50
50
|
"DialogBody": "54b5c40a530eab6136467d7cafb16223ca188b9e1bb40300d24e473619adaab1",
|
|
51
51
|
"DialogFooter": "9e9f1445f1f91dc73d5d3e2a73ca3678cb44303414dceda5e1974541fcb1ed10",
|
|
52
|
-
"DialogClose": "
|
|
52
|
+
"DialogClose": "343ed8c2a80feb4d661fbbd66cf9c866d3071ed5189c7badbd5806a08ef322f2",
|
|
53
53
|
"Direction": "ac13fa49498f18ea7734a565324402953549202080eaa3054c5068f347ec29bf",
|
|
54
54
|
"Drawer": "2f046cdf9f3b860c484b1a2e08000d04338428dd0c92344045b315bf2dadb2ed",
|
|
55
55
|
"DropdownMenu": "edca5e544781fcce8e5fe3898ebc20c34a29953b528a62a930792aa4aba920da",
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
"Graphic": "01b4a421cbc88a830cfa7002924639a1718b356a3bedfce0299e91bee43f2e1a",
|
|
66
66
|
"HoverCard": "14bc75cc0138d0e0569bec1c36a0ef13eaba5ed128595f937806ea9b7638f4ee",
|
|
67
67
|
"Icon": "5339c26722d3c3c0d9c70035ea043b4d8eb9667e38a401f124579f733994aeb6",
|
|
68
|
-
"Image": "
|
|
68
|
+
"Image": "edd4f88f3829a70f6c8cb793e2cedf73c13e930ce1a3ccf6e9a6cab3d7d1768a",
|
|
69
69
|
"DeviceFrame": "fa1fa5248cb797c09eb70da56823692c919ed6b160737cd2a0ce5d03ff3f9ce3",
|
|
70
|
-
"ImageComparison": "
|
|
71
|
-
"MediaViewport": "
|
|
70
|
+
"ImageComparison": "e547f0aad30ec9aa90660b1308a7715b33760a5f716de035aaa51fff02eb6676",
|
|
71
|
+
"MediaViewport": "648368475d1149f8c6e39f556a5ecca48675d2d4b0b89cb00a8687da21b4cf81",
|
|
72
72
|
"MediaThumbnail": "ee4fe6bf672d32a4b2bcd0e6eca1111036f2fdb5b1b8dc5d7670c291c88a7f32",
|
|
73
73
|
"MediaFilmstrip": "985898797261d0a711be9e419f034a468c628f0c39cffa8409ce3f03537224da",
|
|
74
74
|
"Illustration": "1db8d922e5a2490948bb7298d25ca50e10cf44eb297856548a5d2842be1bcfca",
|
|
@@ -81,20 +81,20 @@
|
|
|
81
81
|
"Kbd": "14825287aa0d6ffcec6b51b61b61d8a7c44d7cba1c7c40a77a7d46d0632c25de",
|
|
82
82
|
"Label": "10cb7069a9a24205bda0fd87e74d79bb9066164870dd5959b52fbdea6cd7b242",
|
|
83
83
|
"Link": "d939a1c5f221f60c5dac42dc148434326728305fa0eb0e6b10094a538f9a7b04",
|
|
84
|
-
"LineChart": "
|
|
85
|
-
"ListBox": "
|
|
84
|
+
"LineChart": "cb77c2591bbfdc8bef127af77ba4e59c45df43ad7ea9ca7a9852bb407c16d874",
|
|
85
|
+
"ListBox": "10cccfa9535d0e045dacc68fe475e306fd39f9a7bc577f57480d9798595c4a45",
|
|
86
86
|
"Marker": "b2c3679b841cee6cc4ce6322d6dd7758f85a9371bc4817a750f6de4e7c0cc3d2",
|
|
87
87
|
"Menubar": "ceaa113420c1a99cc1d7e856c04b5a75d8569473ebd8a24a83c947ec3b6608e5",
|
|
88
88
|
"Message": "26c4a46c2090e9834c52dc56bfd2d84f7d70d9fcff6885270df9839eb895ab8e",
|
|
89
89
|
"MessageScroller": "a0907d18407e38f303312d2e6540132131b07ce5001a9c7d54310a0583596a8f",
|
|
90
90
|
"NativeSelect": "419d7e692d59d9dcce5f87b2f151861bbd9ef38554a055eb2059c0831c046e31",
|
|
91
91
|
"NavigationMenu": "fc5494bfec5341fb5bcb0cca5fb451b12feade75b2a90b49088221021c4faaf0",
|
|
92
|
-
"AmountField": "
|
|
92
|
+
"AmountField": "a8c260536be29ab44ed85aecc2a659651b755f8b6acc1d81e61d9d3cd84aa51b",
|
|
93
93
|
"NumberField": "26d8eff3b5d7eeab400b77e787585865af9f5c395f6f83d30d52cffc2c9c2a50",
|
|
94
94
|
"Pagination": "1e85cbdac6aa9a6417ce9cc7e015f2dfceecba126803529c88d8fe228e2d50a9",
|
|
95
95
|
"PasswordField": "784fb8457944c5d6879a3ed1db154de8e14cae7eec3ec3ef38c3b174dd1222b2",
|
|
96
|
-
"PhoneInput": "
|
|
97
|
-
"PieChart": "
|
|
96
|
+
"PhoneInput": "2b10c9b1f6efc47e9348f6996397e3b5250f4d49166ec55987c89a51396c65b4",
|
|
97
|
+
"PieChart": "8ec3d6d45f391f05c7946a755865c8a68125e4882ab68e132bd87196d4dc59c9",
|
|
98
98
|
"Pill": "a0dffa43c0e089ae68d8dd11df5ccca3b83eb0acb5d777752f29475b0bb064a1",
|
|
99
99
|
"Popover": "91a1fc3fdc5b14b7c01fa5c9393d2b2257ed6da9b97977089f01e8643c0c1080",
|
|
100
100
|
"Progress": "42a0a7e700806ff8c31dc793cf4e79fa62b59b6dd310571bcd775d034680081c",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"Tree": "82fec780732d44258a1870898c9df666c5982433ce625ca3b8bf8d62d0dacd3c",
|
|
132
132
|
"TreeGrid": "50577908772c53c08d336747253ca7c4527145d94caac2e268e6148cb21255a5",
|
|
133
133
|
"Typography": "50c18e8bd632c5dff527a239c7e45ee010a619dc5fe1029c9e1b38d1882eecc5",
|
|
134
|
-
"VirtualList": "
|
|
134
|
+
"VirtualList": "54f833d758a881b9b71fe68495fe7eeca89c3f53e519ee5ba55cb05d12a48601",
|
|
135
135
|
"LanguageToggle": "ff65170382ae5b1241ce142b670a32bb708e92fbfbeeac6a285bd39b13c612b6",
|
|
136
136
|
"Particles": "74334a8aa6578b86f01da2ab63a96ce6c8d888e99f3bcc3ab9845667eb97d27e",
|
|
137
137
|
"AnimatedNumber": "78cebe9ac425f1f2ab81c336ccaa166ff2007d1814398f451ca67702742aaa5a",
|
|
@@ -173,10 +173,10 @@
|
|
|
173
173
|
"Affix": "554b64a7393d73e9b53ac7bb32793d16e82b609873c4c53a6ad39f844c1dcb23",
|
|
174
174
|
"SpeedDial": "7e8c5ceb0cfd8e941343809ccec8109c5686872c29438686a494c330955804a1",
|
|
175
175
|
"Grid": "d6c8c478ab1a19de7e523eec43a79f694a6ee47cff3907c509ca297b66f21234",
|
|
176
|
-
"ScatterChart": "
|
|
177
|
-
"Heatmap": "
|
|
178
|
-
"RangeChart": "
|
|
179
|
-
"ComboChart": "
|
|
176
|
+
"ScatterChart": "ff64746a2b1a73ad394676e4092859992930391899f991a268a29c9e1fd6c679",
|
|
177
|
+
"Heatmap": "c514471eda6905601d744a6a41526b99eb0b911e8473d5cae6637274a538807b",
|
|
178
|
+
"RangeChart": "0455ea2a195e94ff1f6b7560c66ec5e1cd8edb64fd447ac923d8c1a2451af3be",
|
|
179
|
+
"ComboChart": "2d20981bc182cb0a7876570145acdd66ae1ce9b37943685068ad1a422167ac5c",
|
|
180
180
|
"VisuallyHidden": "10562829ec1f598aacc2ad819e93c0cd3b257c7eec60818854a08723d9dcc888",
|
|
181
181
|
"CalendarHeatmap": "45f91bc45a6a50274962146933338b852f57488d52108129f92abe279602054b",
|
|
182
182
|
"FunnelChart": "a46ee7ad3fde691fee8140469a2a19bf9a2935e85b4ebbb8f57f372eb8b6dbfb",
|
|
@@ -191,12 +191,12 @@
|
|
|
191
191
|
"ChartMotion": "ae7fdc0ef0673641f0a9d72ccfb4dc5970dde8c05a1329f3fce9f25decf25512",
|
|
192
192
|
"MotionGroup": "5c18e73d3b2541339d7c8b292c5c44de996bfe265967ee40e74bfe5ec2482d14",
|
|
193
193
|
"VisualEffect": "fe652fd01c219994f4d23994e0eecbca86e1e5bb96b77f7eedcd9ecb9cc84adc",
|
|
194
|
-
"ApprovalCard": "
|
|
195
|
-
"PromptComposer": "
|
|
194
|
+
"ApprovalCard": "287b3ae743e5bf452c7b7084fdca0775d1910607729678435e4b19e7586035e0",
|
|
195
|
+
"PromptComposer": "111eab65bf6d59700ec4b3a19b17a45840335906b84363d2d661917b323eb496",
|
|
196
196
|
"SourceCitation": "07799bb00bf251ca0fa3d6663a7212bb9f07dacdc027e1bd01309a86b2dddb76",
|
|
197
197
|
"StreamMessage": "c3753a27af188ef2aa3cb4e0e475a788c805a9326f4e010a365d7996d534f550",
|
|
198
198
|
"ToolActivity": "4501917aa7b7ffe929bde82114d13ab5caf4a9f5ad17fa3a1f806bbe461a52d9",
|
|
199
|
-
"WorldMap": "
|
|
199
|
+
"WorldMap": "1b92c9c6bdebcd0e29afe5536ac6ceed3d6c5e79ee4a118097ab89134b60d839"
|
|
200
200
|
},
|
|
201
201
|
"nativeComponents": {
|
|
202
202
|
"Sparkline": "bf7707572d45de0978788f7825831fcb3d89e390b5a474ff8aaf518d412738c6",
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
"Heatmap": "b6bf78925c07e11be982bd072bb9ae618158968b74473f817a217091962b1f5f",
|
|
216
216
|
"Range chart": "8ea75e50ab85881f9ba5d8919e2ab173aaf37c1202e8865b95dc4af289a67327",
|
|
217
217
|
"Combo chart": "65043d6894c8abcd3be2296fde4c3c2bc1d6b2cf72e2116e37193f924a3f4260",
|
|
218
|
-
"Theme": "
|
|
218
|
+
"Theme": "fcc8da2907218646f83c494f9607d2bfe2be05b183f8d843ce92f10714f75fa9",
|
|
219
219
|
"Text": "9c38c1261814e037a58ac93d1b5c2d6008a4409bc404383a14ba96aa46ff8602",
|
|
220
220
|
"Surface": "15e5e51f77b26a3880b4c8bd04df1a596c09f1e5b8f0163803458c473027c243",
|
|
221
221
|
"Icon": "279318eeae9955028e5fe7ea20080d671bf51d3c090fac80b66f77a540cfd114",
|
|
@@ -1724,6 +1724,9 @@
|
|
|
1724
1724
|
"values": "React nodes / Astro named slots"
|
|
1725
1725
|
},
|
|
1726
1726
|
{
|
|
1727
|
+
"frameworks": [
|
|
1728
|
+
"react"
|
|
1729
|
+
],
|
|
1727
1730
|
"attribute": "onStateChange",
|
|
1728
1731
|
"defaultValue": "-",
|
|
1729
1732
|
"description": "React callback for transitions after the initial render; DOM adapters emit ui:attachment-preview-change with { state }.",
|
|
@@ -1827,7 +1830,6 @@
|
|
|
1827
1830
|
"labels",
|
|
1828
1831
|
"retryKey / retry-key",
|
|
1829
1832
|
"caption / actions",
|
|
1830
|
-
"onStateChange",
|
|
1831
1833
|
"class, className",
|
|
1832
1834
|
"...native attributes"
|
|
1833
1835
|
],
|
|
@@ -2953,6 +2955,9 @@
|
|
|
2953
2955
|
"values": "boolean (Astro / Elements attribute)"
|
|
2954
2956
|
},
|
|
2955
2957
|
{
|
|
2958
|
+
"frameworks": [
|
|
2959
|
+
"react"
|
|
2960
|
+
],
|
|
2956
2961
|
"attribute": "onDatumActivate",
|
|
2957
2962
|
"defaultValue": "-",
|
|
2958
2963
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -3182,7 +3187,6 @@
|
|
|
3182
3187
|
"presentation",
|
|
3183
3188
|
"glass",
|
|
3184
3189
|
"drilldown",
|
|
3185
|
-
"onDatumActivate",
|
|
3186
3190
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
3187
3191
|
"labels.exploreData, labels.formatDatumAction",
|
|
3188
3192
|
"series",
|
|
@@ -8149,6 +8153,9 @@
|
|
|
8149
8153
|
"values": "\"default\" | \"sm\" | \"lg\" | \"icon\""
|
|
8150
8154
|
},
|
|
8151
8155
|
{
|
|
8156
|
+
"frameworks": [
|
|
8157
|
+
"react"
|
|
8158
|
+
],
|
|
8152
8159
|
"attribute": "copiedContent, errorContent",
|
|
8153
8160
|
"defaultValue": "localized labels",
|
|
8154
8161
|
"description": "Customizes visible React feedback while label props remain the accessible announcement.",
|
|
@@ -8332,7 +8339,6 @@
|
|
|
8332
8339
|
"label, copiedLabel, errorLabel",
|
|
8333
8340
|
"resetAfter",
|
|
8334
8341
|
"toast",
|
|
8335
|
-
"copiedContent, errorContent",
|
|
8336
8342
|
"class, className",
|
|
8337
8343
|
"...native attributes"
|
|
8338
8344
|
],
|
|
@@ -9331,18 +9337,27 @@
|
|
|
9331
9337
|
"values": "\"client\" | \"manual\""
|
|
9332
9338
|
},
|
|
9333
9339
|
{
|
|
9340
|
+
"frameworks": [
|
|
9341
|
+
"react"
|
|
9342
|
+
],
|
|
9334
9343
|
"attribute": "column.render, renderDetails",
|
|
9335
9344
|
"defaultValue": "-",
|
|
9336
9345
|
"description": "Renders rich cells and stable record details without changing sort values.",
|
|
9337
9346
|
"values": "React render callbacks"
|
|
9338
9347
|
},
|
|
9339
9348
|
{
|
|
9349
|
+
"frameworks": [
|
|
9350
|
+
"react"
|
|
9351
|
+
],
|
|
9340
9352
|
"attribute": "expandedRowIds, onExpandedRowIdsChange",
|
|
9341
9353
|
"defaultValue": "-",
|
|
9342
9354
|
"description": "Preserves expansion by stable record ID across pages.",
|
|
9343
9355
|
"values": "controlled React expansion"
|
|
9344
9356
|
},
|
|
9345
9357
|
{
|
|
9358
|
+
"frameworks": [
|
|
9359
|
+
"react"
|
|
9360
|
+
],
|
|
9346
9361
|
"attribute": "DataTableSortControls",
|
|
9347
9362
|
"defaultValue": "-",
|
|
9348
9363
|
"description": "Shares controlled sort state with headers for a visible mobile sort path.",
|
|
@@ -9474,9 +9489,6 @@
|
|
|
9474
9489
|
"glass",
|
|
9475
9490
|
"layout",
|
|
9476
9491
|
"sortMode",
|
|
9477
|
-
"column.render, renderDetails",
|
|
9478
|
-
"expandedRowIds, onExpandedRowIdsChange",
|
|
9479
|
-
"DataTableSortControls",
|
|
9480
9492
|
"columns",
|
|
9481
9493
|
"rows",
|
|
9482
9494
|
"name",
|
|
@@ -9726,10 +9738,20 @@
|
|
|
9726
9738
|
"description": "Applies the tokenized liquid-glass surface treatment with backdrop-filter fallbacks; \"subtle\" and \"strong\" adjust the fill intensity.",
|
|
9727
9739
|
"values": "boolean | \"subtle\" | \"strong\""
|
|
9728
9740
|
},
|
|
9741
|
+
{
|
|
9742
|
+
"frameworks": [
|
|
9743
|
+
"astro",
|
|
9744
|
+
"react"
|
|
9745
|
+
],
|
|
9746
|
+
"attribute": "aria-invalid, aria-describedby, aria-errormessage",
|
|
9747
|
+
"defaultValue": "-",
|
|
9748
|
+
"description": "Mirrored to the visible trigger. Application errors retain precedence. Elements consumers put these attributes on the authored data-ui-date-picker-native input, not the custom-element host.",
|
|
9749
|
+
"values": "native input ARIA attributes"
|
|
9750
|
+
},
|
|
9729
9751
|
{
|
|
9730
9752
|
"attribute": "value, defaultValue",
|
|
9731
9753
|
"defaultValue": "-",
|
|
9732
|
-
"description": "Sets the selected date while the trigger shows a localized, human-readable value.",
|
|
9754
|
+
"description": "Sets the selected date while the trigger shows a localized, human-readable value. React reopens the calendar on the selected month.",
|
|
9733
9755
|
"values": "ISO date string (YYYY-MM-DD)"
|
|
9734
9756
|
},
|
|
9735
9757
|
{
|
|
@@ -9747,7 +9769,7 @@
|
|
|
9747
9769
|
{
|
|
9748
9770
|
"attribute": "UIPrimitives",
|
|
9749
9771
|
"defaultValue": "required",
|
|
9750
|
-
"description": "Enhances the form-backed input into
|
|
9772
|
+
"description": "Enhances the form-backed input into a floating Calendar popover that follows scroll and resize and uses the top layer when supported. React and registered Elements provide equivalent behavior.",
|
|
9751
9773
|
"values": "Astro runtime"
|
|
9752
9774
|
},
|
|
9753
9775
|
{
|
|
@@ -9843,16 +9865,16 @@
|
|
|
9843
9865
|
"available": true,
|
|
9844
9866
|
"behavior": {
|
|
9845
9867
|
"mode": "registration",
|
|
9846
|
-
"setup": "Call defineLumenElements() once before rendering Lumen custom elements. Registration includes this component’s interactive behavior."
|
|
9868
|
+
"setup": "Call defineLumenElements() once before rendering Lumen custom elements. Registration includes this component’s interactive behavior. Author the native input, trigger, control and popover children. Place aria-invalid, aria-describedby and aria-errormessage on the input marked data-ui-date-picker-native; the enhancer mirrors them to the visible trigger and observes later input attribute changes. Host ARIA attributes are not forwarded."
|
|
9847
9869
|
},
|
|
9848
|
-
"example": "<lumen-date-picker name=\"launchDate\" aria-label=\"Launch date\"></lumen-date-picker>",
|
|
9870
|
+
"example": "<p id=\"launch-date-hint\">Choose a launch date.</p>\n<lumen-date-picker>\n <input class=\"ui-date-picker ui-date-picker__native\" data-ui-date-picker-native type=\"date\" name=\"launchDate\" aria-label=\"Launch date\" aria-describedby=\"launch-date-hint\">\n <div class=\"ui-date-picker__control\" data-ui-date-picker-control hidden>\n <button class=\"ui-input ui-date-picker ui-date-picker__trigger\" data-ui-date-picker-trigger type=\"button\" aria-controls=\"launch-date-popover\" aria-haspopup=\"dialog\" aria-expanded=\"false\"><span data-ui-date-picker-value>Choose a date</span></button>\n <div class=\"ui-date-picker__popover\" data-ui-date-picker-popover id=\"launch-date-popover\" role=\"dialog\" aria-label=\"Choose a launch date\" hidden><lumen-calendar></lumen-calendar></div>\n </div>\n</lumen-date-picker>",
|
|
9849
9871
|
"importStatement": "import { defineLumenElements } from '@santi020k/lumen-elements/define'",
|
|
9850
9872
|
"language": "html",
|
|
9851
9873
|
"packageName": "@santi020k/lumen-elements",
|
|
9852
9874
|
"props": [],
|
|
9853
9875
|
"propsExtends": "HTMLElement attributes",
|
|
9854
9876
|
"registration": "defineLumenElements()",
|
|
9855
|
-
"source": "DatePicker: {\n attributeClasses: glassAttributeClasses('ui-date-picker-field--glass'),\n baseClassName: 'ui-
|
|
9877
|
+
"source": "DatePicker: {\n attributeClasses: glassAttributeClasses('ui-date-picker-field--glass'),\n baseClassName: 'ui-date-picker-field',\n defaults: { type: 'date' },\n tagName: 'lumen-date-picker'\n }",
|
|
9856
9878
|
"styleImport": "import '@santi020k/lumen-elements/styles.css'",
|
|
9857
9879
|
"tagName": "lumen-date-picker"
|
|
9858
9880
|
},
|
|
@@ -10018,6 +10040,9 @@
|
|
|
10018
10040
|
"values": "two DatePicker controls"
|
|
10019
10041
|
},
|
|
10020
10042
|
{
|
|
10043
|
+
"frameworks": [
|
|
10044
|
+
"react"
|
|
10045
|
+
],
|
|
10021
10046
|
"attribute": "onRangeChange",
|
|
10022
10047
|
"defaultValue": "-",
|
|
10023
10048
|
"description": "React only: receives the synchronized start and end ISO date values.",
|
|
@@ -10100,7 +10125,6 @@
|
|
|
10100
10125
|
"attributes": [
|
|
10101
10126
|
"data-ui-date-range-picker",
|
|
10102
10127
|
"children",
|
|
10103
|
-
"onRangeChange",
|
|
10104
10128
|
"data-range-state",
|
|
10105
10129
|
"class, className",
|
|
10106
10130
|
"...native attributes"
|
|
@@ -11188,6 +11212,9 @@
|
|
|
11188
11212
|
"values": "boolean"
|
|
11189
11213
|
},
|
|
11190
11214
|
{
|
|
11215
|
+
"frameworks": [
|
|
11216
|
+
"react"
|
|
11217
|
+
],
|
|
11191
11218
|
"attribute": "onClick",
|
|
11192
11219
|
"defaultValue": "-",
|
|
11193
11220
|
"description": "Calling preventDefault cancels dismissal.",
|
|
@@ -11277,7 +11304,6 @@
|
|
|
11277
11304
|
"data-ui-dialog-close",
|
|
11278
11305
|
"children",
|
|
11279
11306
|
"disabled / loading",
|
|
11280
|
-
"onClick",
|
|
11281
11307
|
"class, className",
|
|
11282
11308
|
"...native attributes"
|
|
11283
11309
|
],
|
|
@@ -14676,6 +14702,9 @@
|
|
|
14676
14702
|
"values": "boolean"
|
|
14677
14703
|
},
|
|
14678
14704
|
{
|
|
14705
|
+
"frameworks": [
|
|
14706
|
+
"react"
|
|
14707
|
+
],
|
|
14679
14708
|
"attribute": "as",
|
|
14680
14709
|
"defaultValue": "\"img\"",
|
|
14681
14710
|
"description": "React only: renders a compatible optimizer such as next/image while retaining Lumen classes and forwarding optimizer props.",
|
|
@@ -14764,7 +14793,6 @@
|
|
|
14764
14793
|
"priority",
|
|
14765
14794
|
"preload, fill, placeholder",
|
|
14766
14795
|
"invertOnDark",
|
|
14767
|
-
"as",
|
|
14768
14796
|
"class, className",
|
|
14769
14797
|
"...native attributes"
|
|
14770
14798
|
],
|
|
@@ -15189,6 +15217,9 @@
|
|
|
15189
15217
|
"values": "number"
|
|
15190
15218
|
},
|
|
15191
15219
|
{
|
|
15220
|
+
"frameworks": [
|
|
15221
|
+
"react"
|
|
15222
|
+
],
|
|
15192
15223
|
"attribute": "onValueChange",
|
|
15193
15224
|
"defaultValue": "-",
|
|
15194
15225
|
"description": "React callback for user changes. Astro and Elements emit ui:image-comparison-change with { value }.",
|
|
@@ -15347,7 +15378,6 @@
|
|
|
15347
15378
|
"before / after",
|
|
15348
15379
|
"beforeLabel / afterLabel",
|
|
15349
15380
|
"value / defaultValue",
|
|
15350
|
-
"onValueChange",
|
|
15351
15381
|
"class, className",
|
|
15352
15382
|
"...native attributes"
|
|
15353
15383
|
],
|
|
@@ -15586,6 +15616,9 @@
|
|
|
15586
15616
|
"values": "number"
|
|
15587
15617
|
},
|
|
15588
15618
|
{
|
|
15619
|
+
"frameworks": [
|
|
15620
|
+
"react"
|
|
15621
|
+
],
|
|
15589
15622
|
"attribute": "onValueChange",
|
|
15590
15623
|
"defaultValue": "-",
|
|
15591
15624
|
"description": "React callback; Astro and Elements emit ui:media-viewport-change with { zoom, x, y }.",
|
|
@@ -15708,7 +15741,6 @@
|
|
|
15708
15741
|
"label / labels",
|
|
15709
15742
|
"value / defaultValue",
|
|
15710
15743
|
"maxZoom / ratio",
|
|
15711
|
-
"onValueChange",
|
|
15712
15744
|
"disabled / locale",
|
|
15713
15745
|
"children",
|
|
15714
15746
|
"class, className",
|
|
@@ -18251,6 +18283,9 @@
|
|
|
18251
18283
|
"values": "LumenChartAnnotation[]"
|
|
18252
18284
|
},
|
|
18253
18285
|
{
|
|
18286
|
+
"frameworks": [
|
|
18287
|
+
"react"
|
|
18288
|
+
],
|
|
18254
18289
|
"attribute": "cursor, onCursorChange",
|
|
18255
18290
|
"defaultValue": "uncontrolled",
|
|
18256
18291
|
"description": "React can supply the active X identity and receive cursor changes. Astro and Elements emit ui:chart-cursor-change.",
|
|
@@ -18269,6 +18304,9 @@
|
|
|
18269
18304
|
"values": "boolean (Astro / Elements attribute)"
|
|
18270
18305
|
},
|
|
18271
18306
|
{
|
|
18307
|
+
"frameworks": [
|
|
18308
|
+
"react"
|
|
18309
|
+
],
|
|
18272
18310
|
"attribute": "onDatumActivate",
|
|
18273
18311
|
"defaultValue": "-",
|
|
18274
18312
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -18507,10 +18545,8 @@
|
|
|
18507
18545
|
"domain, xDomain",
|
|
18508
18546
|
"interactive, syncGroup",
|
|
18509
18547
|
"annotations",
|
|
18510
|
-
"cursor, onCursorChange",
|
|
18511
18548
|
"width, height",
|
|
18512
18549
|
"drilldown",
|
|
18513
|
-
"onDatumActivate",
|
|
18514
18550
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
18515
18551
|
"labels.exploreData, labels.formatDatumAction",
|
|
18516
18552
|
"series",
|
|
@@ -18822,12 +18858,67 @@
|
|
|
18822
18858
|
{
|
|
18823
18859
|
"apiReference": [
|
|
18824
18860
|
{
|
|
18861
|
+
"frameworks": [
|
|
18862
|
+
"elements"
|
|
18863
|
+
],
|
|
18864
|
+
"attribute": "empty-message, data-selection-follows-focus",
|
|
18865
|
+
"defaultValue": "\"No options available\", \"false\"",
|
|
18866
|
+
"description": "Sets the Elements empty-state text and optionally commits selection as keyboard focus moves.",
|
|
18867
|
+
"values": "string, boolean string"
|
|
18868
|
+
},
|
|
18869
|
+
{
|
|
18870
|
+
"frameworks": [
|
|
18871
|
+
"elements"
|
|
18872
|
+
],
|
|
18873
|
+
"attribute": "aria-label, required",
|
|
18874
|
+
"defaultValue": "-",
|
|
18875
|
+
"description": "Names the Elements visible list and marks its generated select required. For other label or feedback associations, author a native select child and put required and ARIA attributes on that select.",
|
|
18876
|
+
"values": "string, boolean"
|
|
18877
|
+
},
|
|
18878
|
+
{
|
|
18879
|
+
"frameworks": [
|
|
18880
|
+
"astro",
|
|
18881
|
+
"react"
|
|
18882
|
+
],
|
|
18883
|
+
"attribute": "label, aria-label, aria-labelledby",
|
|
18884
|
+
"defaultValue": "-",
|
|
18885
|
+
"description": "Names the single accessible visible list. External labels targeting or wrapping the native select also name it; explicit ARIA names take precedence.",
|
|
18886
|
+
"values": "string"
|
|
18887
|
+
},
|
|
18888
|
+
{
|
|
18889
|
+
"frameworks": [
|
|
18890
|
+
"astro",
|
|
18891
|
+
"react"
|
|
18892
|
+
],
|
|
18893
|
+
"attribute": "required, aria-invalid, aria-describedby, aria-errormessage",
|
|
18894
|
+
"defaultValue": "-",
|
|
18895
|
+
"description": "Forwarded to the visible list. Required single selection stays invalid until a choice is made; native validation focuses the list. Consumer aria-invalid wins.",
|
|
18896
|
+
"values": "native select attributes"
|
|
18897
|
+
},
|
|
18898
|
+
{
|
|
18899
|
+
"frameworks": [
|
|
18900
|
+
"react"
|
|
18901
|
+
],
|
|
18902
|
+
"attribute": "onFocus, onBlur",
|
|
18903
|
+
"defaultValue": "-",
|
|
18904
|
+
"description": "Fire once when focus enters or leaves the visible list, with the native HTMLSelectElement event target and form name.",
|
|
18905
|
+
"values": "React select focus event handlers"
|
|
18906
|
+
},
|
|
18907
|
+
{
|
|
18908
|
+
"frameworks": [
|
|
18909
|
+
"astro",
|
|
18910
|
+
"react"
|
|
18911
|
+
],
|
|
18825
18912
|
"attribute": "options",
|
|
18826
18913
|
"defaultValue": "[]",
|
|
18827
18914
|
"description": "Defines options and optional accessible sections.",
|
|
18828
18915
|
"values": "Array<string | { label, value, disabled?, section? }>"
|
|
18829
18916
|
},
|
|
18830
18917
|
{
|
|
18918
|
+
"frameworks": [
|
|
18919
|
+
"astro",
|
|
18920
|
+
"react"
|
|
18921
|
+
],
|
|
18831
18922
|
"attribute": "value",
|
|
18832
18923
|
"defaultValue": "-",
|
|
18833
18924
|
"description": "Controls the selected value or values.",
|
|
@@ -18846,12 +18937,20 @@
|
|
|
18846
18937
|
"values": "boolean"
|
|
18847
18938
|
},
|
|
18848
18939
|
{
|
|
18940
|
+
"frameworks": [
|
|
18941
|
+
"astro",
|
|
18942
|
+
"react"
|
|
18943
|
+
],
|
|
18849
18944
|
"attribute": "emptyMessage",
|
|
18850
18945
|
"defaultValue": "\"No options available\"",
|
|
18851
18946
|
"description": "Labels the empty collection state.",
|
|
18852
18947
|
"values": "string"
|
|
18853
18948
|
},
|
|
18854
18949
|
{
|
|
18950
|
+
"frameworks": [
|
|
18951
|
+
"astro",
|
|
18952
|
+
"react"
|
|
18953
|
+
],
|
|
18855
18954
|
"attribute": "selectionFollowsFocus",
|
|
18856
18955
|
"defaultValue": "false",
|
|
18857
18956
|
"description": "Selects each enabled option as keyboard focus moves instead of waiting for Space or Enter.",
|
|
@@ -18959,12 +19058,10 @@
|
|
|
18959
19058
|
"elements": {
|
|
18960
19059
|
"attributes": [
|
|
18961
19060
|
"data-ui-list-box",
|
|
18962
|
-
"
|
|
18963
|
-
"
|
|
19061
|
+
"empty-message, data-selection-follows-focus",
|
|
19062
|
+
"aria-label, required",
|
|
18964
19063
|
"multiple",
|
|
18965
19064
|
"loading",
|
|
18966
|
-
"emptyMessage",
|
|
18967
|
-
"selectionFollowsFocus",
|
|
18968
19065
|
"class, className",
|
|
18969
19066
|
"...native attributes"
|
|
18970
19067
|
],
|
|
@@ -20431,7 +20528,6 @@
|
|
|
20431
20528
|
"fractionDigits / fraction-digits",
|
|
20432
20529
|
"allowNegative / allow-negative",
|
|
20433
20530
|
"name",
|
|
20434
|
-
"onValueChange / ui:amount-change",
|
|
20435
20531
|
"invalidMessage / invalid-message",
|
|
20436
20532
|
"class, className",
|
|
20437
20533
|
"...native attributes"
|
|
@@ -21110,10 +21206,23 @@
|
|
|
21110
21206
|
"values": "string"
|
|
21111
21207
|
},
|
|
21112
21208
|
{
|
|
21113
|
-
"
|
|
21209
|
+
"frameworks": [
|
|
21210
|
+
"astro",
|
|
21211
|
+
"react"
|
|
21212
|
+
],
|
|
21213
|
+
"attribute": "inputProps",
|
|
21114
21214
|
"defaultValue": "-",
|
|
21115
|
-
"description": "Passes native number-input attributes
|
|
21116
|
-
"values": "input attributes
|
|
21215
|
+
"description": "Passes native number-input attributes without DOM patches.",
|
|
21216
|
+
"values": "input attributes"
|
|
21217
|
+
},
|
|
21218
|
+
{
|
|
21219
|
+
"frameworks": [
|
|
21220
|
+
"react"
|
|
21221
|
+
],
|
|
21222
|
+
"attribute": "inputRef",
|
|
21223
|
+
"defaultValue": "-",
|
|
21224
|
+
"description": "Exposes the native number-input ref.",
|
|
21225
|
+
"values": "React ref"
|
|
21117
21226
|
},
|
|
21118
21227
|
{
|
|
21119
21228
|
"attribute": "disabled / readOnly / required",
|
|
@@ -21176,10 +21285,19 @@
|
|
|
21176
21285
|
"values": "string"
|
|
21177
21286
|
},
|
|
21178
21287
|
{
|
|
21179
|
-
"attribute": "value
|
|
21288
|
+
"attribute": "value",
|
|
21180
21289
|
"defaultValue": "Uncontrolled",
|
|
21181
|
-
"description": "
|
|
21182
|
-
"values": "
|
|
21290
|
+
"description": "Sets the national input value; React accepts the structured phone-number contract.",
|
|
21291
|
+
"values": "string (Astro / Elements) | LumenPhoneNumber (React)"
|
|
21292
|
+
},
|
|
21293
|
+
{
|
|
21294
|
+
"frameworks": [
|
|
21295
|
+
"react"
|
|
21296
|
+
],
|
|
21297
|
+
"attribute": "onValueChange",
|
|
21298
|
+
"defaultValue": "-",
|
|
21299
|
+
"description": "Receives formatting, validity, and E.164 changes in React.",
|
|
21300
|
+
"values": "(value: LumenPhoneNumber) => void"
|
|
21183
21301
|
},
|
|
21184
21302
|
{
|
|
21185
21303
|
"attribute": "size",
|
|
@@ -21339,7 +21457,6 @@
|
|
|
21339
21457
|
"elements": {
|
|
21340
21458
|
"attributes": [
|
|
21341
21459
|
"id / input-id",
|
|
21342
|
-
"inputProps / inputRef",
|
|
21343
21460
|
"disabled / readOnly / required",
|
|
21344
21461
|
"errorMessage / showValidationError",
|
|
21345
21462
|
"countries",
|
|
@@ -21350,7 +21467,7 @@
|
|
|
21350
21467
|
"locale",
|
|
21351
21468
|
"name",
|
|
21352
21469
|
"placeholder",
|
|
21353
|
-
"value
|
|
21470
|
+
"value",
|
|
21354
21471
|
"size",
|
|
21355
21472
|
"class, className",
|
|
21356
21473
|
"...native attributes"
|
|
@@ -21643,6 +21760,9 @@
|
|
|
21643
21760
|
"values": "boolean (Astro / Elements attribute)"
|
|
21644
21761
|
},
|
|
21645
21762
|
{
|
|
21763
|
+
"frameworks": [
|
|
21764
|
+
"react"
|
|
21765
|
+
],
|
|
21646
21766
|
"attribute": "onDatumActivate",
|
|
21647
21767
|
"defaultValue": "-",
|
|
21648
21768
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -21837,7 +21957,6 @@
|
|
|
21837
21957
|
"variant",
|
|
21838
21958
|
"glass",
|
|
21839
21959
|
"drilldown",
|
|
21840
|
-
"onDatumActivate",
|
|
21841
21960
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
21842
21961
|
"labels.exploreData, labels.formatDatumAction",
|
|
21843
21962
|
"series",
|
|
@@ -30126,18 +30245,27 @@
|
|
|
30126
30245
|
"values": "\"mounted\" | \"data\""
|
|
30127
30246
|
},
|
|
30128
30247
|
{
|
|
30248
|
+
"frameworks": [
|
|
30249
|
+
"react"
|
|
30250
|
+
],
|
|
30129
30251
|
"attribute": "items",
|
|
30130
30252
|
"defaultValue": "-",
|
|
30131
30253
|
"description": "React: enable data rendering with immutable records; do not supply children.",
|
|
30132
30254
|
"values": "readonly T[]"
|
|
30133
30255
|
},
|
|
30134
30256
|
{
|
|
30257
|
+
"frameworks": [
|
|
30258
|
+
"react"
|
|
30259
|
+
],
|
|
30135
30260
|
"attribute": "getKey",
|
|
30136
30261
|
"defaultValue": "-",
|
|
30137
30262
|
"description": "React data mode: stable unique item identity.",
|
|
30138
30263
|
"values": "(item: T, index: number) => string | number"
|
|
30139
30264
|
},
|
|
30140
30265
|
{
|
|
30266
|
+
"frameworks": [
|
|
30267
|
+
"react"
|
|
30268
|
+
],
|
|
30141
30269
|
"attribute": "renderItem",
|
|
30142
30270
|
"defaultValue": "-",
|
|
30143
30271
|
"description": "React data mode: render only the visible window and focused neighbors.",
|
|
@@ -30242,9 +30370,6 @@
|
|
|
30242
30370
|
"glass",
|
|
30243
30371
|
"tabindex",
|
|
30244
30372
|
"mode",
|
|
30245
|
-
"items",
|
|
30246
|
-
"getKey",
|
|
30247
|
-
"renderItem",
|
|
30248
30373
|
"itemSize",
|
|
30249
30374
|
"overscan",
|
|
30250
30375
|
"class, className",
|
|
@@ -39081,6 +39206,9 @@
|
|
|
39081
39206
|
"values": "boolean (Astro / Elements attribute)"
|
|
39082
39207
|
},
|
|
39083
39208
|
{
|
|
39209
|
+
"frameworks": [
|
|
39210
|
+
"react"
|
|
39211
|
+
],
|
|
39084
39212
|
"attribute": "onDatumActivate",
|
|
39085
39213
|
"defaultValue": "-",
|
|
39086
39214
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -39301,7 +39429,6 @@
|
|
|
39301
39429
|
"elements": {
|
|
39302
39430
|
"attributes": [
|
|
39303
39431
|
"drilldown",
|
|
39304
|
-
"onDatumActivate",
|
|
39305
39432
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
39306
39433
|
"labels.exploreData, labels.formatDatumAction",
|
|
39307
39434
|
"formatX, formatY",
|
|
@@ -39594,6 +39721,9 @@
|
|
|
39594
39721
|
"values": "boolean (Astro / Elements attribute)"
|
|
39595
39722
|
},
|
|
39596
39723
|
{
|
|
39724
|
+
"frameworks": [
|
|
39725
|
+
"react"
|
|
39726
|
+
],
|
|
39597
39727
|
"attribute": "onDatumActivate",
|
|
39598
39728
|
"defaultValue": "-",
|
|
39599
39729
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -39751,7 +39881,6 @@
|
|
|
39751
39881
|
"colorScale, midpoint",
|
|
39752
39882
|
"showLegend",
|
|
39753
39883
|
"drilldown",
|
|
39754
|
-
"onDatumActivate",
|
|
39755
39884
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
39756
39885
|
"labels.exploreData, labels.formatDatumAction",
|
|
39757
39886
|
"data",
|
|
@@ -39960,6 +40089,9 @@
|
|
|
39960
40089
|
"values": "boolean (Astro / Elements attribute)"
|
|
39961
40090
|
},
|
|
39962
40091
|
{
|
|
40092
|
+
"frameworks": [
|
|
40093
|
+
"react"
|
|
40094
|
+
],
|
|
39963
40095
|
"attribute": "onDatumActivate",
|
|
39964
40096
|
"defaultValue": "-",
|
|
39965
40097
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -40121,7 +40253,6 @@
|
|
|
40121
40253
|
"elements": {
|
|
40122
40254
|
"attributes": [
|
|
40123
40255
|
"drilldown",
|
|
40124
|
-
"onDatumActivate",
|
|
40125
40256
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
40126
40257
|
"labels.exploreData, labels.formatDatumAction",
|
|
40127
40258
|
"data",
|
|
@@ -40332,6 +40463,9 @@
|
|
|
40332
40463
|
"values": "boolean (Astro / Elements attribute)"
|
|
40333
40464
|
},
|
|
40334
40465
|
{
|
|
40466
|
+
"frameworks": [
|
|
40467
|
+
"react"
|
|
40468
|
+
],
|
|
40335
40469
|
"attribute": "onDatumActivate",
|
|
40336
40470
|
"defaultValue": "-",
|
|
40337
40471
|
"description": "Enables React datum actions and receives validated values with raw identities. Navigation and filtering remain application owned.",
|
|
@@ -40493,7 +40627,6 @@
|
|
|
40493
40627
|
"elements": {
|
|
40494
40628
|
"attributes": [
|
|
40495
40629
|
"drilldown",
|
|
40496
|
-
"onDatumActivate",
|
|
40497
40630
|
"explore-data-label, datum-action-prefix, datumActionFormatter",
|
|
40498
40631
|
"labels.exploreData, labels.formatDatumAction",
|
|
40499
40632
|
"series",
|
|
@@ -44490,7 +44623,6 @@
|
|
|
44490
44623
|
"requestId",
|
|
44491
44624
|
"label, statusLabel",
|
|
44492
44625
|
"approveLabel, rejectLabel",
|
|
44493
|
-
"onResponse / ui:approval-response",
|
|
44494
44626
|
"class, className",
|
|
44495
44627
|
"...native attributes"
|
|
44496
44628
|
],
|
|
@@ -44785,8 +44917,6 @@
|
|
|
44785
44917
|
"maxLength",
|
|
44786
44918
|
"pending, disabled",
|
|
44787
44919
|
"submitOnEnter",
|
|
44788
|
-
"onPromptSubmit / ui:prompt-submit",
|
|
44789
|
-
"onStop / ui:prompt-stop",
|
|
44790
44920
|
"class, className",
|
|
44791
44921
|
"...native attributes"
|
|
44792
44922
|
],
|
|
@@ -45507,6 +45637,9 @@
|
|
|
45507
45637
|
"values": "boolean"
|
|
45508
45638
|
},
|
|
45509
45639
|
{
|
|
45640
|
+
"frameworks": [
|
|
45641
|
+
"react"
|
|
45642
|
+
],
|
|
45510
45643
|
"attribute": "onCountrySelect",
|
|
45511
45644
|
"defaultValue": "-",
|
|
45512
45645
|
"description": "React callback; Astro and Elements emit ui:world-map-select with countryId, highlighted and label.",
|
|
@@ -45686,7 +45819,6 @@
|
|
|
45686
45819
|
"labels / listLabel",
|
|
45687
45820
|
"selectedCountry",
|
|
45688
45821
|
"interactive / animated",
|
|
45689
|
-
"onCountrySelect",
|
|
45690
45822
|
"heading / description / caption",
|
|
45691
45823
|
"CSS variables",
|
|
45692
45824
|
"class, className",
|
|
@@ -45959,11 +46091,11 @@
|
|
|
45959
46091
|
}
|
|
45960
46092
|
],
|
|
45961
46093
|
"docs": {
|
|
45962
|
-
"aiUsage": "# AI Usage Guide\n\nUse this guide when an AI agent is building an app with Lumen or updating examples that teach\nanother agent how to use the library.\n\nThis branch documents the locally prepared Lumen 4 candidate. Version 4 install examples become\nusable after publication; the public plugin and hosted MCP still describe their deployed version.\nUse the workspace packages and generated local MCP snapshot to evaluate this candidate.\n\n## Install in ChatGPT or Codex\n\nThe [Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nis published in the shared Plugins Directory for ChatGPT and Codex.\n\n1. Open the listing and select **Install plugin**. Sign in to ChatGPT if prompted.\n2. Start a request with **@Lumen UI**, or choose one of the listing's starter prompts.\n3. Describe the interface and target framework, for example: “Find the right Lumen components for\n an accessible React settings screen.”\n\nThe plugin combines the Lumen workflow skill with the hosted public MCP catalog. It requires no\nseparate Lumen account, API key, or local server. Its tools only read public Lumen documentation;\nthey do not access private repositories or modify files and services. Your coding agent performs\nimplementation work using the retrieved contracts and its own permissions.\n\nSee the [publication record](./openai-plugin-submission.md) for version and verification details.\n\n## Install the Lumen Agent Skill\n\nLumen ships a portable Agent Skill for AI coding tools that support the open `SKILL.md` format:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe source lives in [`skills/lumen-ui`](../skills/lumen-ui). The skill provides the durable\nframework-selection, component-composition, theming, accessibility, and verification workflow.\nUse it by naming `$lumen-ui` in a prompt, or let a compatible agent trigger it from Lumen-specific\nrequests.\n\nThe skill and MCP server complement each other: the skill explains how to work, while\n`@santi020k/lumen-mcp` supplies structured catalog search, framework-specific contracts, recipes,\nsource, tokens, resources, and rules.\n\n## Pick the Right Package\n\nUse the framework requested by the user. Every adapter shares the same Lumen foundation.\n\n| App Target | Install | Import Components From | Load Styles From |\n| -------------------------- | ---------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------- |\n| Astro | `@santi020k/lumen-astro` | `@santi020k/lumen-astro` | `@santi020k/lumen-astro/styles.css` |\n| React | `@santi020k/lumen-react` | `@santi020k/lumen-react` | `@santi020k/lumen-react/styles.css` |\n| React Hook Form composites | `@santi020k/lumen-react-hook-form` | `@santi020k/lumen-react-hook-form` | Uses React styles |\n| Web Components | `@santi020k/lumen-elements` | `@santi020k/lumen-elements/define` | `@santi020k/lumen-elements/styles.css` |\n| React Native / Expo | `@santi020k/lumen-react-native` | `@santi020k/lumen-react-native` | Not applicable |\n| Apple / SwiftUI | Swift Package `https://github.com/santi020k/lumen`, pinned to `4.1.0` | `LumenUI` | Not applicable |\n| Android / Compose | Maven Central `com.santi020k:lumen-compose:4.0.0` | `com.santi020k.lumen` | Not applicable |\n| Wear OS / Compose | Maven Central `com.santi020k:lumen-compose-wear:4.0.0` | `com.santi020k.lumen` | Not applicable |\n| Package metadata | `@santi020k/lumen-core` | `@santi020k/lumen-core` | Not applicable |\n| Optional web brand icons | `@santi020k/lumen-icons-brand` | Register once, then use the framework `Icon` | Uses framework styles |\n\nEach framework package includes the shared foundation. Install `@santi020k/lumen` separately only\nwhen you need its framework-neutral CLI or registry metadata.\n\nFor React Native, mount one `LumenProvider` near the app root. For SwiftUI, attach the `LumenUI`\nSwift Package product to the application target, use an exact or compatible release-version rule,\nand apply `.lumenTheme(...)` near the root. For Compose, install\n`com.santi020k:lumen-compose:4.0.0` from Maven Central and wrap content in `LumenTheme`. Native\nadapters do not load CSS or the Astro runtime.\n\nReact Native can use ordinary React hooks and platform-neutral application hooks. Import native\nLumen controllers such as `useDialog`, `useTabs`, `useSelect`, `useLanguageToggle`,\n`useThemeToggle`, and `useToast` from `@santi020k/lumen-react-native`; their state semantics track\nthe reusable React contracts while their returned props target native Lumen components. Never\nimport DOM-dependent hooks from `@santi020k/lumen-react` into a native bundle. Share business state\nin an application workspace package and keep the Lumen rendering adapter at each app boundary.\n\nFor watchOS, use the focused `LumenWatch*` contracts from the same Swift package. For Wear OS,\ninstall `com.santi020k:lumen-compose-wear:4.0.0` and wrap wearable content in `LumenWearTheme`\ninside the application-owned Wear Material theme. Keep complications, tiles, rotary input,\nAlways On behavior, haptics, synchronization, navigation, and health or safety logic app-owned.\n\nBrand marks are intentionally excluded from the default web Lucide bundle. In Astro, React, or Web\nComponents, install and register the optional pack once before rendering a namespaced icon:\n\n```ts\nimport { registerLumenBrandIcons } from \"@santi020k/lumen-icons-brand\";\n\nregisterLumenBrandIcons();\n```\n\nThen use any canonical Font Awesome Free brand name, such as `brand:github`, `brand:discord`,\n`brand:figma`, `brand:linkedin`, `brand:whatsapp`, or `brand:youtube`, through the existing Astro,\nReact, or Web Component `Icon` API. The package also provides `brand:x` as an alias for\n`brand:x-twitter`.\n\nBrand marks keep their recognizable filled geometry instead of being redrawn as Lucide outlines.\nThey still inherit the same Lumen size, alignment, `currentColor`, and accessible-label behavior as\nthe default icon catalog. React Native, SwiftUI, and Compose already include the corresponding\ncomplete brand catalog and accept `brand:github`, `.brandGithub`, and\n`LumenIconName.BrandGithub`, respectively, without a separate package.\n\n## Consumer composition in v4\n\n- For reporting filters, use React `DateRangeInput` with controlled `value`/`onValueChange`, localized\n `labels`, bounds and explicit presets. Keep draft changes inside the picker until Apply. See the\n [reporting example](https://lumen.santi020k.com/docs/web/reporting) and [migration guide](migrating-to-lumen.md).\n- Keep date-only values as validated `YYYY-MM-DD` strings. Do not round-trip a local date through\n UTC timestamps. Use the native adapter's existing date field and application-owned calendar/time-zone rules.\n- Compose a metric card from `Card`, `Stat` and a chart with `presentation=\"bare\"`; do not invent\n MetricCard or Status components. Products own status labels, aggregation, currency units and rounding.\n- Use unique chart X identities, abbreviated `xLabel` text and full `formatCategory` labels.\n Preserve absent observations explicitly; never turn missing data into a fabricated zero.\n- Compare two media treatments with `ImageComparison` and matching source dimensions. Supply a\n visible localized control label and useful image alternatives. See [image comparison](image-comparison.md).\n- Use `Prose` for ordinary Markdown, `Code`/`CodeTabs` for examples, and `CopyButton` for dynamic\n command output. Localize clipboard success/error feedback; do not swallow rejected writes.\n- Use public `asChild` composition when a semantic link needs button styling. Disable nested form\n controls separately. On Input, native numeric `size` and Lumen `visualSize` have different meanings.\n- Map the complete semantic theme in light and dark modes, including `brand-solid`/`on-brand` and\n `danger`/`on-danger`. Text-safe colors matter more than matching a decorative swatch.\n- NavigationMenu, Sidebar and Sheet compose a documentation shell. Keep ordinary links in normal\n Tab order and application routing in the app. Do not rebuild primitives with `ui-*` classes.\n\n### Theme bootstrap and generated command output\n\nWhen the application owns theme state, use `ThemeToggle controlled` and its own click handler.\nInitialize the document in the head before paint; unavailable storage must not prevent rendering:\n\n```html\n<script>\n let theme = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n try {\n const saved = localStorage.getItem('application-theme')\n if (saved === 'light' || saved === 'dark') theme = saved\n } catch {\n // Private browsing or storage policy can deny access.\n }\n document.documentElement.dataset.theme = theme\n document.documentElement.classList.toggle('dark', theme === 'dark')\n</script>\n```\n\nThe owner's click handler updates the document and the toggle's `aria-pressed` state, and persists\ninside a guarded storage write. Use a server-rendered theme when the application already has one.\nA controlled Lumen toggle does not read storage or change that state itself.\n\nFor commands generated from a form, point `CopyButton` at the current output element. It reads the\nlatest text when clicked, so the application only updates the output rather than managing clipboard\npromises or feedback timers:\n\n```astro\n---\nimport { CopyButton } from '@santi020k/lumen-astro'\n---\n\n<output id=\"generated-command\">pnpm run check</output>\n<CopyButton target=\"#generated-command\" label=\"Copy command\" copiedLabel=\"Command copied\" errorLabel=\"Copy failed; select the command and copy it manually\">\n Copy command\n</CopyButton>\n```\n\n## MCP-Assisted Workflow\n\nConnect `@santi020k/lumen-mcp` when the agent supports Model Context Protocol. The server returns\nvalidated structured content as well as readable text, so agents can select components without\nscraping documentation.\n\nThe MCP per-component catalog covers Astro, React, Web Components, React Native, SwiftUI, and\nCompose. Use `lumen_list_native_components` to discover platform availability and\n`lumen_get_native_component` for installation, setup, API, examples, accessibility guidance, and\noptional reference source. The skill still verifies installed packages when local versions differ.\n\n```bash\ncodex mcp add lumen -- npx -y @santi020k/lumen-mcp\n```\n\nFor Codex project configuration:\n\n```toml\n[mcp_servers.lumen]\nenabled = true\ncommand = \"npx\"\nargs = [\"-y\", \"@santi020k/lumen-mcp\"]\n```\n\nUse this sequence:\n\n1. Read `lumen://meta` and `lumen://diagnostics` to identify and verify the snapshot.\n2. Read `lumen://rules`.\n3. Search for the use case with `lumen_search`, passing the target `framework` when known.\n4. Inspect the selected framework with `lumen_get_component` and `detail: \"usage\"`.\n5. Follow its framework behavior setup and hook/controller contract.\n6. Read related multi-component guidance with `lumen_get_recipe`.\n7. Request `detail: \"source\"` only when the usage contract is insufficient.\n8. Read `lumen://tokens` before introducing custom styling.\n9. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades to identify contracts that need refreshing.\n\nExample framework-aware component request:\n\n```json\n{\n \"name\": \"lumen_get_component\",\n \"arguments\": {\n \"name\": \"DateRangePicker\",\n \"framework\": \"react\",\n \"detail\": \"usage\"\n }\n}\n```\n\nStable resources are available at `lumen://rules`, `lumen://tokens`, `lumen://components`,\n`lumen://meta`, `lumen://catalog-manifest`, `lumen://diagnostics`,\n`lumen://components/{name}`, and `lumen://recipes/{name}`. See\n[`packages/mcp/README.md`](../packages/mcp/README.md) for Claude Desktop, Cursor, generic stdio,\nlocal repository, and programmatic examples.\n\n## Minimal Setup\n\nImport the stylesheet once in the app's global CSS, root layout, or app entry.\n\nThis section applies to web targets. Native adapters use their provider or theme APIs instead of a\nstylesheet.\n\n```css\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nIf Tailwind is present, keep both imports in the same shared CSS entry.\n\n```css\n@import \"@santi020k/lumen-react/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nKeep the layer prelude first. It places Tailwind base styles below Lumen components and Tailwind\nutilities above them, so consumer spacing, display, radius, width, and responsive visibility\nclasses override Lumen defaults predictably.\n\nAstro apps should mount `UIPrimitives` once in the root layout when using interactive primitives\nsuch as dialogs, menus, popovers, tabs, carousels, command lists, toggles, validated forms, or\ntoasts. Do not place `UIPrimitives` in each component example or next to each primitive instance;\none root include enhances all matching `data-ui-*` markup on the page.\n\n```astro\n---\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<UIPrimitives />\n```\n\n## Astro\n\nUse direct named imports from `@santi020k/lumen-astro`.\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n</Card>\n```\n\n`Stat` defaults to a `div`. Choose `as=\"article\"` for a self-contained metric with supporting\ncontent, or `as=\"section\"` for a labeled region. Do not use a semantic root solely for styling.\nIts visual variants are `default`, `accent`, and `glass`; reserve `accent` for featured metrics and\nkeep glass selective.\n\nFor long reading surfaces, use `Anchor` for local section navigation and pass each item’s optional\nheading `depth`, `index`, and `description` when hierarchy matters. Use `activationOffset` for fixed\napplication headers. Use `ScrollProgress` for document position; keep `Progress` for\napplication-controlled task completion. In Astro, dispatch `ui:progress-change` with\n`{ value, max? }` from the `Progress` root when browser state changes after hydration. Non-finite or\nmissing fields keep the current state, and every adapter clamps to finite ARIA values. Use `Spinner`\nfor unknown-duration waits.\n\nSkeleton and Spinner are decorative (`aria-hidden`) unless named. For a group of placeholders,\nannounce loading once: wrap them in one `role=\"status\"` region with `aria-busy=\"true\"` and a\n`VisuallyHidden` label, and keep the Skeletons unnamed. Size placeholders with `shape`\n(`rectangle`, `text`, `circle`), `width`, and `height`, and use deterministic widths rather than\nrandom values so server and client markup match. Data fetching and when to show placeholders stay\nin the application. `Pill`\naccepts `neutral`, `brand`, and `outline`\nvariants, plus native link and count composition.\n\n## React\n\nReact components share the same visual classes and data attributes as Astro components. For\nbehavior-heavy primitives, use the React behavior hooks from `@santi020k/lumen-react` (`useDialog`,\n`usePopover`, `useDropdownMenu`, `useTabs`, `useSelect`, `useToast`, and `useTooltip`), which track\nthe Astro runtime's ARIA, keyboard, Escape, dismissal, and toast controller semantics.\n\nThe React package entry is a client boundary. In Next.js, it can be imported by a Server Component\nwithout evaluating Lumen's contexts and hooks in the React server runtime; the rendered Lumen\nsubtree becomes a Client Component boundary. Vite and other client-rendered React applications use\nthe same entry without additional setup.\nFor stateless Badge, Card composition, layout, form-label, progress, loading, typography, and\nvisually-hidden usage in a React Server Component, import from\n`@santi020k/lumen-react/server`. That entrypoint contains the implementations directly and avoids\ncreating a client boundary. Keep event handlers, hooks, and interactive primitives on the package\nroot inside a Client Component.\n`Button`, `Link`, `ButtonLink`, and `Input` accept React 19 DOM refs directly as props.\n`Button asChild` composes the button contract onto one child element. Across adapters, native\nnumeric `size` remains available on `Input` and `NativeSelect`; use `visualSize` in Astro and React,\nor `visual-size` in Elements, for presentation.\n`Stat as` and `variant` follow the same semantic-root and visual guidance as the Astro component.\n\n```tsx\nimport \"@santi020k/lumen-react/styles.css\";\nimport { Button, Card, Input, Label } from \"@santi020k/lumen-react\";\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n </Card>\n );\n}\n```\n\nFor React Hook Form, spread `register()` directly onto native-backed controls. Install\n`@santi020k/lumen-react-hook-form` only for controlled composites such as `Select`, `DatePicker`,\n`InputOTP`, and `ListBox`. Do not combine React Hook Form and `useFormValidation` unless the\napplication intentionally coordinates two validation sources. For schema validation, use\n`zodResolver` or `yupResolver` from `@hookform/resolvers`; keep Zod or Yup in the application and\nrender the resulting React Hook Form errors through Lumen's `FieldError` contract.\n\n## Web Components\n\nRegister custom elements once, then use `lumen-*` tags in HTML. The elements adapter includes\ninteractive behavior parity for DataTable, Dialog, Popover, DropdownMenu, ContextMenu, Tabs,\nSelect, ThemeBuilder, Toast, Tooltip, forms, RichTextEditor, Schedule, and VirtualList: selection,\nvalidation, rich text command, context menu, schedule drag/drop, theme, and range events, ARIA\nstate, roving/listbox keyboard paths, Escape/outside dismissal, focus return/trapping, native\nselect form participation, and the same toast controller events as the Astro runtime.\n\n```html\n<script type=\"module\">\n import { defineLumenElements } from \"@santi020k/lumen-elements/define\";\n\n defineLumenElements();\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input\n id=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n ></lumen-input>\n <lumen-button type=\"submit\">Subscribe</lumen-button>\n</lumen-card>\n```\n\nFor a surface that uses only Badge, Button, Card, and Combobox, import\n`@santi020k/lumen-elements/components/badge`, `/button`, `/card`, and `/combobox` and call their\ncorresponding `defineLumenBadge`, `defineLumenButton`, `defineLumenCard`, and `defineLumenCombobox`\nfunctions. Those implementation-level entrypoints avoid the complete catalog bundle; the Combobox\nentrypoint includes its filtering and keyboard behavior. Continue using `defineLumenElements` for\nany component without a granular entrypoint.\n\nFor a foundation-only custom-element surface, import `defineLumenFoundations` from\n`@santi020k/lumen-elements/components/foundations`. It registers Card compound parts, Container,\nDirection, Grid, Label, Separator, Skeleton, Spinner, Stack, Typography, and VisuallyHidden without\npulling the complete catalog implementation.\n\nPut input elements inside a native `<form data-ui-form>`. Scalar Lumen controls participate in\n`FormData`, validity, disabled state, focus, and reset through form-associated custom-element\nbehavior with a native-control fallback. Do not generate `<lumen-form>` as the submission\ncontainer.\n\n## Component Selection\n\nThe shared catalog includes:\n\n`Accordion`, `Alert`, `AlertDialog`, `Agenda`, `AspectRatio`, `Attachment`, `Autocomplete`,\n`Avatar`, `Badge`, `BarChart`, `Breadcrumb`, `Bubble`, `Button`, `ButtonGroup`, `Calendar`, `Card`,\n`Carousel`, `Chart`, `Checkbox`, `CheckboxGroup`, `Collapsible`, `Code`, `Combobox`, `Command`,\n`Container`, `ColorPicker`,\n`ContextMenu`, `DataTable`, `DatePicker`, `DateRangePicker`, `Dialog`, `Direction`, `Drawer`,\n`DropdownMenu`, `Empty`, `ErrorState`, `ErrorSummary`, `Field`, `FieldError`, `Form`, `Grid`, `HoverCard`, `Icon`,\n`Graphic`, `Backdrop`, `Illustration`, `Input`, `InputGroup`, `InputOTP`, `Item`, `KanbanBoard`, `KanbanColumn`, `Kbd`, `Label`, `LineChart`, `ListBox`, `Marker`, `Menubar`,\n`Message`, `MessageScroller`, `NativeSelect`, `ContextNavigation`, `NavigationMenu`, `NumberField`, `Pagination`,\n`PasswordField`, `PieChart`, `Popover`, `Progress`, `CopyButton`, `RadioGroup`, `Resizable`, `RichTextEditor`,\n`ScrollArea`, `Schedule`, `SearchField`, `Select`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`,\n`Slider`, `Sparkline`, `Spinner`, `Stack`, `Switch`, `Table`, `Tabs`, `TagGroup`, `Textarea`, `ThemeBuilder`,\n`TimeField`, `Toast`, `Toggle`, `ToggleGroup`, `Tooltip`, `Tree`, `TreeGrid`, `Typography`, and\n`VirtualList`, and `VisuallyHidden`.\n\n## Product Recipes\n\n- Follow [the error-handling guide](./error-handling.md) when a generated interface needs failure\n feedback. Use `FieldError` and `ErrorSummary` for validation, `Alert` for persistent inline\n problems, `Toast` for brief non-blocking failures, and `ErrorState` only when a page or region\n cannot show its primary content. Keep exception capture, logging, retry policy, and safe message\n mapping in application code. For React Native, SwiftUI, and Compose, follow the guide's native API\n mapping and announcement rules; do not invent a cross-platform error manager inside the component\n layer.\n\n- Start full product surfaces from the bundled `analytics-dashboard`, `saas-admin`,\n `commerce-dashboard`, `project-workspace`, or `auth-onboarding` recipe. Install with\n `lumen add <recipe>` for Astro, or add `--target react` / `--target elements`. Keep the generated\n shared template stylesheet, then replace sample data and routes while preserving public Lumen\n components, semantic tokens, accessible names, focus paths, and responsive navigation.\n- Use `Sparkline` for a compact trend beside a labelled metric, `BarChart` for categorical\n comparison, `LineChart` for ordered or time-series data, `PieChart` for a small\n part-to-whole breakdown. Pass already-aggregated `LumenChartSeries` data; keep statistics,\n resampling, model evaluation, and domain calculations in application code. Keep `showTable`\n enabled unless the same values are already available in a nearby semantic table. Prefer a bar\n chart when precise slice comparison matters or the pie would exceed roughly seven categories.\n Use `ScatterChart` for numeric relationships, `Heatmap` for a labeled matrix, `RangeChart` for\n low-to-high intervals, and `ComboChart` only when mixed marks share a meaningful domain.\n Use `WaterfallChart` for signed balance changes, `LollipopChart` for ranking, `DumbbellChart`\n for reference/current comparison, and `BulletChart` for actual/target comparison. Use `Histogram`\n for supplied bins, `BoxPlot` for supplied distribution summaries, `CalendarHeatmap` for daily\n activity, and `FunnelChart` for ordered cohort stages. Retrieve each specialized data contract;\n these components do not all accept `LumenChartSeries`. Keep statistical and conversion policy\n in the application, preserve missing days, and use density for unequal histogram bin widths. Use\n `Chart` as the custom SVG/canvas escape hatch. Preserve `null` gaps and the default accessible\n summary and fallback data. See `docs/data-visualization.md` for the full contract.\n Pass `labels` to replace every library-owned visible and assistive phrase. Finite zero is valid\n data, while an empty chart has no finite values; do not silently reinterpret an all-zero series\n as missing. Set `showTable={false}` (`showData={false}` on native adapters) only when an\n equivalent accessible table or scoreboard is adjacent.\n Keep stable identity in `datum.x` and put display text in `datum.xLabel`. Use `formatCategory`\n and `formatValue` for localized axes, SVG titles, and data tables. Choose\n `presentation=\"bare\"` inside an existing Card, `categoryWidth` for long horizontal labels,\n `markers=\"auto\"` or a numeric marker step for dense lines, and `emptyLabel` for actionable\n source-specific guidance.\n- Use `Icon name=\"search\"` or any Lucide icon name for standard interface pictograms. Kebab-case\n names and Lucide aliases work. Pair decorative icons with `decorative`, and pass `label` only\n when the icon carries meaning on its own. On the web, install and register\n `@santi020k/lumen-icons-brand` for supported brand marks; native adapters include those names.\n Then use `brand:github` or the adapter's generated equivalent. Render project-specific or\n unsupported brand SVGs through the default `Icon` slot. Astro reports unknown named icons during\n development instead of failing silently.\n- Use `Schedule` with `Agenda`, `Calendar`, and `DatePicker` for booking, CRM, content calendar, and\n event planning screens. Mark slots with `data-ui-schedule-slot` and events with\n `data-ui-schedule-event`; the runtime emits `ui:schedule-change` when a draggable event is moved.\n `Calendar` renders a hidden input plus selectable date grid; Astro `UIPrimitives` and registered\n Web Components wire month navigation and roving keyboard focus directly, while React apps can use\n the `Calendar` component or `useCalendar` for the same date selection contract.\n Astro `UIPrimitives` and registered Web Components bind schedule drag/drop directly; React apps\n can use `useSchedule` for `rootProps`, `getEventProps`, `getSlotProps`, and `ui:schedule-change`\n parity. Use `createScheduleSlots`, `moveScheduleEvent`, and `expandRecurringScheduleEvent` from\n `@santi020k/lumen-core` for reusable schedule state; use `getScheduleConflicts` and\n `canPlaceScheduleEvent` before committing drag/drop changes. Persist local schedule edits with\n `createScheduleStorageKey`, `saveScheduleEvents`, and `loadScheduleEvents` against any\n `localStorage`-compatible adapter. Use `resizeScheduleEvent` and `resizeScheduleEvents` for\n snap-to-grid resize handles.\n- Use `KanbanBoard` and `KanbanColumn` for status-based workspaces while continuing to use `Card` for\n items. Mark items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a dedicated button;\n do not make the whole card draggable. Astro and Elements emit the cancellable, controlled\n `ui:kanban-move-request` event, and React provides `useKanban`. The event reports `itemId`,\n `fromColumn`, `toColumn`, optional `beforeId`, and keyboard or pointer input, but never moves DOM or\n persists application data. The host owns validation, `aria-busy`, optimistic state, rollback,\n status names, and APIs. Use compact `Empty` states inside columns, preserve every column with\n `Skeleton` blocks while loading, and paginate large columns. See [Kanban composition and movement](kanban.md).\n- React applications can compose [TanStack-powered table views](data-table-views.md) through\n `DataTableView`, preserving application-owned cells and server queries. Numeric/date ranges and\n page-scoped selection are opt-in. Use `DataTableSavedViews` for explicit host-owned preference\n persistence, and `useLumenReviewWorkflow` for reviewed commands with stale-revision and unknown-outcome\n guards. Install `review-workflow`, `import-review` or `record-workspace` for executable React compositions.\n- Use `DataTable`, `Tree`, `TreeGrid`, `VirtualList`, `Pagination`, and `Command` for dense data\n collection workflows. Astro and Elements wire selectable/sortable `DataTable` behavior and\n `VirtualList` range events; React also provides built-in fixed-height list windowing. `Tree`\n and `TreeGrid` use roving keyboard focus, and `VirtualList` emits `ui:virtual-list-range` as the\n rendered range changes. Use `createDataViewState`,\n `serializeDataViewState`, `parseDataViewState`, `applyDataViewState`, and `getVirtualRange` for\n saved views, filtering, sorting, pagination, and virtualization math. Use\n `createDataViewStorageKey`, `saveDataViewState`, and `loadDataViewState` when saved views should\n survive navigation or reloads. Use `pinDataViewColumn`, `unpinDataViewColumn`,\n `toggleDataViewSelection`, `createDataViewSearchParams`, and `createDataViewServerRequest` when\n connecting dense views to router state or server-backed data.\n- Use `Resizable` for split-pane editors, dashboards, inspectors, and side-by-side review layouts.\n Astro `UIPrimitives` and registered Web Components generate separator handles and pane sizing\n directly; React apps can use the `Resizable` component or `useResizable` for pointer, keyboard,\n min/max, and double-click reset parity.\n- Use `ThemeBuilder`, `ColorPicker`, `Card`, `Button`, and `Input` to build token preview and export\n tools. Add `data-ui-theme-brand-hue`, `data-ui-theme-accent-hue`, `data-ui-theme-scheme`,\n `data-ui-theme-mode`, manual color inputs such as `data-ui-theme-primary-hex`, and\n `data-ui-theme-target` for scoped live previews. Add `data-ui-theme-export-format`,\n `data-ui-theme-export`, and `data-ui-theme-output` to copy CSS, Figma variable JSON, or design\n token JSON. Astro `UIPrimitives` and registered Web Components wire those controls directly;\n React apps can use `useThemeBuilder` for the same tokens, exports, and control props. Use\n `createThemePalette`, `createThemeBuilderTokens`, `exportThemeBuilderValue`, `exportThemeCss`,\n `parseThemeCss`, and `scoreThemeContrast`\n for theme import/export and accessibility checks. Use `suggestReadableInk`, `mergeThemeTokens`,\n and `tuneThemeContrast` when generated palettes need readable foreground repair.\n- Use `RichTextEditor` with `ButtonGroup`, `ToggleGroup`, and `Textarea` for editor compositions.\n Controls with `data-ui-editor-command` request formatting, block, history, link or list commands;\n use `data-ui-editor-value` for value-bearing commands. Before the browser executes a command,\n Lumen emits the bubbling, cancelable `ui:editor-command-request` event. External engines must\n call `preventDefault()` and set `event.detail.executed` to their synchronous result. A failed\n external command never falls back to the browser. The existing `ui:editor-command` event reports\n completion; use it for notifications, not executing the command again.\n Editable surfaces support formatting shortcuts and emit `ui:editor-change` with HTML and text.\n Set `data-ui-editor-native-state=\"false\"` when an external engine owns toolbar state.\n React apps can use `useRichTextEditor({ commandHandler: ({ command, value }) => boolean })`;\n providing a handler disables native toolbar state syncing by default. Use `nativeState` to\n explicitly control that policy. Engine commands are synchronous; engines with asynchronous\n work should manage completion themselves. Keep Tiptap, ProseMirror, Lexical and other engines\n in the application.\n- Use `Autocomplete`, `SearchField`, `NumberField`, `TimeField`, `DateRangePicker`, `ColorPicker`,\n and `TagGroup` when forms need more than plain text inputs. Add `data-ui-form` to forms that\n should reflect native Constraint Validation API state into `Field` error slots on blur and submit.\n For `InputOTP`, Astro `UIPrimitives` and registered Web Components render/sync a real native input\n with visual OTP segments; React apps can use the `InputOTP` component or `useInputOTP` for the\n same sanitization, paste handling, and caret navigation.\n For `DateRangePicker`, Astro `UIPrimitives` and registered Web Components sync the paired native\n date input `min`/`max` constraints directly; React apps can use `useDateRangePicker` for the same\n start/end syncing props.\n Use native attributes such as `required`, `pattern`, `min`, `max`, and `type`; add\n `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or\n `data-error-custom` when a control needs custom copy. Listen for `ui:validate` to call\n `control.setCustomValidity(message)` before the runtime reads validity, and listen for\n `ui:invalid` or `ui:valid` on the form for submission state. Astro `UIPrimitives` and registered\n Web Components bind `data-ui-form` directly; React apps can use `useFormValidation` with `Field`\n to get the same control props, CustomEvents, and error slot updates.\n- Use `Toast` for transient feedback. Static `<Toast>` markup works without\n JavaScript. With Astro `UIPrimitives` or registered Web Components, create toasts with\n `window.LumenToast.create(detail)`, `LumenToast.create(detail)` from `@santi020k/lumen-elements`,\n or `document.dispatchEvent(new CustomEvent('ui:toast', { detail }))`; update them with\n `.update(id, detail)` or `ui:toast-update`; and dismiss them with `.dismiss(id?)` or\n `ui:toast-dismiss`. `detail` supports `title`, `description`, `variant`, `duration`, `placement`,\n `max`, and `action: { label, value }`. Destructive toasts use `role=\"alert\"`; other variants use\n `role=\"status\"`. Runtime toasts pause their timeout on hover or focus, and Escape dismisses the\n focused toast.\n\n## Runtime CustomEvents\n\nWhen `UIPrimitives` is mounted, or when Web Components are registered for matching behavior, Lumen\nuses these CustomEvents:\n\n| Event | Target | Detail | Fires When |\n| -------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `ui:data-table-selection-change` | `DataTable` root `[data-ui-datatable]` | `{ values: string[] }` | Selectable row checkboxes, the select-all checkbox, or form reset changes selected row values. |\n| `ui:schedule-change` | `Schedule` root `[data-ui-schedule]` | `{ eventId?: string, slot?: string }` | A draggable schedule event is dropped on a `[data-ui-schedule-slot]`. |\n| `ui:virtual-list-range` | `VirtualList` root `[data-ui-virtual-list]` | `{ startIndex: number, endIndex: number }` | The virtual list calculates its visible range on init or scroll. |\n| `ui:kanban-move-request` | `KanbanBoard` root `[data-ui-kanban]` | `{ itemId: string, fromColumn: string, toColumn: string, beforeId?: string, input: 'keyboard' \\| 'pointer' }` | A handle requests a controlled move; the event is cancellable and never moves data or DOM. |\n| `ui:tag-remove` | `TagGroup` root `.ui-tag-group` or `[data-ui-tag-group]` | `{ value?: string }` | A `[data-ui-tag-remove]` control removes its closest tag or list item. |\n| `ui:editor-command-request` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | Cancel before execution and set executed for external engine ownership. |\n| `ui:editor-command` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | A toolbar control or keyboard shortcut runs an editor command. |\n| `ui:editor-change` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ html: string, text: string }` | Editable content changes or an editor command runs. |\n| `ui:theme-change` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ hue: number, accentHue: number, mode: 'generated' \\| 'manual', scheme: 'dark' \\| 'light', tokens: Record<string, string> }` | Hue, manual color, mode, or scheme controls update generated tokens. |\n| `ui:theme-export` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ css?: string, format: 'css' \\| 'figma' \\| 'tokens', tokens: Record<string, string> \\| null, value: string }` | A `[data-ui-theme-export]` control writes the selected export to the output and clipboard when available. |\n| `ui:language-change` | `LanguageToggle` control | `{ previousValue: string, value: string }` | Requests or applies the next configured locale. |\n| `ui:validate` | Form `[data-ui-form]` | `{ control, form, value }` | Before validation state is read, so custom code can call `control.setCustomValidity(message)`. |\n| `ui:invalid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds invalid controls. Failed submits focus the first invalid control. |\n| `ui:valid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds the checked control or form valid. |\n| `ui:toast` | `document` | `{ id?, title?, description?, variant?, duration?, placement?, action? }` | Creates a runtime toast in the matching notification viewport. |\n| `ui:toast-update` | `document` | `{ id, title?, description?, variant?, duration? }` | Updates a runtime toast. |\n| `ui:toast-dismiss` | `document` | `{ id? }` | Dismisses one runtime toast, or all runtime toasts when `id` is omitted. |\n| `ui:toast-action` | Toast `[data-ui-toast]` | `{ id, value? }` | A runtime toast action button is pressed, unless the action specifies a custom event name. |\n\n## Registry Helper\n\n`@santi020k/lumen` exports typed component, recipe, and documentation metadata from\n`@santi020k/lumen/registry`. The umbrella package and every web adapter expose the same small\n`lumen` binary, so projects that install Astro, React, or Elements can use discovery and diagnostics\nwithout another direct dependency. Individual components are addressable by name, including\nkebab-case aliases such as `data-table`:\n\n```bash\nlumen list\nlumen show Button\nlumen show data-table\nlumen add Button\nlumen add Button --target react\nlumen add Button --target elements\nlumen add data-table\nlumen show scheduler\nlumen add scheduler\nlumen add scheduler --target react\nlumen add scheduler --target elements\nlumen add scheduler --dry-run\nlumen add scheduler --fail-on-conflict\nlumen add scheduler --force\nlumen add scheduler --merge\nlumen list --registry ./registry/lumen.registry.json\nlumen add custom-recipe --registry ./registry/custom.registry.json\nlumen add private-recipe --registry https://example.com/lumen.registry.json --registry-token \"$TOKEN\"\nlumen install\nlumen doctor --json\nlumen doctor-native --json\nlumen init --framework astro --tailwind\n```\n\n`lumen doctor-native` emits the same adoption inventory and Android toolchain preflight in readable\ntext or JSON. Treat direct-primitive matches as review suggestions, not rewrite instructions: first\nconfirm whether a surface is product-owned and whether the public Lumen contract preserves its\nbehavior. A failed JDK, SDK, platform-package, or required-NDK preflight should be fixed before\nstarting Gradle so native compilation does not obscure the setup problem.\n\n`lumen add <component>` defaults to an Astro wrapper. Use `--target react` for a local React wrapper\nor `--target elements` for a custom-elements starter. Bundled recipes also accept `--target astro`,\n`--target react`, and `--target elements`; external registry inline files keep their authored paths\nand sources. Those paths must be unique, relative forward-slash paths: the installer rejects\nabsolute paths, parent traversal, and symbolic-link path segments before writing any file. Registry\nmanifests are limited to 5 MiB by default; programmatic `loadLumenRegistry` callers can lower the\nlimit with `maxBytes` and control remote request time with `timeoutMs`.\n\nBefore inventing a wrapper or custom class pattern, check the component source in\n`packages/astro/components`. Astro is the reference implementation for props, class names, data\nattributes, and accessible markup.\n\n## Styling Rules for Generated Code\n\n- Read the [content flow contract](content-flow.md). Stack and Grid own sibling gaps; surfaces own\n padding; Field owns label/control/feedback spacing. Do not add another child margin for the same\n relationship. Use `gap=\"related\"` for closely related controls, `gap=\"group\"` for separate groups\n and `gap=\"section\"` for major sections. Retrieve spacing tokens through MCP.\n- Use Card parts for header/body/actions and a nested Stack inside complex CardContent. Card\n handles missing or hidden parts and wraps footer actions. Choose `density=\"compact\"`,\n `\"comfortable\"` (default) or `\"spacious\"` instead of scattering padding overrides.\n- Keep text rhythm inside Prose or Typography. Verify long and translated text, enlarged text,\n validation states, optional sections and mobile wrapping before calling a composition finished.\n- Use the `page-header` and `section-header` recipes for dashboard identity, optional breadcrumbs,\n status/count badges and independent actions. Keep headings in document order, pass translated\n navigation/action labels and unique heading ids, and connect buttons to application-owned handlers.\n- Retrieve complete `content-flow-header`, `content-flow-settings`, `content-flow-list` and\n `content-flow-actions` examples with `lumen_get_recipe`; the CLI installs the same compositions.\n Keep one outer Container for fluid gutters. Card permits interactive overflow; use AspectRatio\n to clip media. Reading blocks trim their outer margins, so avoid compensating offsets.\n\n- Do not require consumers to configure Tailwind for Lumen components.\n- Prefer component props and composition before adding custom CSS.\n- Assign each visible outer edge to one container. For flush content inside a rounded frame,\n let the frame own the border and corner clipping; remove child rounding with supported props.\n For example, use `<Image radius=\"none\" ... />` below a framed header: `Image` defaults to\n `radius=\"lg\"`, which otherwise leaves rounded gaps at the shared edge. Keep standalone image\n rounding when appropriate.\n- Use a single divider between header and body rather than overlapping full borders. Inset\n surfaces with padding may have their own radius; account for the inset so their curves align.\n Clip artwork at its frame, not an entire interactive surface where focus rings or overlays\n could be cut off. Verify shared corners, borders, and keyboard focus at phone and desktop\n widths in both light and dark themes.\n- Use the shared token names for custom surfaces: `canvas`, `surface`, `surface-muted`,\n `surface-strong`, `line`, `ink`, `ink-soft`, `ink-muted`, `brand`, `brand-solid`, `brand-soft`,\n `accent`, `success`, `warning`, and `danger`.\n- Use `glass` as a boolean prop or attribute for glass surfaces when supported.\n- Preserve semantic labels, keyboard paths, focus states, and native form attributes.\n\n## Figma Handoff\n\nUse `exportThemeFigmaVariables` from `@santi020k/lumen-core/figma` when a design workflow needs\nFigma variable-friendly color values, and `exportThemeDesignTokens` when exporting standard design\ntoken JSON for token importers. See [docs/figma.md](figma.md) for the recommended Figma workflow.\n\n## When Editing This Repo\n\n- Shared contracts belong in `packages/core`.\n- Astro implementation and standalone CSS belong in `packages/astro`.\n- React-specific primitives belong in `packages/react`.\n- Web Component-specific primitives belong in `packages/elements`.\n- Update the relevant package README when public usage changes.\n- Add tests beside package code as `*.test.ts` when behavior, exported metadata, or component\n contracts change.\n\n### React inline range calendar\n\nFor a range editor with both endpoint calendars visible, use the React-only\n`DateRangeCalendar` export. Pass `value={{ start, end }}`, `onValueChange`, required\n`labels={{ start, end, presets }}`, `locale`, optional `min`/`max`, and optional\n`presets={[{ label, value: { start, end } }]}`. `formatDate` customizes the displayed\nendpoint summaries. Keep a draft in the consuming application and apply it only\nwhen confirmed. ISO endpoints must be valid and ascending. Crossing a boundary\nmoves the other endpoint; presets outside the bounds are disabled. The component\nshares `useCalendar` keyboard behavior and stacks its calendars on narrow screens.\n\nFor React input-attached range selection, import `DateRangeInput` from\n`@santi020k/lumen-react`. Pass controlled ISO `value`/`onValueChange`, an accessible\n`label`, and localized `labels` containing `start`, `end`, `presets`, `apply`, and\n`cancel`. It owns draft state and confirmation, anchors a non-modal popover to its\ntrigger, and supports keyboard dismissal and responsive scrolling. Use `validate`\nfor domain restrictions and `renderSummary` for draft details. Optional\n`name={{ start: 'from', end: 'to' }}` submits applied ISO values through hidden inputs.\nSee the React README for the complete usage and browser fallback contract.\n\n## Fixed-height virtual lists\n\n`VirtualList` uses the same DOM windowing controller in Astro, React and Elements. All rows remain\nmounted; offscreen rows are hidden, and inert spacers preserve the complete scroll height. This\nreduces displayed rows, not the cost of creating the initial DOM. Use pagination or an application\nrenderer when retaining the entire dataset in the DOM is too expensive.\n\nUse `itemSize` (Astro/React) or `item-size` (Elements) for a fixed row height in pixels, defaulting\nto 44. `overscan` defaults to four rows on each side. Every direct element child is a row; put\ncontrols inside it, and keep headers or empty-state actions outside the list. Rows must fit the\nconfigured height; variable-height measurement is not supported. Do not add external row margins\nor gaps. Initially hidden rows remain hidden and are excluded from the window.\n\nThe controller refreshes on scrolling, resizing, direct child changes and sizing attribute changes.\nA focused row and its neighboring rows remain available for native Tab navigation, which can extend\nthe displayed window until focus moves. Name the scroll container using `aria-label` or\n`aria-labelledby`; it is keyboard-focusable by default. `ui:virtual-list-range` reports inclusive\n`startIndex` and `endIndex` when the displayed range or item count changes. Empty lists use\n`{ startIndex: 0, endIndex: -1 }`. `data-ui-range-start` and `data-ui-range-end` mirror the current\nwindow. Elements disconnect and React unmount restore original row styles and hidden state.\n\n## NumberField boundary\n\n`NumberField` remains a native number-input wrapper. The v4 consumer audits do not establish a\nshared requirement for a locale-aware draft parser. Applications needing decimal-comma entry,\nlossless monetary drafts or currency rules should retain their explicit parsing and validation\npolicy; Lumen must not infer currency units or precision from locale.\n\n\n## Combobox keyboard behavior in v4\n\nCombobox keeps DOM focus in its input while Arrow Up/Down changes the active option through\n`aria-activedescendant`. Enter commits the active option only while the list is open; without an\nactive option, normal form submission remains available. Home, End, Left and Right retain native\ntext editing. Composition input and canceled keyboard events do not trigger selection or dismissal.\nEscape closes the innermost open control first, preserving an enclosing popup or dialog until the\nnext Escape. Tab dismisses the list when focus leaves the component.\n\nAstro and Elements observe added, removed, relabeled and disabled options, and delegate pointer\nselection to the current list. React derives options from its current props and reports committed\nselections through `onChange`, including controlled inputs. Disabled and read-only inputs stay closed.\nApplications should keep focus on the input and observe `aria-activedescendant` instead of calling\nfocus on option buttons. The DOM adapters match both option labels and values.\n\n## Appearance presets\n\nChoose `default`, `studio` or `glass` at the theme boundary. See [appearance presets](appearance-presets.md) for exact framework APIs. Studio follows the neutral PostLens look. Glass remains explicit per supporting surface; React Native and Compose retain solid fallbacks without adding blur dependencies. Customize semantic colors, typography and surface dimensions through public theme APIs.\n\n## Version-aware build, review, and migration\n\nThe portable skills are `lumen-ui` for implementation, `lumen-review` for a read-only audit, and\n`lumen-migrate` for an explicitly requested upgrade. Their canonical sources live under `skills/`;\n`pnpm run generate:plugin-package` produces the client snapshots. Both client manifests use plugin\n1.1.0 and pin the v4 catalog. This candidate is not proof of publication or directory approval.\n\nBefore applying MCP contracts, call `lumen_check_compatibility` with resolved versions from installed\nmetadata or native lock files. A manifest range is not an installed version. When versions differ,\ninspect installed public types and README or use a matching MCP package. Do not silently upgrade.\nSearch supports English and Spanish queries with framework and platform filters.\n\nFor a v4 upgrade, read `lumen_get_migration` and preview `lumen migrate v4 --dry-run --json` locally.\n`--apply` rewrites only known static SDK import paths in JavaScript and TypeScript. It preserves\ncomments and examples and reports UI/native review triggers. Dependency updates and application-owned\nworkarounds require review; the CLI does not infer that a workaround can be removed. JSX, Astro,\nnative source, dynamic imports, and dependency manifests are not automatically rewritten by the\nSDK import transform.\n\nSee [v4 AI verification](lumen-4-ai.md) for actual agent benchmark coverage and release evidence.\n\nThe integrated `lumen migrate v4` command also retains the release's web spacing migrations and\noptional coordinated dependency workflow. SDK edits compose into that command's source transform,\nso its apply ledger fingerprints the final source and repeat runs do not rewrite spacing twice.\nSDK import edits remain limited to `.ts`, `.js`, and `.mjs`; this restriction does not disable the\nseparate documented JSX/Astro spacing migration. The JSON report includes installed package\nversions and explicit SDK dependency-review findings.\n\n\n## Login examples and Auth\n\nUse the `auth-onboarding` recipe for visual email-code sign-in, code verification, and passkey\nrecovery states in Astro, React, or Elements. The preview controls are disabled and do not authenticate.\nLumen supplies the UI; the optional `@santi020k/auth-client` supplies protocol operations.\nSee the [login integration reference](../packages/templates/README.md#login-examples-and-auth)\nfor helper calls, safe result handling, and the application-owned server and session requirements.\n## Complex React forms\n\nUse the optional `@santi020k/lumen-react/forms` hooks with public form primitives for dependent\nvalidation, unsaved edits, stable repeatable rows, step navigation, and cancellation-safe checks.\nRead [composable form workflows](powerful-forms.md) before implementation. Choose one validation\nowner; keep schemas, financial policy, requests, authorization, and draft storage in the consumer.\n\nFor photo and media editors, install the `media-workspace` recipe and follow\n[Studio media workspaces](studio-media-workspaces.md). Reuse public media controls; keep\nimage processing, storage and export in the consumer application.\n",
|
|
45963
|
-
"readme": "<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"./docs/assets/readme/hero-dark.svg\">\n <img src=\"./docs/assets/readme/hero-light.svg\" alt=\"Lumen UI — A shared language. Every interface.\" width=\"1200\" height=\"340\">\n </picture>\n </a>\n</p>\n\n<h1 align=\"center\">Lumen UI</h1>\n\n<p align=\"center\">\n <strong>Accessible primitives. Native authoring. Shared foundations.</strong><br>\n An open-source UI system for Astro, React, Web Components, React Native, SwiftUI, and Jetpack Compose.\n</p>\n\n<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">Documentation</a> ·\n <a href=\"https://lumen.santi020k.com/docs/components\">Components</a> ·\n <a href=\"https://lumen.santi020k.com/#playgrounds\">Playgrounds</a> ·\n <a href=\"https://www.figma.com/community/file/1662337342676541513\">Figma library</a> ·\n <a href=\"https://lumen.santi020k.com/support\">Support</a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/ci.yml\"><img alt=\"CI status on main\" src=\"https://img.shields.io/github/actions/workflow/status/santi020k/lumen/ci.yml?branch=main&style=flat-square&label=CI\"></a>\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml\"><img alt=\"CodeQL status\" src=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml/badge.svg\"></a>\n <a href=\"https://www.npmjs.com/package/@santi020k/lumen-astro\"><img alt=\"Published Astro package version\" src=\"https://img.shields.io/npm/v/@santi020k/lumen-astro?style=flat-square&label=npm\"></a>\n <a href=\"./LICENSE\"><img alt=\"MIT license\" src=\"https://img.shields.io/badge/license-MIT-036b8c?style=flat-square\"></a>\n</p>\n\nLumen gives your product a consistent visual language while preserving each framework's native\nway of building. Compose accessible controls, forms, charts, and complete product layouts from\nshared tokens and explicit component contracts.\n\n**Explore:** [Quick start](#web-quick-start) · [Native apps](#native-playgrounds) · [Templates](#dashboards-and-templates) · [AI and Figma](#ai-and-design-workflows) · [Packages](#packages) · [Repository apps](#repository-apps) · [Contributing](#contributing)\n\n> **Lumen 4 candidate:** This branch documents the upcoming major version. The npm badge reports\n> the published version; local examples and store builds may differ. See the\n> [migration and readiness notes](#lumen-4-candidate) before upgrading.\n\n## See Lumen in action\n\n<p>\n <a href=\"https://lumen.santi020k.com/docs/web/playground\">\n <img src=\"./apps/docs/src/assets/platforms/web.webp\" alt=\"A Lumen web workspace with navigation, metric cards, an interactive chart, and a task form\" width=\"1184\" height=\"802\">\n </a>\n</p>\n\n*Real Lumen components with illustrative data. [Open the web playground →](https://lumen.santi020k.com/docs/web/playground)*\n\n| Built for | What you get |\n| --- | --- |\n| **Web products** | Astro, React, and custom elements with standalone CSS and optional Tailwind integration. |\n| **Native interfaces** | React Native, SwiftUI, Compose, WidgetKit, and Wear OS foundations with platform conventions. |\n| **Accessible interaction** | Semantic structure, keyboard paths, focus management, and reduced-motion support. |\n| **Design and AI workflows** | Figma resources, a portable agent skill, MCP discovery, and machine-readable contracts. |\n\n## Web quick start\n\nChoose one adapter. Astro requires Astro 5+, React requires React 19+, and the web packages\nrequire Node.js 22.12+ for their tooling. Install in your existing application:\n\n```bash\n# Astro\npnpm add @santi020k/lumen-astro\n\n# React\npnpm add @santi020k/lumen-react\n\n# Web Components\npnpm add @santi020k/lumen-elements\n```\n\n### Astro\n\nImport the stylesheet and mount `UIPrimitives` once in your root layout. The runtime enhances all\ninteractive Lumen markup on the page.\n\n```astro\n---\nimport '@santi020k/lumen-astro/styles.css'\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<html lang=\"en\">\n <body>\n <slot />\n <UIPrimitives />\n </body>\n</html>\n```\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\n### React\n\nLoad the stylesheet once from your app entry or global CSS:\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input, Label } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n </Card>\n )\n}\n```\n\n### Web Components\n\nImport the styles and register the elements once:\n\n```html\n<style>\n @import \"@santi020k/lumen-elements/styles.css\";\n</style>\n\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <lumen-label id=\"email-label\">Email</lumen-label>\n <lumen-input aria-labelledby=\"email-label\" id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button>Subscribe</lumen-button>\n</lumen-card>\n```\n\nSee the [Web documentation](https://lumen.santi020k.com/docs/web) for installation, theming,\ncomponent examples, and API details.\n\nBrowse the [package directory](https://lumen.santi020k.com/docs/packages) for framework adapters,\nshared foundations, and optional integrations, with links to focused examples and usage guides.\n\nFor failure states, use the [error-handling guide](./docs/error-handling.md) to choose between field\nfeedback, summaries, persistent alerts, transient toasts, and the `ErrorState` recovery surface.\nLumen owns presentation and accessibility; applications retain logging, retry, and exception policy.\n\nFor native applications, choose the [React Native](https://lumen.santi020k.com/docs/react-native),\n[Apple / SwiftUI](https://lumen.santi020k.com/docs/apple), or\n[Android / Compose](https://lumen.santi020k.com/docs/android) guide. The\n[shared foundations](https://lumen.santi020k.com/docs/foundations) explain the cross-platform token\nand component contract; repository contributors can also use the\n[cross-platform architecture](./docs/cross-platform.md) and\n[native component reference](./docs/native-components.md).\n\n> **Native qualification:** Check the [compatibility matrix](docs/native-compatibility.md) and\n> [device evidence](docs/native-device-validation.md) for your target. Local builds, published\n> artifacts, and physical-device accessibility are distinct verification steps.\n\n## Tailwind CSS\n\nKeep the layer prelude, Tailwind import, and Lumen stylesheet in the same shared CSS entry. Replace\n`lumen-astro` with the package for your framework.\n\n```css\n@import \"@santi020k/lumen-astro/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-astro/styles.css\";\n```\n\nThis order places Tailwind base styles before Lumen components and Tailwind utilities above Lumen\ncomponent defaults.\n\n## Native playgrounds\n\n<table>\n <tr>\n <th>SwiftUI</th>\n <th>Jetpack Compose</th>\n <th>React Native</th>\n </tr>\n <tr>\n <td><a href=\"https://lumen.santi020k.com/docs/apple/playground\"><img src=\"./apps/docs/src/assets/platforms/apple.webp\" alt=\"Native SwiftUI release checklist example\" width=\"220\"></a></td>\n <td><a href=\"https://lumen.santi020k.com/docs/android/playground\"><img src=\"./apps/docs/src/assets/platforms/android.webp\" alt=\"Native Compose release checklist example\" width=\"220\"></a></td>\n <td><a href=\"https://lumen.santi020k.com/docs/react-native/playground\"><img src=\"./apps/docs/src/assets/platforms/react-native.webp\" alt=\"Expo browser preview of the contributor profile example\" width=\"220\"></a></td>\n </tr>\n <tr>\n <td>Native app capture</td>\n <td>Native app capture</td>\n <td>Expo browser preview</td>\n </tr>\n</table>\n\nSee [capture provenance](apps/docs/src/assets/platforms/README.md) for sources and verification scope.\n\nTry Lumen before adding it to your project. The [playground section](https://lumen.santi020k.com/#playgrounds)\nbrings together the browser demos and native apps. Explore component states and themes, then use\nthe open-source apps as implementation references. Start with the\n[web playground](https://lumen.santi020k.com/docs/web/playground) for Astro, React, and Web Components,\nor choose a native gallery:\n\n- [`apps/playground-react-native`](./apps/playground-react-native) runs through Expo on the web,\n iOS, and Android. [Try the browser preview](https://lumen.santi020k.com/docs/react-native/playground#preview)\n or use the local setup guide for native evaluation. EAS profiles cover TestFlight, Android App\n Bundles, and APKs; this gallery has no separate public store listing.\n- [`apps/playground-apple`](./apps/playground-apple) is available on the\n [App Store](https://apps.apple.com/app/id6805250815) for iPhone, iPad, and Mac. It also builds as\n an iOS Xcode app or macOS Swift Package executable for local exploration.\n- [`apps/playground-android`](./apps/playground-android) builds a native Compose application and a\n directly installable debug APK. It is also available on\n [Google Play](https://play.google.com/store/apps/details?id=com.santi020k.lumen.playground.compose)\n for Android phones and tablets.\n\nSee the [playground workflow](./docs/playgrounds.md) for a platform chooser, reference-app source,\nand run, capture, and distribution commands. Store builds follow their own release schedule and\nmay differ from the current repository candidate.\n\n## Appearance presets\n\nChoose an appearance preset or customize the existing themes with the [appearance guide](docs/appearance-presets.md). The Studio preset uses PostLens as its visual reference.\n\n## Dashboards and templates\n\nExplore the [template gallery](https://lumen.santi020k.com/templates) for complete analytics,\nSaaS admin, commerce, project workspace, and authentication/onboarding experiences. Each family\nuses public Lumen primitives and semantic tokens, includes responsive and accessibility coverage,\nand ships through the CLI for all three framework targets:\n\n```bash\nlumen add analytics-dashboard\nlumen add commerce-dashboard --target react\nlumen add auth-onboarding --target elements\n```\n\n## AI and design workflows\n\nInstall the portable Lumen skill in Codex, Claude Code, Cursor, Windsurf, and other compatible\ncoding agents:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe skill teaches agents how to select, compose, theme, and verify Lumen primitives. Pair it with\n[`@santi020k/lumen-mcp`](./packages/mcp) when an agent needs to search the live catalog or retrieve\ncurrent source, props, tokens, and usage rules.\n\nFor ChatGPT and Codex, install the published\n[Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nfrom the Plugins Directory. Select **Install plugin**, then mention **@Lumen UI** in a request such\nas “Find the right Lumen components for an accessible React settings screen.” The plugin bundles\nthe skill with the hosted, read-only catalog; no separate Lumen account or local MCP setup is needed.\n\nLumen is designed to reduce repetitive UI code and unnecessary agent context. Actual token usage\ndepends on the model, task, and corrections; see the [measurement method](docs/ai-efficiency.md).\n\n<details>\n<summary>Guides, registries, and integration references</summary>\n\nAdditional machine-readable surfaces include:\n\n- [`llms.txt`](./llms.txt) for a concise project map.\n- [`docs/ai-usage.md`](./docs/ai-usage.md) for downstream generation examples.\n- [`docs/styling-contract.md`](./docs/styling-contract.md) for stable parts and component variables.\n- [`docs/import-and-icon-performance.md`](./docs/import-and-icon-performance.md) for repeatable import and icon evidence.\n- [`docs/graphics.md`](./docs/graphics.md) for portable decorative artwork across web and native platforms.\n- [`docs/consumer-regression-fixtures.md`](./docs/consumer-regression-fixtures.md) for production-shaped local coverage.\n- [`docs/project-adoption.md`](./docs/project-adoption.md) for the sibling-project audit and staged\n web and native migration strategy.\n- [`registry/lumen.registry.json`](./registry/lumen.registry.json) for recipes and installable file\n groups.\n- [`docs/figma.md`](./docs/figma.md) for Figma variables and Code Connect workflows.\n- [`apps/figma-plugin/README.md`](./apps/figma-plugin/README.md) for the **beta** Figma plugin,\n Astro starters, and AI handoff.\n- [`plugins/lumen-ui`](./plugins/lumen-ui) for the packaged Codex and ChatGPT plugin, with its\n [submission and review record](./docs/openai-plugin-submission.md).\n- [`.claude-plugin/marketplace.json`](./.claude-plugin/marketplace.json) for the Claude Code\n marketplace, with its [installation and submission guide](./docs/claude-code-plugin.md).\n- [`docs/cross-platform.md`](./docs/cross-platform.md) for the native architecture, support tiers,\n and component-parity policy.\n- [`docs/native-patterns.md`](./docs/native-patterns.md) for developed native compositions and the\n boundary between Lumen presentation and platform-owned application structure.\n\n</details>\n\n## Packages\n\n| Package | Purpose |\n| --- | --- |\n| [`@santi020k/lumen-astro`](./packages/astro) | Reference implementation, Astro components, and progressive-enhancement runtime |\n| [`@santi020k/lumen-react`](./packages/react) | React components and behavior hooks |\n| [`@santi020k/lumen-react-hook-form`](./packages/react-hook-form) | Optional React Hook Form adapters for composite controls |\n| [`@santi020k/lumen-elements`](./packages/elements) | Standards-based Web Components |\n| [`@santi020k/lumen-core`](./packages/core) | Shared tokens, metadata, class helpers, and behavior contracts |\n| [`@santi020k/lumen-tokens`](./packages/tokens) | Canonical platform-neutral design token source |\n| [`@santi020k/lumen-react-native`](./packages/react-native) | React Native foundations and native primitives |\n| [`LumenUI`](./packages/swift) | Swift Package for iOS, iPadOS, macOS, tvOS, visionOS, and watchOS foundations and native primitives |\n| [`LumenWidgetUI`](./packages/swift-widget) | Focused WidgetKit-safe semantic text, icon, badge, and compact-stat foundations |\n| [`lumen-compose`](./packages/compose) | Android library for Jetpack Compose foundations and native primitives |\n| [`lumen-compose-wear`](./packages/compose/wear) | Focused Wear OS primitives for at-a-glance round-screen actions, progress, status, metrics, and rows |\n| [`@santi020k/lumen-icons-brand`](./packages/icons-brand) | Optional namespaced brand icons for web `Icon` adapters |\n| [`@santi020k/lumen`](./packages/lumen) | Umbrella package, CLI, registry metadata, and public package map |\n| [`@santi020k/lumen-mcp`](./packages/mcp) | MCP server for component discovery and source retrieval |\n\nThe private [`@santi020k/lumen-templates`](packages/templates/README.md) workspace supplies the\ngallery and preview renderer. Consumer recipes are installed through the public Lumen CLI.\n\n## Repository apps\n\nEach app has its own setup, source map, and verification guidance:\n\n| App | Purpose |\n| --- | --- |\n| [Documentation](apps/docs/README.md) | Public documentation, live examples, search, and native previews |\n| [Template showcase](apps/templates/README.md) | Standalone gallery of five product-template families |\n| [Next.js smoke app](apps/next-smoke/README.md) | Server/client boundaries and rendered consumer regression fixtures |\n| [Apple playground](apps/playground-apple/README.md) | iPhone, iPad, Mac, watchOS, and WidgetKit reference hosts |\n| [Android playground](apps/playground-android/README.md) | Compose phone/tablet app and separate Wear OS host |\n| [React Native playground](apps/playground-react-native/README.md) | Expo reference app and embedded browser gallery |\n| [Figma plugin](apps/figma-plugin/README.md) | Development beta for Astro starters and structured agent handoff |\n| [Motion studio](apps/motion/README.md) | Reusable product films and local video exports |\n| [Store artwork](apps/store-assets/README.md) | Canonical SVG sources for the playground app icons |\n\n## Lumen 4 candidate\n\nThe v4 release branch brings consumer-driven date selection, chart readability, clipboard feedback,\nloading states, native layout improvements and a new ImageComparison primitive. Start with the\n[v3 → v4 migration guide](docs/migrating-v3-to-v4.md), [consumer audit](docs/lumen-4-consumer-audit.md),\nand [local readiness record](docs/lumen-4-readiness.md). The refreshed community gallery includes\npublished consumer screenshots; it does not imply those applications already run this candidate.\n\nFor existing applications, start with [v1 → v2](docs/migrating-v1-to-v2.md) when needed, then\n[v2 → v3](docs/migrating-v2-to-v3.md) before\n[v3 → v4](docs/migrating-v3-to-v4.md). For initial adoption, use\n[Migrating to Lumen](docs/migrating-to-lumen.md).\n\n## Contributing\n\nLumen is a pnpm workspace. Astro is the reference implementation; shared contracts belong in\n`packages/core`, while framework-specific behavior stays in its adapter package.\n\nUse Node.js 22.22.2+, 24.15.0+, or 26+ and pnpm 12.8.1, as declared in the root manifest.\n\n```bash\npnpm install --frozen-lockfile\npnpm run dev\npnpm run validate\n```\n\nRead [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. User-visible package\nchanges require a changeset.\n\n## Community\n\nCreate reusable product animations and social videos locally with the\n[Lumen motion studio](./apps/motion/README.md). Its first pilot previews one interface in four\nappearances and exports portrait, square, and landscape MP4s through HyperFrames.\n\n[Feedback & support](https://lumen.santi020k.com/support) connects ideas, questions, bug reports,\nand the [public roadmap](https://lumen.santi020k.com/support#roadmap). Discuss improvements on GitHub\nand follow accepted work through implementation and release.\n\nLumen is free and MIT licensed while the project focuses on adoption, public examples, and\nreal-world feedback.\n\n- Explore task-oriented [guides](https://lumen.santi020k.com/guides).\n- Share a public project in the [community showcase](https://lumen.santi020k.com/community).\n- Evaluate the system with the open [team adoption path](https://lumen.santi020k.com/teams).\n- Tell us what would improve your next build through the\n [adoption survey](https://github.com/santi020k/lumen/issues/new?template=adoption-feedback.yml).\n\nMaintainers can use the [exposure playbook](./docs/exposure-playbook.md) for publishing cadence,\ncampaign measurement, video scripts, and launch copy, and the\n[marketing package](./docs/marketing/README.md) for execution-ready copy, channel decisions, a\nfour-week calendar, asset briefs, an approval queue, and a community-response library.\n\n## Find your next step\n\n| Resource | Use it for |\n| --- | --- |\n| [Cross-platform architecture](docs/cross-platform.md) | Shared foundations and adapter responsibilities. |\n| [Native compatibility](docs/native-compatibility.md) | Supported toolchains and platform baselines. |\n| [Native release runbook](docs/native-release-runbook.md) | Artifact qualification and distribution gates. |\n\n## License\n\nLumen's original code and artwork are available under the [MIT License](./LICENSE). Generated\ninterface icons retain the Lucide ISC or Feather MIT terms, and generated brand icons retain Font\nAwesome Free's CC BY 4.0 terms. The native packages include the required attribution and license\nnotices; see [`icons/THIRD_PARTY_NOTICES.md`](./icons/THIRD_PARTY_NOTICES.md).\n\nCreated and maintained by [Santiago Molina](https://santi020k.com).\n"
|
|
46094
|
+
"aiUsage": "# AI Usage Guide\n\nUse this guide when an AI agent is building an app with Lumen or updating examples that teach\nanother agent how to use the library.\n\nLumen 4 is released. See the [v4.2 highlights](lumen-4.2.md) and use the\n[release manifest](../registry/release-manifest.json) for each package's version; companion packages\ndo not all share the umbrella version. The public plugin and hosted MCP describe their deployed\nsnapshot, which can differ from this checkout. Inspect snapshot metadata before using its contracts.\n\n## Install in ChatGPT or Codex\n\nThe [Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nis published in the shared Plugins Directory for ChatGPT and Codex.\n\n1. Open the listing and select **Install plugin**. Sign in to ChatGPT if prompted.\n2. Start a request with **@Lumen UI**, or choose one of the listing's starter prompts.\n3. Describe the interface and target framework, for example: “Find the right Lumen components for\n an accessible React settings screen.”\n\nThe plugin combines the Lumen workflow skill with the hosted public MCP catalog. It requires no\nseparate Lumen account, API key, or local server. Its tools only read public Lumen documentation;\nthey do not access private repositories or modify files and services. Your coding agent performs\nimplementation work using the retrieved contracts and its own permissions.\n\nSee the [publication record](./openai-plugin-submission.md) for version and verification details.\n\n## Install the Lumen Agent Skill\n\nLumen ships a portable Agent Skill for AI coding tools that support the open `SKILL.md` format:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe source lives in [`skills/lumen-ui`](../skills/lumen-ui). The skill provides the durable\nframework-selection, component-composition, theming, accessibility, and verification workflow.\nUse it by naming `$lumen-ui` in a prompt, or let a compatible agent trigger it from Lumen-specific\nrequests.\n\nThe skill and MCP server complement each other: the skill explains how to work, while\n`@santi020k/lumen-mcp` supplies structured catalog search, framework-specific contracts, recipes,\nsource, tokens, resources, and rules.\n\n## Web selection controls\n\nUse `Select` as the default for web product option lists, and `Combobox` when the user needs to\nsearch the choices. `NativeSelect` remains supported for a product requirement that explicitly\ncalls for the browser or platform picker. Do not remove native fallbacks from custom controls.\n\nMount the matching framework behavior before verifying the result: Astro uses `UIPrimitives`,\nReact uses the public Select behavior hook, and Elements uses registered custom elements. Check\nthat opening the control shows the enhanced list, that keyboard selection and Escape work, and\nthat dependent choices, forms, mobile layouts, and both appearances remain correct.\n\nFor personal projects that require custom lists, enforce the choice in ESLint as well as agent\nguidance. The prepared `lumenCustomSelects()` helper in `@santi020k/eslint-config-libraries` rejects\n`NativeSelect` imports (including aliases), namespace JSX usage, raw JSX selects, and JSX native\nselect custom elements without replacing the project's existing syntax restrictions. Enable it\nonly after that package version is available. Keep intentional native-control exceptions scoped\nto specific files with a documented product reason. Library source and fallback implementations\nshould not opt into a consumer-only policy.\n\n## Pick the Right Package\n\nUse the framework requested by the user. Every adapter shares the same Lumen foundation.\n\n| App Target | Install | Import Components From | Load Styles From |\n| -------------------------- | ---------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------- |\n| Astro | `@santi020k/lumen-astro` | `@santi020k/lumen-astro` | `@santi020k/lumen-astro/styles.css` |\n| React | `@santi020k/lumen-react` | `@santi020k/lumen-react` | `@santi020k/lumen-react/styles.css` |\n| React Hook Form composites | `@santi020k/lumen-react-hook-form` | `@santi020k/lumen-react-hook-form` | Uses React styles |\n| Web Components | `@santi020k/lumen-elements` | `@santi020k/lumen-elements/define` | `@santi020k/lumen-elements/styles.css` |\n| React Native / Expo | `@santi020k/lumen-react-native` | `@santi020k/lumen-react-native` | Not applicable |\n| Apple / SwiftUI | Swift Package `https://github.com/santi020k/lumen`, pinned to `4.2.0` | `LumenUI` | Not applicable |\n| Android / Compose | Maven Central `com.santi020k:lumen-compose:4.0.0` | `com.santi020k.lumen` | Not applicable |\n| Wear OS / Compose | Maven Central `com.santi020k:lumen-compose-wear:4.0.0` | `com.santi020k.lumen` | Not applicable |\n| Package metadata | `@santi020k/lumen-core` | `@santi020k/lumen-core` | Not applicable |\n| Optional web brand icons | `@santi020k/lumen-icons-brand` | Register once, then use the framework `Icon` | Uses framework styles |\n\nEach framework package includes the shared foundation. Install `@santi020k/lumen` separately only\nwhen you need its framework-neutral CLI or registry metadata.\n\nFor React Native, mount one `LumenProvider` near the app root. For SwiftUI, attach the `LumenUI`\nSwift Package product to the application target, use an exact or compatible release-version rule,\nand apply `.lumenTheme(...)` near the root. For Compose, install\n`com.santi020k:lumen-compose:4.0.0` from Maven Central and wrap content in `LumenTheme`. Native\nadapters do not load CSS or the Astro runtime.\n\nReact Native can use ordinary React hooks and platform-neutral application hooks. Import native\nLumen controllers such as `useDialog`, `useTabs`, `useSelect`, `useLanguageToggle`,\n`useThemeToggle`, and `useToast` from `@santi020k/lumen-react-native`; their state semantics track\nthe reusable React contracts while their returned props target native Lumen components. Never\nimport DOM-dependent hooks from `@santi020k/lumen-react` into a native bundle. Share business state\nin an application workspace package and keep the Lumen rendering adapter at each app boundary.\n\nFor watchOS, use the focused `LumenWatch*` contracts from the same Swift package. For Wear OS,\ninstall `com.santi020k:lumen-compose-wear:4.0.0` and wrap wearable content in `LumenWearTheme`\ninside the application-owned Wear Material theme. Keep complications, tiles, rotary input,\nAlways On behavior, haptics, synchronization, navigation, and health or safety logic app-owned.\n\nBrand marks are intentionally excluded from the default web Lucide bundle. In Astro, React, or Web\nComponents, install and register the optional pack once before rendering a namespaced icon:\n\n```ts\nimport { registerLumenBrandIcons } from \"@santi020k/lumen-icons-brand\";\n\nregisterLumenBrandIcons();\n```\n\nThen use any canonical Font Awesome Free brand name, such as `brand:github`, `brand:discord`,\n`brand:figma`, `brand:linkedin`, `brand:whatsapp`, or `brand:youtube`, through the existing Astro,\nReact, or Web Component `Icon` API. The package also provides `brand:x` as an alias for\n`brand:x-twitter`.\n\nBrand marks keep their recognizable filled geometry instead of being redrawn as Lucide outlines.\nThey still inherit the same Lumen size, alignment, `currentColor`, and accessible-label behavior as\nthe default icon catalog. React Native, SwiftUI, and Compose already include the corresponding\ncomplete brand catalog and accept `brand:github`, `.brandGithub`, and\n`LumenIconName.BrandGithub`, respectively, without a separate package.\n\n## Consumer composition in v4\n\n- For reporting filters, use React `DateRangeInput` with controlled `value`/`onValueChange`, localized\n `labels`, bounds and explicit presets. Keep draft changes inside the picker until Apply. See the\n [reporting example](https://lumen.santi020k.com/docs/web/reporting) and [migration guide](migrating-to-lumen.md).\n- Keep date-only values as validated `YYYY-MM-DD` strings. Do not round-trip a local date through\n UTC timestamps. Use the native adapter's existing date field and application-owned calendar/time-zone rules.\n- Compose a metric card from `Card`, `Stat` and a chart with `presentation=\"bare\"`; do not invent\n MetricCard or Status components. Products own status labels, aggregation, currency units and rounding.\n- Use unique chart X identities, abbreviated `xLabel` text and full `formatCategory` labels.\n Preserve absent observations explicitly; never turn missing data into a fabricated zero.\n- Compare two media treatments with `ImageComparison` and matching source dimensions. Supply a\n visible localized control label and useful image alternatives. See [image comparison](image-comparison.md).\n- Use `Prose` for ordinary Markdown, `Code`/`CodeTabs` for examples, and `CopyButton` for dynamic\n command output. Localize clipboard success/error feedback; do not swallow rejected writes.\n- Use public `asChild` composition when a semantic link needs button styling. Disable nested form\n controls separately. On Input, native numeric `size` and Lumen `visualSize` have different meanings.\n- Map the complete semantic theme in light and dark modes, including `brand-solid`/`on-brand` and\n `danger`/`on-danger`. Text-safe colors matter more than matching a decorative swatch.\n- NavigationMenu, Sidebar and Sheet compose a documentation shell. Keep ordinary links in normal\n Tab order and application routing in the app. Do not rebuild primitives with `ui-*` classes.\n\n### Theme bootstrap and generated command output\n\nWhen the application owns theme state, use `ThemeToggle controlled` and its own click handler.\nInitialize the document in the head before paint; unavailable storage must not prevent rendering:\n\n```html\n<script>\n let theme = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n try {\n const saved = localStorage.getItem('application-theme')\n if (saved === 'light' || saved === 'dark') theme = saved\n } catch {\n // Private browsing or storage policy can deny access.\n }\n document.documentElement.dataset.theme = theme\n document.documentElement.classList.toggle('dark', theme === 'dark')\n</script>\n```\n\nThe owner's click handler updates the document and the toggle's `aria-pressed` state, and persists\ninside a guarded storage write. Use a server-rendered theme when the application already has one.\nA controlled Lumen toggle does not read storage or change that state itself.\n\nFor commands generated from a form, point `CopyButton` at the current output element. It reads the\nlatest text when clicked, so the application only updates the output rather than managing clipboard\npromises or feedback timers:\n\n```astro\n---\nimport { CopyButton } from '@santi020k/lumen-astro'\n---\n\n<output id=\"generated-command\">pnpm run check</output>\n<CopyButton target=\"#generated-command\" label=\"Copy command\" copiedLabel=\"Command copied\" errorLabel=\"Copy failed; select the command and copy it manually\">\n Copy command\n</CopyButton>\n```\n\n## MCP-Assisted Workflow\n\nConnect `@santi020k/lumen-mcp` when the agent supports Model Context Protocol. The server returns\nvalidated structured content as well as readable text, so agents can select components without\nscraping documentation.\n\nThe MCP per-component catalog covers Astro, React, Web Components, React Native, SwiftUI, and\nCompose. Use `lumen_list_native_components` to discover platform availability and\n`lumen_get_native_component` for installation, setup, API, examples, accessibility guidance, and\noptional reference source. The skill still verifies installed packages when local versions differ.\n\n```bash\ncodex mcp add lumen -- npx -y @santi020k/lumen-mcp\n```\n\nFor Codex project configuration:\n\n```toml\n[mcp_servers.lumen]\nenabled = true\ncommand = \"npx\"\nargs = [\"-y\", \"@santi020k/lumen-mcp\"]\n```\n\nUse this sequence:\n\n1. Read `lumen://meta` and `lumen://diagnostics` to identify and verify the snapshot.\n2. Read `lumen://rules`.\n3. Search for the use case with `lumen_search`, passing the target `framework` when known.\n4. Inspect the selected framework with `lumen_get_component` and `detail: \"usage\"`.\n5. Follow its framework behavior setup and hook/controller contract.\n6. Read related multi-component guidance with `lumen_get_recipe`.\n7. Request `detail: \"source\"` only when the usage contract is insufficient.\n8. Read `lumen://tokens` before introducing custom styling.\n9. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades to identify contracts that need refreshing.\n\nExample framework-aware component request:\n\n```json\n{\n \"name\": \"lumen_get_component\",\n \"arguments\": {\n \"name\": \"DateRangePicker\",\n \"framework\": \"react\",\n \"detail\": \"usage\"\n }\n}\n```\n\nStable resources are available at `lumen://rules`, `lumen://tokens`, `lumen://components`,\n`lumen://meta`, `lumen://catalog-manifest`, `lumen://diagnostics`,\n`lumen://components/{name}`, and `lumen://recipes/{name}`. See\n[`packages/mcp/README.md`](../packages/mcp/README.md) for Claude Desktop, Cursor, generic stdio,\nlocal repository, and programmatic examples.\n\n## Minimal Setup\n\nImport the stylesheet once in the app's global CSS, root layout, or app entry.\n\nThis section applies to web targets. Native adapters use their provider or theme APIs instead of a\nstylesheet.\n\n```css\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nIf Tailwind is present, keep both imports in the same shared CSS entry.\n\n```css\n@import \"@santi020k/lumen-react/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nKeep the layer prelude first. It places Tailwind base styles below Lumen components and Tailwind\nutilities above them, so consumer spacing, display, radius, width, and responsive visibility\nclasses override Lumen defaults predictably.\n\nAstro apps should mount `UIPrimitives` once in the root layout when using interactive primitives\nsuch as dialogs, menus, popovers, tabs, carousels, command lists, toggles, validated forms, or\ntoasts. Do not place `UIPrimitives` in each component example or next to each primitive instance;\none root include enhances all matching `data-ui-*` markup on the page.\n\n```astro\n---\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<UIPrimitives />\n```\n\n## Astro\n\nUse direct named imports from `@santi020k/lumen-astro`.\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n</Card>\n```\n\n`Stat` defaults to a `div`. Choose `as=\"article\"` for a self-contained metric with supporting\ncontent, or `as=\"section\"` for a labeled region. Do not use a semantic root solely for styling.\nIts visual variants are `default`, `accent`, and `glass`; reserve `accent` for featured metrics and\nkeep glass selective.\n\nFor long reading surfaces, use `Anchor` for local section navigation and pass each item’s optional\nheading `depth`, `index`, and `description` when hierarchy matters. Use `activationOffset` for fixed\napplication headers. Use `ScrollProgress` for document position; keep `Progress` for\napplication-controlled task completion. In Astro, dispatch `ui:progress-change` with\n`{ value, max? }` from the `Progress` root when browser state changes after hydration. Non-finite or\nmissing fields keep the current state, and every adapter clamps to finite ARIA values. Use `Spinner`\nfor unknown-duration waits.\n\nSkeleton and Spinner are decorative (`aria-hidden`) unless named. For a group of placeholders,\nannounce loading once: wrap them in one `role=\"status\"` region with `aria-busy=\"true\"` and a\n`VisuallyHidden` label, and keep the Skeletons unnamed. Size placeholders with `shape`\n(`rectangle`, `text`, `circle`), `width`, and `height`, and use deterministic widths rather than\nrandom values so server and client markup match. Data fetching and when to show placeholders stay\nin the application. `Pill`\naccepts `neutral`, `brand`, and `outline`\nvariants, plus native link and count composition.\n\n## React\n\nReact components share the same visual classes and data attributes as Astro components. For\nbehavior-heavy primitives, use the React behavior hooks from `@santi020k/lumen-react` (`useDialog`,\n`usePopover`, `useDropdownMenu`, `useTabs`, `useSelect`, `useToast`, and `useTooltip`), which track\nthe Astro runtime's ARIA, keyboard, Escape, dismissal, and toast controller semantics.\n\nThe React package entry is a client boundary. In Next.js, it can be imported by a Server Component\nwithout evaluating Lumen's contexts and hooks in the React server runtime; the rendered Lumen\nsubtree becomes a Client Component boundary. Vite and other client-rendered React applications use\nthe same entry without additional setup.\nFor stateless Badge, Card composition, layout, form-label, progress, loading, typography, and\nvisually-hidden usage in a React Server Component, import from\n`@santi020k/lumen-react/server`. That entrypoint contains the implementations directly and avoids\ncreating a client boundary. Keep event handlers, hooks, and interactive primitives on the package\nroot inside a Client Component.\n`Button`, `Link`, `ButtonLink`, and `Input` accept React 19 DOM refs directly as props.\n`Button asChild` composes the button contract onto one child element. Across adapters, native\nnumeric `size` remains available on `Input` and `NativeSelect`; use `visualSize` in Astro and React,\nor `visual-size` in Elements, for presentation.\n`Stat as` and `variant` follow the same semantic-root and visual guidance as the Astro component.\n\n```tsx\nimport \"@santi020k/lumen-react/styles.css\";\nimport { Button, Card, Input, Label } from \"@santi020k/lumen-react\";\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n </Card>\n );\n}\n```\n\nFor React Hook Form, spread `register()` directly onto native-backed controls. Install\n`@santi020k/lumen-react-hook-form` only for controlled composites such as `Select`, `DatePicker`,\n`InputOTP`, and `ListBox`. Do not combine React Hook Form and `useFormValidation` unless the\napplication intentionally coordinates two validation sources. For schema validation, use\n`zodResolver` or `yupResolver` from `@hookform/resolvers`; keep Zod or Yup in the application and\nrender the resulting React Hook Form errors through Lumen's `FieldError` contract.\n\n## Web Components\n\nRegister custom elements once, then use `lumen-*` tags in HTML. The elements adapter includes\ninteractive behavior parity for DataTable, Dialog, Popover, DropdownMenu, ContextMenu, Tabs,\nSelect, ThemeBuilder, Toast, Tooltip, forms, RichTextEditor, Schedule, and VirtualList: selection,\nvalidation, rich text command, context menu, schedule drag/drop, theme, and range events, ARIA\nstate, roving/listbox keyboard paths, Escape/outside dismissal, focus return/trapping, native\nselect form participation, and the same toast controller events as the Astro runtime.\n\n```html\n<script type=\"module\">\n import { defineLumenElements } from \"@santi020k/lumen-elements/define\";\n\n defineLumenElements();\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input\n id=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n ></lumen-input>\n <lumen-button type=\"submit\">Subscribe</lumen-button>\n</lumen-card>\n```\n\nFor a surface that uses only Badge, Button, Card, and Combobox, import\n`@santi020k/lumen-elements/components/badge`, `/button`, `/card`, and `/combobox` and call their\ncorresponding `defineLumenBadge`, `defineLumenButton`, `defineLumenCard`, and `defineLumenCombobox`\nfunctions. Those implementation-level entrypoints avoid the complete catalog bundle; the Combobox\nentrypoint includes its filtering and keyboard behavior. Continue using `defineLumenElements` for\nany component without a granular entrypoint.\n\nFor a foundation-only custom-element surface, import `defineLumenFoundations` from\n`@santi020k/lumen-elements/components/foundations`. It registers Card compound parts, Container,\nDirection, Grid, Label, Separator, Skeleton, Spinner, Stack, Typography, and VisuallyHidden without\npulling the complete catalog implementation.\n\nPut input elements inside a native `<form data-ui-form>`. Scalar Lumen controls participate in\n`FormData`, validity, disabled state, focus, and reset through form-associated custom-element\nbehavior with a native-control fallback. Do not generate `<lumen-form>` as the submission\ncontainer.\n\n## Component Selection\n\nThe shared catalog includes:\n\n`Accordion`, `Alert`, `AlertDialog`, `Agenda`, `AspectRatio`, `Attachment`, `Autocomplete`,\n`Avatar`, `Badge`, `BarChart`, `Breadcrumb`, `Bubble`, `Button`, `ButtonGroup`, `Calendar`, `Card`,\n`Carousel`, `Chart`, `Checkbox`, `CheckboxGroup`, `Collapsible`, `Code`, `Combobox`, `Command`,\n`Container`, `ColorPicker`,\n`ContextMenu`, `DataTable`, `DatePicker`, `DateRangePicker`, `Dialog`, `Direction`, `Drawer`,\n`DropdownMenu`, `Empty`, `ErrorState`, `ErrorSummary`, `Field`, `FieldError`, `Form`, `Grid`, `HoverCard`, `Icon`,\n`Graphic`, `Backdrop`, `Illustration`, `Input`, `InputGroup`, `InputOTP`, `Item`, `KanbanBoard`, `KanbanColumn`, `Kbd`, `Label`, `LineChart`, `ListBox`, `Marker`, `Menubar`,\n`Message`, `MessageScroller`, `NativeSelect`, `ContextNavigation`, `NavigationMenu`, `NumberField`, `Pagination`,\n`PasswordField`, `PieChart`, `Popover`, `Progress`, `CopyButton`, `RadioGroup`, `Resizable`, `RichTextEditor`,\n`ScrollArea`, `Schedule`, `SearchField`, `Select`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`,\n`Slider`, `Sparkline`, `Spinner`, `Stack`, `Switch`, `Table`, `Tabs`, `TagGroup`, `Textarea`, `ThemeBuilder`,\n`TimeField`, `Toast`, `Toggle`, `ToggleGroup`, `Tooltip`, `Tree`, `TreeGrid`, `Typography`, and\n`VirtualList`, and `VisuallyHidden`.\n\n## Product Recipes\n\n- Follow [the error-handling guide](./error-handling.md) when a generated interface needs failure\n feedback. Use `FieldError` and `ErrorSummary` for validation, `Alert` for persistent inline\n problems, `Toast` for brief non-blocking failures, and `ErrorState` only when a page or region\n cannot show its primary content. Keep exception capture, logging, retry policy, and safe message\n mapping in application code. For React Native, SwiftUI, and Compose, follow the guide's native API\n mapping and announcement rules; do not invent a cross-platform error manager inside the component\n layer.\n\n- Start full product surfaces from the bundled `analytics-dashboard`, `saas-admin`,\n `commerce-dashboard`, `project-workspace`, or `auth-onboarding` recipe. Install with\n `lumen add <recipe>` for Astro, or add `--target react` / `--target elements`. Keep the generated\n shared template stylesheet, then replace sample data and routes while preserving public Lumen\n components, semantic tokens, accessible names, focus paths, and responsive navigation.\n- Use `Sparkline` for a compact trend beside a labelled metric, `BarChart` for categorical\n comparison, `LineChart` for ordered or time-series data, `PieChart` for a small\n part-to-whole breakdown. Pass already-aggregated `LumenChartSeries` data; keep statistics,\n resampling, model evaluation, and domain calculations in application code. Keep `showTable`\n enabled unless the same values are already available in a nearby semantic table. Prefer a bar\n chart when precise slice comparison matters or the pie would exceed roughly seven categories.\n Use `ScatterChart` for numeric relationships, `Heatmap` for a labeled matrix, `RangeChart` for\n low-to-high intervals, and `ComboChart` only when mixed marks share a meaningful domain.\n Use `WaterfallChart` for signed balance changes, `LollipopChart` for ranking, `DumbbellChart`\n for reference/current comparison, and `BulletChart` for actual/target comparison. Use `Histogram`\n for supplied bins, `BoxPlot` for supplied distribution summaries, `CalendarHeatmap` for daily\n activity, and `FunnelChart` for ordered cohort stages. Retrieve each specialized data contract;\n these components do not all accept `LumenChartSeries`. Keep statistical and conversion policy\n in the application, preserve missing days, and use density for unequal histogram bin widths. Use\n `Chart` as the custom SVG/canvas escape hatch. Preserve `null` gaps and the default accessible\n summary and fallback data. See `docs/data-visualization.md` for the full contract.\n Pass `labels` to replace every library-owned visible and assistive phrase. Finite zero is valid\n data, while an empty chart has no finite values; do not silently reinterpret an all-zero series\n as missing. Set `showTable={false}` (`showData={false}` on native adapters) only when an\n equivalent accessible table or scoreboard is adjacent.\n Keep stable identity in `datum.x` and put display text in `datum.xLabel`. Use `formatCategory`\n and `formatValue` for localized axes, SVG titles, and data tables. Choose\n `presentation=\"bare\"` inside an existing Card, `categoryWidth` for long horizontal labels,\n `markers=\"auto\"` or a numeric marker step for dense lines, and `emptyLabel` for actionable\n source-specific guidance.\n- Use `Icon name=\"search\"` or any Lucide icon name for standard interface pictograms. Kebab-case\n names and Lucide aliases work. Pair decorative icons with `decorative`, and pass `label` only\n when the icon carries meaning on its own. On the web, install and register\n `@santi020k/lumen-icons-brand` for supported brand marks; native adapters include those names.\n Then use `brand:github` or the adapter's generated equivalent. Render project-specific or\n unsupported brand SVGs through the default `Icon` slot. Astro reports unknown named icons during\n development instead of failing silently.\n- Use `Schedule` with `Agenda`, `Calendar`, and `DatePicker` for booking, CRM, content calendar, and\n event planning screens. Mark slots with `data-ui-schedule-slot` and events with\n `data-ui-schedule-event`; the runtime emits `ui:schedule-change` when a draggable event is moved.\n `Calendar` renders a hidden input plus selectable date grid; Astro `UIPrimitives` and registered\n Web Components wire month navigation and roving keyboard focus directly, while React apps can use\n the `Calendar` component or `useCalendar` for the same date selection contract.\n Astro `UIPrimitives` and registered Web Components bind schedule drag/drop directly; React apps\n can use `useSchedule` for `rootProps`, `getEventProps`, `getSlotProps`, and `ui:schedule-change`\n parity. Use `createScheduleSlots`, `moveScheduleEvent`, and `expandRecurringScheduleEvent` from\n `@santi020k/lumen-core` for reusable schedule state; use `getScheduleConflicts` and\n `canPlaceScheduleEvent` before committing drag/drop changes. Persist local schedule edits with\n `createScheduleStorageKey`, `saveScheduleEvents`, and `loadScheduleEvents` against any\n `localStorage`-compatible adapter. Use `resizeScheduleEvent` and `resizeScheduleEvents` for\n snap-to-grid resize handles.\n- Use `KanbanBoard` and `KanbanColumn` for status-based workspaces while continuing to use `Card` for\n items. Mark items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a dedicated button;\n do not make the whole card draggable. Astro and Elements emit the cancellable, controlled\n `ui:kanban-move-request` event, and React provides `useKanban`. The event reports `itemId`,\n `fromColumn`, `toColumn`, optional `beforeId`, and keyboard or pointer input, but never moves DOM or\n persists application data. The host owns validation, `aria-busy`, optimistic state, rollback,\n status names, and APIs. Use compact `Empty` states inside columns, preserve every column with\n `Skeleton` blocks while loading, and paginate large columns. See [Kanban composition and movement](kanban.md).\n- React applications can compose [TanStack-powered table views](data-table-views.md) through\n `DataTableView`, preserving application-owned cells and server queries. Numeric/date ranges and\n page-scoped selection are opt-in. Use `DataTableSavedViews` for explicit host-owned preference\n persistence, and `useLumenReviewWorkflow` for reviewed commands with stale-revision and unknown-outcome\n guards. Install `review-workflow`, `import-review` or `record-workspace` for executable React compositions.\n- Use `DataTable`, `Tree`, `TreeGrid`, `VirtualList`, `Pagination`, and `Command` for dense data\n collection workflows. Astro and Elements wire selectable/sortable `DataTable` behavior and\n `VirtualList` range events; React also provides built-in fixed-height list windowing. `Tree`\n and `TreeGrid` use roving keyboard focus, and `VirtualList` emits `ui:virtual-list-range` as the\n rendered range changes. Use `createDataViewState`,\n `serializeDataViewState`, `parseDataViewState`, `applyDataViewState`, and `getVirtualRange` for\n saved views, filtering, sorting, pagination, and virtualization math. Use\n `createDataViewStorageKey`, `saveDataViewState`, and `loadDataViewState` when saved views should\n survive navigation or reloads. Use `pinDataViewColumn`, `unpinDataViewColumn`,\n `toggleDataViewSelection`, `createDataViewSearchParams`, and `createDataViewServerRequest` when\n connecting dense views to router state or server-backed data.\n- Use `Resizable` for split-pane editors, dashboards, inspectors, and side-by-side review layouts.\n Astro `UIPrimitives` and registered Web Components generate separator handles and pane sizing\n directly; React apps can use the `Resizable` component or `useResizable` for pointer, keyboard,\n min/max, and double-click reset parity.\n- Use `ThemeBuilder`, `ColorPicker`, `Card`, `Button`, and `Input` to build token preview and export\n tools. Add `data-ui-theme-brand-hue`, `data-ui-theme-accent-hue`, `data-ui-theme-scheme`,\n `data-ui-theme-mode`, manual color inputs such as `data-ui-theme-primary-hex`, and\n `data-ui-theme-target` for scoped live previews. Add `data-ui-theme-export-format`,\n `data-ui-theme-export`, and `data-ui-theme-output` to copy CSS, Figma variable JSON, or design\n token JSON. Astro `UIPrimitives` and registered Web Components wire those controls directly;\n React apps can use `useThemeBuilder` for the same tokens, exports, and control props. Use\n `createThemePalette`, `createThemeBuilderTokens`, `exportThemeBuilderValue`, `exportThemeCss`,\n `parseThemeCss`, and `scoreThemeContrast`\n for theme import/export and accessibility checks. Use `suggestReadableInk`, `mergeThemeTokens`,\n and `tuneThemeContrast` when generated palettes need readable foreground repair.\n- Use `RichTextEditor` with `ButtonGroup`, `ToggleGroup`, and `Textarea` for editor compositions.\n Controls with `data-ui-editor-command` request formatting, block, history, link or list commands;\n use `data-ui-editor-value` for value-bearing commands. Before the browser executes a command,\n Lumen emits the bubbling, cancelable `ui:editor-command-request` event. External engines must\n call `preventDefault()` and set `event.detail.executed` to their synchronous result. A failed\n external command never falls back to the browser. The existing `ui:editor-command` event reports\n completion; use it for notifications, not executing the command again.\n Editable surfaces support formatting shortcuts and emit `ui:editor-change` with HTML and text.\n Set `data-ui-editor-native-state=\"false\"` when an external engine owns toolbar state.\n React apps can use `useRichTextEditor({ commandHandler: ({ command, value }) => boolean })`;\n providing a handler disables native toolbar state syncing by default. Use `nativeState` to\n explicitly control that policy. Engine commands are synchronous; engines with asynchronous\n work should manage completion themselves. Keep Tiptap, ProseMirror, Lexical and other engines\n in the application.\n- Use `Autocomplete`, `SearchField`, `NumberField`, `TimeField`, `DateRangePicker`, `ColorPicker`,\n and `TagGroup` when forms need more than plain text inputs. Add `data-ui-form` to forms that\n should reflect native Constraint Validation API state into `Field` error slots on blur and submit.\n For `InputOTP`, Astro `UIPrimitives` and registered Web Components render/sync a real native input\n with visual OTP segments; React apps can use the `InputOTP` component or `useInputOTP` for the\n same sanitization, paste handling, and caret navigation.\n For `DateRangePicker`, Astro `UIPrimitives` and registered Web Components sync the paired native\n date input `min`/`max` constraints directly; React apps can use `useDateRangePicker` for the same\n start/end syncing props.\n Use native attributes such as `required`, `pattern`, `min`, `max`, and `type`; add\n `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or\n `data-error-custom` when a control needs custom copy. Listen for `ui:validate` to call\n `control.setCustomValidity(message)` before the runtime reads validity, and listen for\n `ui:invalid` or `ui:valid` on the form for submission state. Astro `UIPrimitives` and registered\n Web Components bind `data-ui-form` directly; React apps can use `useFormValidation` with `Field`\n to get the same control props, CustomEvents, and error slot updates.\n- Use `Toast` for transient feedback. Static `<Toast>` markup works without\n JavaScript. With Astro `UIPrimitives` or registered Web Components, create toasts with\n `window.LumenToast.create(detail)`, `LumenToast.create(detail)` from `@santi020k/lumen-elements`,\n or `document.dispatchEvent(new CustomEvent('ui:toast', { detail }))`; update them with\n `.update(id, detail)` or `ui:toast-update`; and dismiss them with `.dismiss(id?)` or\n `ui:toast-dismiss`. `detail` supports `title`, `description`, `variant`, `duration`, `placement`,\n `max`, and `action: { label, value }`. Destructive toasts use `role=\"alert\"`; other variants use\n `role=\"status\"`. Runtime toasts pause their timeout on hover or focus, and Escape dismisses the\n focused toast.\n\n## Runtime CustomEvents\n\nWhen `UIPrimitives` is mounted, or when Web Components are registered for matching behavior, Lumen\nuses these CustomEvents:\n\n| Event | Target | Detail | Fires When |\n| -------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `ui:data-table-selection-change` | `DataTable` root `[data-ui-datatable]` | `{ values: string[] }` | Selectable row checkboxes, the select-all checkbox, or form reset changes selected row values. |\n| `ui:schedule-change` | `Schedule` root `[data-ui-schedule]` | `{ eventId?: string, slot?: string }` | A draggable schedule event is dropped on a `[data-ui-schedule-slot]`. |\n| `ui:virtual-list-range` | `VirtualList` root `[data-ui-virtual-list]` | `{ startIndex: number, endIndex: number }` | The virtual list calculates its visible range on init or scroll. |\n| `ui:kanban-move-request` | `KanbanBoard` root `[data-ui-kanban]` | `{ itemId: string, fromColumn: string, toColumn: string, beforeId?: string, input: 'keyboard' \\| 'pointer' }` | A handle requests a controlled move; the event is cancellable and never moves data or DOM. |\n| `ui:tag-remove` | `TagGroup` root `.ui-tag-group` or `[data-ui-tag-group]` | `{ value?: string }` | A `[data-ui-tag-remove]` control removes its closest tag or list item. |\n| `ui:editor-command-request` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | Cancel before execution and set executed for external engine ownership. |\n| `ui:editor-command` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | A toolbar control or keyboard shortcut runs an editor command. |\n| `ui:editor-change` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ html: string, text: string }` | Editable content changes or an editor command runs. |\n| `ui:theme-change` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ hue: number, accentHue: number, mode: 'generated' \\| 'manual', scheme: 'dark' \\| 'light', tokens: Record<string, string> }` | Hue, manual color, mode, or scheme controls update generated tokens. |\n| `ui:theme-export` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ css?: string, format: 'css' \\| 'figma' \\| 'tokens', tokens: Record<string, string> \\| null, value: string }` | A `[data-ui-theme-export]` control writes the selected export to the output and clipboard when available. |\n| `ui:language-change` | `LanguageToggle` control | `{ previousValue: string, value: string }` | Requests or applies the next configured locale. |\n| `ui:validate` | Form `[data-ui-form]` | `{ control, form, value }` | Before validation state is read, so custom code can call `control.setCustomValidity(message)`. |\n| `ui:invalid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds invalid controls. Failed submits focus the first invalid control. |\n| `ui:valid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds the checked control or form valid. |\n| `ui:toast` | `document` | `{ id?, title?, description?, variant?, duration?, placement?, action? }` | Creates a runtime toast in the matching notification viewport. |\n| `ui:toast-update` | `document` | `{ id, title?, description?, variant?, duration? }` | Updates a runtime toast. |\n| `ui:toast-dismiss` | `document` | `{ id? }` | Dismisses one runtime toast, or all runtime toasts when `id` is omitted. |\n| `ui:toast-action` | Toast `[data-ui-toast]` | `{ id, value? }` | A runtime toast action button is pressed, unless the action specifies a custom event name. |\n\n## Registry Helper\n\n`@santi020k/lumen` exports typed component, recipe, and documentation metadata from\n`@santi020k/lumen/registry`. The umbrella package and every web adapter expose the same small\n`lumen` binary, so projects that install Astro, React, or Elements can use discovery and diagnostics\nwithout another direct dependency. Individual components are addressable by name, including\nkebab-case aliases such as `data-table`:\n\n```bash\nlumen list\nlumen show Button\nlumen show data-table\nlumen add Button\nlumen add Button --target react\nlumen add Button --target elements\nlumen add data-table\nlumen show scheduler\nlumen add scheduler\nlumen add scheduler --target react\nlumen add scheduler --target elements\nlumen add scheduler --dry-run\nlumen add scheduler --fail-on-conflict\nlumen add scheduler --force\nlumen add scheduler --merge\nlumen list --registry ./registry/lumen.registry.json\nlumen add custom-recipe --registry ./registry/custom.registry.json\nlumen add private-recipe --registry https://example.com/lumen.registry.json --registry-token \"$TOKEN\"\nlumen install\nlumen doctor --json\nlumen doctor-native --json\nlumen init --framework astro --tailwind\n```\n\n`lumen doctor-native` emits the same adoption inventory and Android toolchain preflight in readable\ntext or JSON. Treat direct-primitive matches as review suggestions, not rewrite instructions: first\nconfirm whether a surface is product-owned and whether the public Lumen contract preserves its\nbehavior. A failed JDK, SDK, platform-package, or required-NDK preflight should be fixed before\nstarting Gradle so native compilation does not obscure the setup problem.\n\n`lumen add <component>` defaults to an Astro wrapper. Use `--target react` for a local React wrapper\nor `--target elements` for a custom-elements starter. Bundled recipes also accept `--target astro`,\n`--target react`, and `--target elements`; external registry inline files keep their authored paths\nand sources. Those paths must be unique, relative forward-slash paths: the installer rejects\nabsolute paths, parent traversal, and symbolic-link path segments before writing any file. Registry\nmanifests are limited to 5 MiB by default; programmatic `loadLumenRegistry` callers can lower the\nlimit with `maxBytes` and control remote request time with `timeoutMs`.\n\nBefore inventing a wrapper or custom class pattern, check the component source in\n`packages/astro/components`. Astro is the reference implementation for props, class names, data\nattributes, and accessible markup.\n\n## Styling Rules for Generated Code\n\n- Read the [content flow contract](content-flow.md). Stack and Grid own sibling gaps; surfaces own\n padding; Field owns label/control/feedback spacing. Do not add another child margin for the same\n relationship. Use `gap=\"related\"` for closely related controls, `gap=\"group\"` for separate groups\n and `gap=\"section\"` for major sections. Retrieve spacing tokens through MCP.\n- Use Card parts for header/body/actions and a nested Stack inside complex CardContent. Card\n handles missing or hidden parts and wraps footer actions. Choose `density=\"compact\"`,\n `\"comfortable\"` (default) or `\"spacious\"` instead of scattering padding overrides.\n- Keep text rhythm inside Prose or Typography. Verify long and translated text, enlarged text,\n validation states, optional sections and mobile wrapping before calling a composition finished.\n- Use the `page-header` and `section-header` recipes for dashboard identity, optional breadcrumbs,\n status/count badges and independent actions. Keep headings in document order, pass translated\n navigation/action labels and unique heading ids, and connect buttons to application-owned handlers.\n- Retrieve complete `content-flow-header`, `content-flow-settings`, `content-flow-list` and\n `content-flow-actions` examples with `lumen_get_recipe`; the CLI installs the same compositions.\n Keep one outer Container for fluid gutters. Card permits interactive overflow; use AspectRatio\n to clip media. Reading blocks trim their outer margins, so avoid compensating offsets.\n\n- Do not require consumers to configure Tailwind for Lumen components.\n- Prefer component props and composition before adding custom CSS.\n- Assign each visible outer edge to one container. For flush content inside a rounded frame,\n let the frame own the border and corner clipping; remove child rounding with supported props.\n For example, use `<Image radius=\"none\" ... />` below a framed header: `Image` defaults to\n `radius=\"lg\"`, which otherwise leaves rounded gaps at the shared edge. Keep standalone image\n rounding when appropriate.\n- Use a single divider between header and body rather than overlapping full borders. Inset\n surfaces with padding may have their own radius; account for the inset so their curves align.\n Clip artwork at its frame, not an entire interactive surface where focus rings or overlays\n could be cut off. Verify shared corners, borders, and keyboard focus at phone and desktop\n widths in both light and dark themes.\n- Use the shared token names for custom surfaces: `canvas`, `surface`, `surface-muted`,\n `surface-strong`, `line`, `ink`, `ink-soft`, `ink-muted`, `brand`, `brand-solid`, `brand-soft`,\n `accent`, `success`, `warning`, and `danger`.\n- Use `glass` as a boolean prop or attribute for glass surfaces when supported.\n- Preserve semantic labels, keyboard paths, focus states, and native form attributes.\n\n## Figma Handoff\n\nUse `exportThemeFigmaVariables` from `@santi020k/lumen-core/figma` when a design workflow needs\nFigma variable-friendly color values, and `exportThemeDesignTokens` when exporting standard design\ntoken JSON for token importers. See [docs/figma.md](figma.md) for the recommended Figma workflow.\n\n## When Editing This Repo\n\n- Shared contracts belong in `packages/core`.\n- Astro implementation and standalone CSS belong in `packages/astro`.\n- React-specific primitives belong in `packages/react`.\n- Web Component-specific primitives belong in `packages/elements`.\n- Update the relevant package README when public usage changes.\n- Add tests beside package code as `*.test.ts` when behavior, exported metadata, or component\n contracts change.\n\n### React inline range calendar\n\nFor a range editor with both endpoint calendars visible, use the React-only\n`DateRangeCalendar` export. Pass `value={{ start, end }}`, `onValueChange`, required\n`labels={{ start, end, presets }}`, `locale`, optional `min`/`max`, and optional\n`presets={[{ label, value: { start, end } }]}`. `formatDate` customizes the displayed\nendpoint summaries. Keep a draft in the consuming application and apply it only\nwhen confirmed. ISO endpoints must be valid and ascending. Crossing a boundary\nmoves the other endpoint; presets outside the bounds are disabled. The component\nshares `useCalendar` keyboard behavior and stacks its calendars on narrow screens.\n\nFor React input-attached range selection, import `DateRangeInput` from\n`@santi020k/lumen-react`. Pass controlled ISO `value`/`onValueChange`, an accessible\n`label`, and localized `labels` containing `start`, `end`, `presets`, `apply`, and\n`cancel`. It owns draft state and confirmation, anchors a non-modal popover to its\ntrigger, and supports keyboard dismissal and responsive scrolling. Use `validate`\nfor domain restrictions and `renderSummary` for draft details. Optional\n`name={{ start: 'from', end: 'to' }}` submits applied ISO values through hidden inputs.\nSee the React README for the complete usage and browser fallback contract.\n\n## Fixed-height virtual lists\n\n`VirtualList` uses the same DOM windowing controller in Astro, React and Elements. All rows remain\nmounted; offscreen rows are hidden, and inert spacers preserve the complete scroll height. This\nreduces displayed rows, not the cost of creating the initial DOM. Use pagination or an application\nrenderer when retaining the entire dataset in the DOM is too expensive.\n\nUse `itemSize` (Astro/React) or `item-size` (Elements) for a fixed row height in pixels, defaulting\nto 44. `overscan` defaults to four rows on each side. Every direct element child is a row; put\ncontrols inside it, and keep headers or empty-state actions outside the list. Rows must fit the\nconfigured height; variable-height measurement is not supported. Do not add external row margins\nor gaps. Initially hidden rows remain hidden and are excluded from the window.\n\nThe controller refreshes on scrolling, resizing, direct child changes and sizing attribute changes.\nA focused row and its neighboring rows remain available for native Tab navigation, which can extend\nthe displayed window until focus moves. Name the scroll container using `aria-label` or\n`aria-labelledby`; it is keyboard-focusable by default. `ui:virtual-list-range` reports inclusive\n`startIndex` and `endIndex` when the displayed range or item count changes. Empty lists use\n`{ startIndex: 0, endIndex: -1 }`. `data-ui-range-start` and `data-ui-range-end` mirror the current\nwindow. Elements disconnect and React unmount restore original row styles and hidden state.\n\n## NumberField boundary\n\n`NumberField` remains a native number-input wrapper. The v4 consumer audits do not establish a\nshared requirement for a locale-aware draft parser. Applications needing decimal-comma entry,\nlossless monetary drafts or currency rules should retain their explicit parsing and validation\npolicy; Lumen must not infer currency units or precision from locale.\n\n\n## Combobox keyboard behavior in v4\n\nCombobox keeps DOM focus in its input while Arrow Up/Down changes the active option through\n`aria-activedescendant`. Enter commits the active option only while the list is open; without an\nactive option, normal form submission remains available. Home, End, Left and Right retain native\ntext editing. Composition input and canceled keyboard events do not trigger selection or dismissal.\nEscape closes the innermost open control first, preserving an enclosing popup or dialog until the\nnext Escape. Tab dismisses the list when focus leaves the component.\n\nAstro and Elements observe added, removed, relabeled and disabled options, and delegate pointer\nselection to the current list. React derives options from its current props and reports committed\nselections through `onChange`, including controlled inputs. Disabled and read-only inputs stay closed.\nApplications should keep focus on the input and observe `aria-activedescendant` instead of calling\nfocus on option buttons. The DOM adapters match both option labels and values.\n\n## Appearance presets\n\nChoose `default`, `studio` or `glass` at the theme boundary. See [appearance presets](appearance-presets.md) for exact framework APIs. Studio follows the neutral PostLens look. Glass remains explicit per supporting surface; React Native and Compose retain solid fallbacks without adding blur dependencies. Customize semantic colors, typography and surface dimensions through public theme APIs.\n\n## Version-aware build, review, and migration\n\nThe portable skills are `lumen-ui` for implementation, `lumen-review` for a read-only audit, and\n`lumen-migrate` for an explicitly requested upgrade. Their canonical sources live under `skills/`;\n`pnpm run generate:plugin-package` produces the client snapshots. Both client manifests use plugin\n1.1.0 and pin the v4 catalog. This candidate is not proof of publication or directory approval.\n\nBefore applying MCP contracts, call `lumen_check_compatibility` with resolved versions from installed\nmetadata or native lock files. A manifest range is not an installed version. When versions differ,\ninspect installed public types and README or use a matching MCP package. Do not silently upgrade.\nSearch supports English and Spanish queries with framework and platform filters.\n\nFor a v4 upgrade, read `lumen_get_migration` and preview `lumen migrate v4 --dry-run --json` locally.\n`--apply` rewrites only known static SDK import paths in JavaScript and TypeScript. It preserves\ncomments and examples and reports UI/native review triggers. Dependency updates and application-owned\nworkarounds require review; the CLI does not infer that a workaround can be removed. JSX, Astro,\nnative source, dynamic imports, and dependency manifests are not automatically rewritten by the\nSDK import transform.\n\nSee [v4 AI verification](lumen-4-ai.md) for actual agent benchmark coverage and release evidence.\n\nThe integrated `lumen migrate v4` command also retains the release's web spacing migrations and\noptional coordinated dependency workflow. SDK edits compose into that command's source transform,\nso its apply ledger fingerprints the final source and repeat runs do not rewrite spacing twice.\nSDK import edits remain limited to `.ts`, `.js`, and `.mjs`; this restriction does not disable the\nseparate documented JSX/Astro spacing migration. The JSON report includes installed package\nversions and explicit SDK dependency-review findings.\n\n\n## Login examples and Auth\n\nUse the `auth-onboarding` recipe for visual email-code sign-in, code verification, and passkey\nrecovery states in Astro, React, or Elements. The preview controls are disabled and do not authenticate.\nLumen supplies the UI; the optional `@santi020k/auth-client` supplies protocol operations.\nSee the [login integration reference](../packages/templates/README.md#login-examples-and-auth)\nfor helper calls, safe result handling, and the application-owned server and session requirements.\n## Complex React forms\n\nUse the optional `@santi020k/lumen-react/forms` hooks with public form primitives for dependent\nvalidation, unsaved edits, stable repeatable rows, step navigation, and cancellation-safe checks.\nRead [composable form workflows](powerful-forms.md) before implementation. Choose one validation\nowner; keep schemas, financial policy, requests, authorization, and draft storage in the consumer.\n\nFor photo and media editors, install the `media-workspace` recipe and follow\n[Studio media workspaces](studio-media-workspaces.md). Reuse public media controls; keep\nimage processing, storage and export in the consumer application.\n",
|
|
46095
|
+
"readme": "<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"./docs/assets/readme/hero-dark.svg\">\n <img src=\"./docs/assets/readme/hero-light.svg\" alt=\"Lumen UI — A shared language. Every interface.\" width=\"1200\" height=\"340\">\n </picture>\n </a>\n</p>\n\n<h1 align=\"center\">Lumen UI</h1>\n\n<p align=\"center\">\n <strong>Accessible primitives. Native authoring. Shared foundations.</strong><br>\n An open-source UI system for Astro, React, Web Components, React Native, SwiftUI, and Jetpack Compose.\n</p>\n\n<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">Documentation</a> ·\n <a href=\"https://lumen.santi020k.com/docs/components\">Components</a> ·\n <a href=\"https://lumen.santi020k.com/#playgrounds\">Playgrounds</a> ·\n <a href=\"https://www.figma.com/community/file/1662337342676541513\">Figma library</a> ·\n <a href=\"https://lumen.santi020k.com/support\">Support</a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/ci.yml\"><img alt=\"CI status on main\" src=\"https://img.shields.io/github/actions/workflow/status/santi020k/lumen/ci.yml?branch=main&style=flat-square&label=CI\"></a>\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml\"><img alt=\"CodeQL status\" src=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml/badge.svg\"></a>\n <a href=\"https://www.npmjs.com/package/@santi020k/lumen-astro\"><img alt=\"Published Astro package version\" src=\"https://img.shields.io/npm/v/@santi020k/lumen-astro?style=flat-square&label=npm\"></a>\n <a href=\"./LICENSE\"><img alt=\"MIT license\" src=\"https://img.shields.io/badge/license-MIT-036b8c?style=flat-square\"></a>\n</p>\n\nLumen gives your product a consistent visual language while preserving each framework's native\nway of building. Compose accessible controls, forms, charts, and complete product layouts from\nshared tokens and explicit component contracts.\n\n**Explore:** [Quick start](#web-quick-start) · [Native apps](#native-playgrounds) · [Templates](#dashboards-and-templates) · [AI and Figma](#ai-and-design-workflows) · [Packages](#packages) · [Repository apps](#repository-apps) · [Contributing](#contributing)\n\n> **Lumen 4:** See the [v4.2 highlights](docs/lumen-4.2.md) for SwiftUI appearance and agent guidance,\n> and [v4.1 highlights](docs/lumen-4.1.md) for loading and forms. Follow the [migration guide](docs/migrating-v3-to-v4.md) when\n> upgrading from v3. Package versions and store builds follow their own release schedules.\n\n## See Lumen in action\n\n<p>\n <a href=\"https://lumen.santi020k.com/docs/web/playground\">\n <img src=\"./apps/docs/src/assets/platforms/web.webp\" alt=\"A Lumen web workspace with navigation, metric cards, an interactive chart, and a task form\" width=\"1184\" height=\"802\">\n </a>\n</p>\n\n*Real Lumen components with illustrative data. [Open the web playground →](https://lumen.santi020k.com/docs/web/playground)*\n\n| Built for | What you get |\n| --- | --- |\n| **Web products** | Astro, React, and custom elements with standalone CSS and optional Tailwind integration. |\n| **Native interfaces** | React Native, SwiftUI, Compose, WidgetKit, and Wear OS foundations with platform conventions. |\n| **Accessible interaction** | Semantic structure, keyboard paths, focus management, and reduced-motion support. |\n| **Design and AI workflows** | Figma resources, a portable agent skill, MCP discovery, and machine-readable contracts. |\n\n## Web quick start\n\nChoose one adapter. Astro requires Astro 5+, React requires React 19+, and the web packages\nrequire Node.js 22.12+ for their tooling. Install in your existing application:\n\n```bash\n# Astro\npnpm add @santi020k/lumen-astro\n\n# React\npnpm add @santi020k/lumen-react\n\n# Web Components\npnpm add @santi020k/lumen-elements\n```\n\n### Astro\n\nImport the stylesheet and mount `UIPrimitives` once in your root layout. The runtime enhances all\ninteractive Lumen markup on the page.\n\n```astro\n---\nimport '@santi020k/lumen-astro/styles.css'\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<html lang=\"en\">\n <body>\n <slot />\n <UIPrimitives />\n </body>\n</html>\n```\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\n### React\n\nLoad the stylesheet once from your app entry or global CSS:\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input, Label } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n </Card>\n )\n}\n```\n\n### Web Components\n\nImport the styles and register the elements once:\n\n```html\n<style>\n @import \"@santi020k/lumen-elements/styles.css\";\n</style>\n\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <lumen-label id=\"email-label\">Email</lumen-label>\n <lumen-input aria-labelledby=\"email-label\" id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button>Subscribe</lumen-button>\n</lumen-card>\n```\n\nSee the [Web documentation](https://lumen.santi020k.com/docs/web) for installation, theming,\ncomponent examples, and API details.\n\nBrowse the [package directory](https://lumen.santi020k.com/docs/packages) for framework adapters,\nshared foundations, and optional integrations, with links to focused examples and usage guides.\n\nFor failure states, use the [error-handling guide](./docs/error-handling.md) to choose between field\nfeedback, summaries, persistent alerts, transient toasts, and the `ErrorState` recovery surface.\nLumen owns presentation and accessibility; applications retain logging, retry, and exception policy.\n\nFor native applications, choose the [React Native](https://lumen.santi020k.com/docs/react-native),\n[Apple / SwiftUI](https://lumen.santi020k.com/docs/apple), or\n[Android / Compose](https://lumen.santi020k.com/docs/android) guide. The\n[shared foundations](https://lumen.santi020k.com/docs/foundations) explain the cross-platform token\nand component contract; repository contributors can also use the\n[cross-platform architecture](./docs/cross-platform.md) and\n[native component reference](./docs/native-components.md).\n\n> **Native qualification:** Check the [compatibility matrix](docs/native-compatibility.md) and\n> [device evidence](docs/native-device-validation.md) for your target. Local builds, published\n> artifacts, and physical-device accessibility are distinct verification steps.\n\n## Tailwind CSS\n\nKeep the layer prelude, Tailwind import, and Lumen stylesheet in the same shared CSS entry. Replace\n`lumen-astro` with the package for your framework.\n\n```css\n@import \"@santi020k/lumen-astro/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-astro/styles.css\";\n```\n\nThis order places Tailwind base styles before Lumen components and Tailwind utilities above Lumen\ncomponent defaults.\n\n## Native playgrounds\n\n<table>\n <tr>\n <th>SwiftUI</th>\n <th>Jetpack Compose</th>\n <th>React Native</th>\n </tr>\n <tr>\n <td><a href=\"https://lumen.santi020k.com/docs/apple/playground\"><img src=\"./apps/docs/src/assets/platforms/apple.webp\" alt=\"Native SwiftUI release checklist example\" width=\"220\"></a></td>\n <td><a href=\"https://lumen.santi020k.com/docs/android/playground\"><img src=\"./apps/docs/src/assets/platforms/android.webp\" alt=\"Native Compose release checklist example\" width=\"220\"></a></td>\n <td><a href=\"https://lumen.santi020k.com/docs/react-native/playground\"><img src=\"./apps/docs/src/assets/platforms/react-native.webp\" alt=\"Expo browser preview of the contributor profile example\" width=\"220\"></a></td>\n </tr>\n <tr>\n <td>Native app capture</td>\n <td>Native app capture</td>\n <td>Expo browser preview</td>\n </tr>\n</table>\n\nSee [capture provenance](apps/docs/src/assets/platforms/README.md) for sources and verification scope.\n\nTry Lumen before adding it to your project. The [playground section](https://lumen.santi020k.com/#playgrounds)\nbrings together the browser demos and native apps. Explore component states and themes, then use\nthe open-source apps as implementation references. Start with the\n[web playground](https://lumen.santi020k.com/docs/web/playground) for Astro, React, and Web Components,\nor choose a native gallery:\n\n- [`apps/playground-react-native`](./apps/playground-react-native) runs through Expo on the web,\n iOS, and Android. [Try the browser preview](https://lumen.santi020k.com/docs/react-native/playground#preview)\n or use the local setup guide for native evaluation. EAS profiles cover TestFlight, Android App\n Bundles, and APKs; this gallery has no separate public store listing.\n- [`apps/playground-apple`](./apps/playground-apple) is available on the\n [App Store](https://apps.apple.com/app/id6805250815) for iPhone, iPad, and Mac. It also builds as\n an iOS Xcode app or macOS Swift Package executable for local exploration.\n- [`apps/playground-android`](./apps/playground-android) builds a native Compose application and a\n directly installable debug APK. It is also available on\n [Google Play](https://play.google.com/store/apps/details?id=com.santi020k.lumen.playground.compose)\n for Android phones and tablets.\n\nSee the [playground workflow](./docs/playgrounds.md) for a platform chooser, reference-app source,\nand run, capture, and distribution commands. Store builds follow their own release schedule and\nmay differ from the current repository version.\n\n## Appearance presets\n\nChoose an appearance preset or customize the existing themes with the [appearance guide](docs/appearance-presets.md). The Studio preset uses PostLens as its visual reference.\n\n## Dashboards and templates\n\nExplore the [template gallery](https://lumen.santi020k.com/templates) for complete analytics,\nSaaS admin, commerce, project workspace, and authentication/onboarding experiences. Each family\nuses public Lumen primitives and semantic tokens, includes responsive and accessibility coverage,\nand ships through the CLI for all three framework targets:\n\n```bash\nlumen add analytics-dashboard\nlumen add commerce-dashboard --target react\nlumen add auth-onboarding --target elements\n```\n\n## AI and design workflows\n\nInstall the portable Lumen skill in Codex, Claude Code, Cursor, Windsurf, and other compatible\ncoding agents:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe skill teaches agents how to select, compose, theme, and verify Lumen primitives. Pair it with\n[`@santi020k/lumen-mcp`](./packages/mcp) when an agent needs to search the live catalog or retrieve\ncurrent source, props, tokens, and usage rules.\n\nFor ChatGPT and Codex, install the published\n[Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nfrom the Plugins Directory. Select **Install plugin**, then mention **@Lumen UI** in a request such\nas “Find the right Lumen components for an accessible React settings screen.” The plugin bundles\nthe skill with the hosted, read-only catalog; no separate Lumen account or local MCP setup is needed.\n\nLumen is designed to reduce repetitive UI code and unnecessary agent context. Actual token usage\ndepends on the model, task, and corrections; see the [measurement method](docs/ai-efficiency.md).\n\n<details>\n<summary>Guides, registries, and integration references</summary>\n\nAdditional machine-readable surfaces include:\n\n- [`llms.txt`](./llms.txt) for a concise project map.\n- [`docs/ai-usage.md`](./docs/ai-usage.md) for downstream generation examples.\n- [`docs/styling-contract.md`](./docs/styling-contract.md) for stable parts and component variables.\n- [`docs/import-and-icon-performance.md`](./docs/import-and-icon-performance.md) for repeatable import and icon evidence.\n- [`docs/graphics.md`](./docs/graphics.md) for portable decorative artwork across web and native platforms.\n- [`docs/consumer-regression-fixtures.md`](./docs/consumer-regression-fixtures.md) for production-shaped local coverage.\n- [`docs/project-adoption.md`](./docs/project-adoption.md) for the sibling-project audit and staged\n web and native migration strategy.\n- [`registry/lumen.registry.json`](./registry/lumen.registry.json) for recipes and installable file\n groups.\n- [`docs/figma.md`](./docs/figma.md) for Figma variables and Code Connect workflows.\n- [`apps/figma-plugin/README.md`](./apps/figma-plugin/README.md) for the **beta** Figma plugin,\n Astro starters, and AI handoff.\n- [`plugins/lumen-ui`](./plugins/lumen-ui) for the packaged Codex and ChatGPT plugin, with its\n [submission and review record](./docs/openai-plugin-submission.md).\n- [`.claude-plugin/marketplace.json`](./.claude-plugin/marketplace.json) for the Claude Code\n marketplace, with its [installation and submission guide](./docs/claude-code-plugin.md).\n- [`docs/cross-platform.md`](./docs/cross-platform.md) for the native architecture, support tiers,\n and component-parity policy.\n- [`docs/native-patterns.md`](./docs/native-patterns.md) for developed native compositions and the\n boundary between Lumen presentation and platform-owned application structure.\n\n</details>\n\n## Packages\n\n| Package | Purpose |\n| --- | --- |\n| [`@santi020k/lumen-astro`](./packages/astro) | Reference implementation, Astro components, and progressive-enhancement runtime |\n| [`@santi020k/lumen-react`](./packages/react) | React components and behavior hooks |\n| [`@santi020k/lumen-react-hook-form`](./packages/react-hook-form) | Optional React Hook Form adapters for composite controls |\n| [`@santi020k/lumen-elements`](./packages/elements) | Standards-based Web Components |\n| [`@santi020k/lumen-core`](./packages/core) | Shared tokens, metadata, class helpers, and behavior contracts |\n| [`@santi020k/lumen-tokens`](./packages/tokens) | Canonical platform-neutral design token source |\n| [`@santi020k/lumen-react-native`](./packages/react-native) | React Native foundations and native primitives |\n| [`LumenUI`](./packages/swift) | Swift Package for iOS, iPadOS, macOS, tvOS, visionOS, and watchOS foundations and native primitives |\n| [`LumenWidgetUI`](./packages/swift-widget) | Focused WidgetKit-safe semantic text, icon, badge, and compact-stat foundations |\n| [`lumen-compose`](./packages/compose) | Android library for Jetpack Compose foundations and native primitives |\n| [`lumen-compose-wear`](./packages/compose/wear) | Focused Wear OS primitives for at-a-glance round-screen actions, progress, status, metrics, and rows |\n| [`@santi020k/lumen-icons-brand`](./packages/icons-brand) | Optional namespaced brand icons for web `Icon` adapters |\n| [`@santi020k/lumen`](./packages/lumen) | Umbrella package, CLI, registry metadata, and public package map |\n| [`@santi020k/lumen-mcp`](./packages/mcp) | MCP server for component discovery and source retrieval |\n\nThe private [`@santi020k/lumen-templates`](packages/templates/README.md) workspace supplies the\ngallery and preview renderer. Consumer recipes are installed through the public Lumen CLI.\n\n## Repository apps\n\nEach app has its own setup, source map, and verification guidance:\n\n| App | Purpose |\n| --- | --- |\n| [Documentation](apps/docs/README.md) | Public documentation, live examples, search, and native previews |\n| [Template showcase](apps/templates/README.md) | Standalone gallery of five product-template families |\n| [Next.js smoke app](apps/next-smoke/README.md) | Server/client boundaries and rendered consumer regression fixtures |\n| [Apple playground](apps/playground-apple/README.md) | iPhone, iPad, Mac, watchOS, and WidgetKit reference hosts |\n| [Android playground](apps/playground-android/README.md) | Compose phone/tablet app and separate Wear OS host |\n| [React Native playground](apps/playground-react-native/README.md) | Expo reference app and embedded browser gallery |\n| [Figma plugin](apps/figma-plugin/README.md) | Development beta for Astro starters and structured agent handoff |\n| [Motion studio](apps/motion/README.md) | Reusable product films and local video exports |\n| [Store artwork](apps/store-assets/README.md) | Canonical SVG sources for the playground app icons |\n\n## Lumen 4\n\nLumen 4 brings consumer-driven date selection, chart readability, clipboard feedback,\nloading states, native layout improvements and a new ImageComparison primitive. Start with the\n[v3 → v4 migration guide](docs/migrating-v3-to-v4.md), [consumer audit](docs/lumen-4-consumer-audit.md),\nand [v4.2 release highlights](docs/lumen-4.2.md). The community gallery includes published consumer\nscreenshots; it does not imply those applications already run the current release.\n\nFor existing applications, start with [v1 → v2](docs/migrating-v1-to-v2.md) when needed, then\n[v2 → v3](docs/migrating-v2-to-v3.md) before\n[v3 → v4](docs/migrating-v3-to-v4.md). For initial adoption, use\n[Migrating to Lumen](docs/migrating-to-lumen.md).\n\n## Contributing\n\nLumen is a pnpm workspace. Astro is the reference implementation; shared contracts belong in\n`packages/core`, while framework-specific behavior stays in its adapter package.\n\nUse Node.js 22.22.2+, 24.15.0+, or 26+ and pnpm 12.8.1, as declared in the root manifest.\n\n```bash\npnpm install --frozen-lockfile\npnpm run dev\npnpm run validate\n```\n\nRead [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. User-visible package\nchanges require a changeset.\n\n## Community\n\nCreate reusable product animations and social videos locally with the\n[Lumen motion studio](./apps/motion/README.md). Its first pilot previews one interface in four\nappearances and exports portrait, square, and landscape MP4s through HyperFrames.\n\n[Feedback & support](https://lumen.santi020k.com/support) connects ideas, questions, bug reports,\nand the [public roadmap](https://lumen.santi020k.com/support#roadmap). Discuss improvements on GitHub\nand follow accepted work through implementation and release.\n\nLumen is free and MIT licensed while the project focuses on adoption, public examples, and\nreal-world feedback.\n\n- Explore task-oriented [guides](https://lumen.santi020k.com/guides).\n- Share a public project in the [community showcase](https://lumen.santi020k.com/community).\n- Evaluate the system with the open [team adoption path](https://lumen.santi020k.com/teams).\n- Tell us what would improve your next build through the\n [adoption survey](https://github.com/santi020k/lumen/issues/new?template=adoption-feedback.yml).\n\nMaintainers can use the [exposure playbook](./docs/exposure-playbook.md) for publishing cadence,\ncampaign measurement, video scripts, and launch copy, and the\n[marketing package](./docs/marketing/README.md) for execution-ready copy, channel decisions, a\nfour-week calendar, asset briefs, an approval queue, and a community-response library.\n\n## Find your next step\n\n| Resource | Use it for |\n| --- | --- |\n| [Cross-platform architecture](docs/cross-platform.md) | Shared foundations and adapter responsibilities. |\n| [Native compatibility](docs/native-compatibility.md) | Supported toolchains and platform baselines. |\n| [Native release runbook](docs/native-release-runbook.md) | Artifact qualification and distribution gates. |\n\n## License\n\nLumen's original code and artwork are available under the [MIT License](./LICENSE). Generated\ninterface icons retain the Lucide ISC or Feather MIT terms, and generated brand icons retain Font\nAwesome Free's CC BY 4.0 terms. The native packages include the required attribution and license\nnotices; see [`icons/THIRD_PARTY_NOTICES.md`](./icons/THIRD_PARTY_NOTICES.md).\n\nCreated and maintained by [Santiago Molina](https://santi020k.com).\n"
|
|
45964
46096
|
},
|
|
45965
46097
|
"meta": {
|
|
45966
|
-
"catalogHash": "
|
|
46098
|
+
"catalogHash": "7113faa8466fe24529069b1918abbbfdf18c101d00341b2824cba2149c416e77",
|
|
45967
46099
|
"componentCount": 196,
|
|
45968
46100
|
"nativeComponentCount": 125,
|
|
45969
46101
|
"packages": [
|
|
@@ -45979,19 +46111,19 @@
|
|
|
45979
46111
|
"packageVersions": {
|
|
45980
46112
|
"@santi020k/lumen-astro": "4.1.0",
|
|
45981
46113
|
"@santi020k/lumen-core": "4.1.0",
|
|
45982
|
-
"@santi020k/lumen-elements": "4.
|
|
45983
|
-
"@santi020k/lumen-mcp": "4.
|
|
46114
|
+
"@santi020k/lumen-elements": "4.2.0",
|
|
46115
|
+
"@santi020k/lumen-mcp": "4.2.0",
|
|
45984
46116
|
"@santi020k/lumen-react": "4.1.0",
|
|
45985
46117
|
"@santi020k/lumen-react-native": "4.0.1",
|
|
45986
46118
|
"@santi020k/lumen-tokens": "4.0.0",
|
|
45987
|
-
"LumenUI": "4.
|
|
46119
|
+
"LumenUI": "4.2.0",
|
|
45988
46120
|
"com.santi020k:lumen-compose": "4.0.0",
|
|
45989
46121
|
"com.santi020k:lumen-compose-wear": "4.0.0"
|
|
45990
46122
|
},
|
|
45991
46123
|
"registryName": "lumen",
|
|
45992
46124
|
"registryVersion": 1,
|
|
45993
46125
|
"schemaVersion": 8,
|
|
45994
|
-
"serverVersion": "4.
|
|
46126
|
+
"serverVersion": "4.2.0"
|
|
45995
46127
|
},
|
|
45996
46128
|
"migration": {
|
|
45997
46129
|
"schemaVersion": 1,
|
|
@@ -60434,7 +60566,7 @@
|
|
|
60434
60566
|
"packages/swift/Sources/LumenUI/KanbanBoardComponents.swift": "#if os(iOS) || os(macOS) || os(visionOS)\nimport SwiftUI\n\npublic struct LumenKanbanBoard<CardContent: View>: View {\n @Binding private var columns: [LumenKanbanColumnData]\n @Environment(\\.isEnabled) private var enabled\n private let label: String\n private let readOnly: Bool\n private let loading: Bool\n private let error: String?\n private let loadingLabel: String\n private let emptyLabel: String\n private let invalidLabel: String\n private let formatMove: (String, String, Int) -> String\n private let cardContent: (LumenKanbanCard) -> CardContent\n\n public init(_ label: String, columns: Binding<[LumenKanbanColumnData]>, readOnly: Bool = false,\n loading: Bool = false, error: String? = nil, loadingLabel: String = \"Loading\",\n emptyLabel: String = \"No cards\", invalidLabel: String = \"Invalid board data\",\n formatMove: @escaping (String, String, Int) -> String = { \"Move \\($0) to \\($1), position \\($2)\" },\n @ViewBuilder cardContent: @escaping (LumenKanbanCard) -> CardContent) {\n self.label = label; _columns = columns; self.readOnly = readOnly; self.loading = loading; self.error = error\n self.loadingLabel = loadingLabel; self.emptyLabel = emptyLabel; self.invalidLabel = invalidLabel\n self.formatMove = formatMove; self.cardContent = cardContent\n }\n private var interactive: Bool { enabled && !readOnly && !loading && error == nil }\n private var model: LumenKanbanModel { LumenKanbanModel(columns: columns) }\n private func move(_ cardId: String, to targetId: String, index: Int) -> Bool {\n guard interactive, let next = model.moving(cardId, toColumnId: targetId, toIndex: index) else { return false }\n columns = next\n return true\n }\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.md) {\n LumenText(.verbatim(label)).accessibilityAddTraits(.isHeader)\n if loading { LumenText(.verbatim(loadingLabel)) }\n else if let error { LumenText(.verbatim(error)) }\n else if !model.valid { LumenText(.verbatim(invalidLabel)) }\n else if columns.isEmpty { LumenText(.verbatim(emptyLabel)) }\n else {\n ScrollView(.horizontal) {\n HStack(alignment: .top, spacing: LumenSpacing.md) {\n ForEach(columns) { column in\n VStack(alignment: .leading, spacing: LumenSpacing.md) {\n LumenText(.verbatim(column.label)).accessibilityAddTraits(.isHeader)\n if column.cards.isEmpty { LumenText(.verbatim(emptyLabel)) }\n ForEach(Array(column.cards.enumerated()), id: \\.element.id) { index, card in\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n if interactive && !column.disabled && !card.disabled {\n LumenCard(padding: .md, radius: .md) { cardContent(card) }.draggable(card.id)\n } else { LumenCard(padding: .md, radius: .md) { cardContent(card) } }\n ForEach(columns) { target in\n let positions = target.id == column.id ? [index - 1, index + 1].filter { column.cards.indices.contains($0) } : [target.cards.count]\n ForEach(positions, id: \\.self) { position in\n LumenButton(disabled: !interactive || model.moving(card.id, toColumnId: target.id, toIndex: position) == nil,\n action: { _ = move(card.id, to: target.id, index: position) }) {\n Text(formatMove(card.label, target.label, position + 1))\n }\n }\n }\n }.dropDestination(for: String.self) { ids, _ in\n guard ids.count == 1, let id = ids.first else { return false }\n let currentIndex = column.cards.firstIndex(where: { $0.id == id })\n return move(id, to: column.id, index: currentIndex.map { $0 < index ? index - 1 : index } ?? index)\n }\n }\n }.padding(LumenSpacing.md).frame(width: 272, alignment: .leading).frame(minHeight: 88, alignment: .topLeading)\n .dropDestination(for: String.self) { ids, _ in\n guard ids.count == 1, let id = ids.first else { return false }\n let count = column.cards.filter { $0.id != id }.count\n return move(id, to: column.id, index: count)\n }\n }\n }\n }\n }\n }.accessibilityElement(children: .contain).accessibilityLabel(Text(label))\n }\n}\npublic extension LumenKanbanBoard where CardContent == Text {\n init(_ label: String, columns: Binding<[LumenKanbanColumnData]>, readOnly: Bool = false,\n loading: Bool = false, error: String? = nil, loadingLabel: String = \"Loading\",\n emptyLabel: String = \"No cards\", invalidLabel: String = \"Invalid board data\",\n formatMove: @escaping (String, String, Int) -> String = { \"Move \\($0) to \\($1), position \\($2)\" }) {\n self.init(label, columns: columns, readOnly: readOnly, loading: loading, error: error,\n loadingLabel: loadingLabel, emptyLabel: emptyLabel, invalidLabel: invalidLabel,\n formatMove: formatMove, cardContent: { Text($0.label) })\n }\n}\n#endif\n",
|
|
60435
60567
|
"packages/swift/Sources/LumenUI/KanbanColumnComponents.swift": "#if os(iOS) || os(macOS) || os(visionOS)\nimport SwiftUI\n\npublic struct LumenKanbanColumn<CardContent: View>: View {\n @Binding private var column: LumenKanbanColumnData\n @Environment(\\.isEnabled) private var enabled\n private let readOnly: Bool\n private let loading: Bool\n private let error: String?\n private let onAdd: (() -> Void)?\n private let onCardPress: ((String) -> Void)?\n private let addLabel: String\n private let loadingLabel: String\n private let emptyLabel: String\n private let invalidLabel: String\n private let formatCount: (Int, Int?) -> String\n private let formatMove: (String, Int) -> String\n private let formatOpen: (String) -> String\n private let cardContent: (LumenKanbanCard) -> CardContent\n\n public init(column: Binding<LumenKanbanColumnData>, readOnly: Bool = false,\n loading: Bool = false, error: String? = nil, onAdd: (() -> Void)? = nil,\n onCardPress: ((String) -> Void)? = nil, addLabel: String = \"Add card\",\n loadingLabel: String = \"Loading\", emptyLabel: String = \"No cards\",\n invalidLabel: String = \"Invalid column data\",\n formatCount: @escaping (Int, Int?) -> String = { count, capacity in capacity.map { \"\\(count) of \\($0) cards\" } ?? \"\\(count) cards\" },\n formatMove: @escaping (String, Int) -> String = { \"Move \\($0) to position \\($1)\" },\n formatOpen: @escaping (String) -> String = { \"Open \\($0)\" },\n @ViewBuilder cardContent: @escaping (LumenKanbanCard) -> CardContent) {\n _column = column; self.readOnly = readOnly; self.loading = loading; self.error = error\n self.onAdd = onAdd; self.onCardPress = onCardPress; self.addLabel = addLabel\n self.loadingLabel = loadingLabel; self.emptyLabel = emptyLabel; self.invalidLabel = invalidLabel\n self.formatCount = formatCount; self.formatMove = formatMove; self.formatOpen = formatOpen\n self.cardContent = cardContent\n }\n private var interactive: Bool { enabled && !readOnly && !column.disabled && !loading && error == nil }\n private var model: LumenKanbanModel { LumenKanbanModel(columns: [column]) }\n private func move(_ id: String, index: Int) -> Bool {\n guard interactive, let next = model.moving(id, toColumnId: column.id, toIndex: index)?.first else { return false }\n column = next\n return true\n }\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.md) {\n LumenText(.verbatim(column.label)).accessibilityAddTraits(.isHeader)\n if loading { LumenText(.verbatim(loadingLabel)) }\n else if let error { LumenText(.verbatim(error)) }\n else if !model.valid { LumenText(.verbatim(invalidLabel)) }\n else {\n LumenText(.verbatim(formatCount(column.cards.count, column.capacity)))\n if column.cards.isEmpty { LumenText(.verbatim(emptyLabel)) }\n ForEach(Array(column.cards.enumerated()), id: \\.element.id) { index, card in\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n if interactive && !card.disabled {\n LumenCard(padding: .md, radius: .md) { cardContent(card) }.draggable(card.id)\n } else { LumenCard(padding: .md, radius: .md) { cardContent(card) } }\n if let onCardPress {\n LumenButton(disabled: column.disabled || card.disabled, action: {\n if enabled && !column.disabled && !card.disabled { onCardPress(card.id) }\n }) { Text(formatOpen(card.label)) }\n }\n ForEach([index - 1, index + 1].filter { column.cards.indices.contains($0) }, id: \\.self) { position in\n LumenButton(disabled: !interactive || model.moving(card.id, toColumnId: column.id, toIndex: position) == nil,\n action: { _ = move(card.id, index: position) }) {\n Text(formatMove(card.label, position + 1))\n }\n }\n }.dropDestination(for: String.self) { ids, _ in\n guard ids.count == 1, let id = ids.first else { return false }\n let current = column.cards.firstIndex(where: { $0.id == id })\n return move(id, index: current.map { $0 < index ? index - 1 : index } ?? index)\n }\n }\n if let onAdd {\n LumenButton(disabled: !interactive || column.capacity.map { column.cards.count >= $0 } == true,\n action: { if interactive && column.capacity.map({ column.cards.count < $0 }) != false { onAdd() } }) {\n Text(addLabel)\n }\n }\n }\n }.frame(minHeight: 88, alignment: .topLeading)\n .accessibilityElement(children: .contain).accessibilityLabel(Text(column.label))\n .dropDestination(for: String.self) { ids, _ in\n guard ids.count == 1, let id = ids.first else { return false }\n return move(id, index: column.cards.filter { $0.id != id }.count)\n }\n }\n}\npublic extension LumenKanbanColumn where CardContent == Text {\n init(column: Binding<LumenKanbanColumnData>, readOnly: Bool = false, loading: Bool = false,\n error: String? = nil, onAdd: (() -> Void)? = nil, onCardPress: ((String) -> Void)? = nil,\n addLabel: String = \"Add card\", loadingLabel: String = \"Loading\", emptyLabel: String = \"No cards\",\n invalidLabel: String = \"Invalid column data\",\n formatCount: @escaping (Int, Int?) -> String = { count, capacity in capacity.map { \"\\(count) of \\($0) cards\" } ?? \"\\(count) cards\" },\n formatMove: @escaping (String, Int) -> String = { \"Move \\($0) to position \\($1)\" },\n formatOpen: @escaping (String) -> String = { \"Open \\($0)\" }) {\n self.init(column: column, readOnly: readOnly, loading: loading, error: error, onAdd: onAdd,\n onCardPress: onCardPress, addLabel: addLabel, loadingLabel: loadingLabel,\n emptyLabel: emptyLabel, invalidLabel: invalidLabel, formatCount: formatCount,\n formatMove: formatMove, formatOpen: formatOpen, cardContent: { Text($0.label) })\n }\n}\n#endif\n",
|
|
60436
60568
|
"packages/swift/Sources/LumenUI/KanbanModels.swift": "import Foundation\n\npublic struct LumenKanbanCard: Identifiable, Equatable, Sendable {\n public let id: String\n public var label: String\n public var disabled: Bool\n public init(id: String, label: String, disabled: Bool = false) {\n self.id = id; self.label = label; self.disabled = disabled\n }\n}\npublic struct LumenKanbanColumnData: Identifiable, Equatable, Sendable {\n public let id: String\n public var label: String\n public var cards: [LumenKanbanCard]\n public var capacity: Int?\n public var disabled: Bool\n public init(id: String, label: String, cards: [LumenKanbanCard], capacity: Int? = nil, disabled: Bool = false) {\n self.id = id; self.label = label; self.cards = cards; self.capacity = capacity; self.disabled = disabled\n }\n}\n/// Destination indices are measured after removing the moving card.\npublic struct LumenKanbanModel: Sendable {\n public let columns: [LumenKanbanColumnData]\n public let valid: Bool\n public init(columns: [LumenKanbanColumnData]) {\n self.columns = columns\n var columnIds: Set<String> = []; var cardIds: Set<String> = []\n valid = columns.allSatisfy { column in\n guard !column.id.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,\n columnIds.insert(column.id).inserted,\n column.capacity.map({ $0 >= column.cards.count }) ?? true else { return false }\n return column.cards.allSatisfy { card in\n !card.id.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && cardIds.insert(card.id).inserted\n }\n }\n }\n public func moving(_ cardId: String, toColumnId: String, toIndex: Int) -> [LumenKanbanColumnData]? {\n guard valid, let source = columns.first(where: { $0.cards.contains(where: { $0.id == cardId }) }),\n let target = columns.first(where: { $0.id == toColumnId }),\n let card = source.cards.first(where: { $0.id == cardId }), !source.disabled, !target.disabled, !card.disabled else { return nil }\n var destination = target.cards.filter { $0.id != cardId }\n guard toIndex >= 0, toIndex <= destination.count,\n target.capacity.map({ destination.count < $0 }) ?? true else { return nil }\n if source.id == target.id && source.cards.firstIndex(where: { $0.id == cardId }) == toIndex { return nil }\n destination.insert(card, at: toIndex)\n return columns.map { column in\n var next = column\n if column.id == target.id { next.cards = destination }\n else if column.id == source.id { next.cards.removeAll { $0.id == cardId } }\n return next\n }\n }\n}\n",
|
|
60437
|
-
"packages/swift/Sources/LumenUI/LumenTheme.swift": "import SwiftUI\n\npublic enum LumenColorScheme: Sendable {\n case dark\n case light\n}\n\npublic enum LumenSurfaceMaterial: Sendable {\n case solid\n case glass\n}\n\npublic struct LumenAppearance: Sendable {\n public let radiusScale: CGFloat\n public let spacingScale: CGFloat\n public let borderWidth: CGFloat\n public let elevationScale: CGFloat\n public let material: LumenSurfaceMaterial\n\n public init(\n radiusScale: CGFloat = 1, spacingScale: CGFloat = 1,\n borderWidth: CGFloat = 1, elevationScale: CGFloat = 1,\n material: LumenSurfaceMaterial = .solid\n ) {\n precondition([radiusScale, spacingScale, borderWidth, elevationScale].allSatisfy { $0.isFinite && $0 >= 0 })\n self.radiusScale = radiusScale\n self.spacingScale = spacingScale\n self.borderWidth = borderWidth\n self.elevationScale = elevationScale\n self.material = material\n }\n}\n\npublic struct LumenTextStyles: Sendable {\n public let body: Font\n public let caption: Font\n public let label: Font\n public let title: Font\n\n public init(\n body: Font = .body, caption: Font = .caption,\n label: Font = .callout.weight(.semibold), title: Font = .title2.weight(.bold)\n ) {\n self.body = body\n self.caption = caption\n self.label = label\n self.title = title\n }\n}\n\npublic struct LumenTheme: Sendable {\n public let colors: LumenColorPalette\n public let scheme: LumenColorScheme\n public let appearance: LumenAppearance\n public let textStyles: LumenTextStyles\n\n public init(\n colors: LumenColorPalette, scheme: LumenColorScheme,\n appearance: LumenAppearance = LumenAppearance(),\n textStyles: LumenTextStyles = LumenTextStyles()\n ) {\n self.colors = colors\n self.scheme = scheme\n self.appearance = appearance\n self.textStyles = textStyles\n }\n\n public init(\n preset: LumenThemePreset, scheme: LumenColorScheme,\n appearance: LumenAppearance? = nil,\n textStyles: LumenTextStyles = LumenTextStyles()\n ) {\n self.init(colors: preset.colors(for: scheme), scheme: scheme,\n appearance: appearance ?? preset.appearance, textStyles: textStyles)\n }\n\n public static let light = LumenTheme(colors: LumenColors.light, scheme: .light)\n public static let dark = LumenTheme(colors: LumenColors.dark, scheme: .dark)\n}\n\npublic extension LumenColorPalette {\n func overriding(\n canvas: Color? = nil,\n surface: Color? = nil,\n surfaceMuted: Color? = nil,\n surfaceStrong: Color? = nil,\n line: Color? = nil,\n ink: Color? = nil,\n inkSoft: Color? = nil,\n inkMuted: Color? = nil,\n brand: Color? = nil,\n brandSolid: Color? = nil,\n brandSoft: Color? = nil,\n onBrand: Color? = nil,\n accent: Color? = nil,\n success: Color? = nil,\n warning: Color? = nil,\n danger: Color? = nil,\n onDanger: Color? = nil\n ) -> LumenColorPalette {\n LumenColorPalette(\n canvas: canvas ?? self.canvas,\n surface: surface ?? self.surface,\n surfaceMuted: surfaceMuted ?? self.surfaceMuted,\n surfaceStrong: surfaceStrong ?? self.surfaceStrong,\n line: line ?? self.line,\n ink: ink ?? self.ink,\n inkSoft: inkSoft ?? self.inkSoft,\n inkMuted: inkMuted ?? self.inkMuted,\n brand: brand ?? self.brand,\n brandSolid: brandSolid ?? self.brandSolid,\n brandSoft: brandSoft ?? self.brandSoft,\n onBrand: onBrand ?? self.onBrand,\n accent: accent ?? self.accent,\n success: success ?? self.success,\n warning: warning ?? self.warning,\n danger: danger ?? self.danger,\n onDanger: onDanger ?? self.onDanger\n )\n }\n}\n\nextension LumenTheme {\n func resolvedPreferredColorScheme(enforceColorScheme: Bool) -> ColorScheme? {\n guard enforceColorScheme else { return nil }\n\n return scheme == .dark ? .dark : .light\n }\n}\n\nprivate struct LumenThemeKey: EnvironmentKey {\n static let defaultValue = LumenTheme.light\n}\n\npublic extension EnvironmentValues {\n var lumenTheme: LumenTheme {\n get { self[LumenThemeKey.self] }\n set { self[LumenThemeKey.self] = newValue }\n }\n}\n\npublic extension View {\n func lumenTheme(\n _ theme: LumenTheme,\n enforceColorScheme: Bool = true\n ) -> some View {\n lumenTheme(theme, enforceColorScheme: enforceColorScheme, applyTint: true)\n }\n\n @ViewBuilder\n func lumenTheme(\n _ theme: LumenTheme,\n enforceColorScheme: Bool = true,\n applyTint: Bool\n ) -> some View {\n
|
|
60569
|
+
"packages/swift/Sources/LumenUI/LumenTheme.swift": "import SwiftUI\n\npublic enum LumenColorScheme: Sendable {\n case dark\n case light\n}\n\npublic enum LumenSurfaceMaterial: Sendable {\n case solid\n case glass\n}\n\npublic struct LumenAppearance: Sendable {\n public let radiusScale: CGFloat\n public let spacingScale: CGFloat\n public let borderWidth: CGFloat\n public let elevationScale: CGFloat\n public let material: LumenSurfaceMaterial\n\n public init(\n radiusScale: CGFloat = 1, spacingScale: CGFloat = 1,\n borderWidth: CGFloat = 1, elevationScale: CGFloat = 1,\n material: LumenSurfaceMaterial = .solid\n ) {\n precondition([radiusScale, spacingScale, borderWidth, elevationScale].allSatisfy { $0.isFinite && $0 >= 0 })\n self.radiusScale = radiusScale\n self.spacingScale = spacingScale\n self.borderWidth = borderWidth\n self.elevationScale = elevationScale\n self.material = material\n }\n}\n\npublic struct LumenTextStyles: Sendable {\n public let body: Font\n public let caption: Font\n public let label: Font\n public let title: Font\n\n public init(\n body: Font = .body, caption: Font = .caption,\n label: Font = .callout.weight(.semibold), title: Font = .title2.weight(.bold)\n ) {\n self.body = body\n self.caption = caption\n self.label = label\n self.title = title\n }\n}\n\npublic struct LumenTheme: Sendable {\n public let colors: LumenColorPalette\n public let scheme: LumenColorScheme\n public let appearance: LumenAppearance\n public let textStyles: LumenTextStyles\n\n public init(\n colors: LumenColorPalette, scheme: LumenColorScheme,\n appearance: LumenAppearance = LumenAppearance(),\n textStyles: LumenTextStyles = LumenTextStyles()\n ) {\n self.colors = colors\n self.scheme = scheme\n self.appearance = appearance\n self.textStyles = textStyles\n }\n\n public init(\n preset: LumenThemePreset, scheme: LumenColorScheme,\n appearance: LumenAppearance? = nil,\n textStyles: LumenTextStyles = LumenTextStyles()\n ) {\n self.init(colors: preset.colors(for: scheme), scheme: scheme,\n appearance: appearance ?? preset.appearance, textStyles: textStyles)\n }\n\n public static let light = LumenTheme(colors: LumenColors.light, scheme: .light)\n public static let dark = LumenTheme(colors: LumenColors.dark, scheme: .dark)\n}\n\npublic extension LumenColorPalette {\n func overriding(\n canvas: Color? = nil,\n surface: Color? = nil,\n surfaceMuted: Color? = nil,\n surfaceStrong: Color? = nil,\n line: Color? = nil,\n ink: Color? = nil,\n inkSoft: Color? = nil,\n inkMuted: Color? = nil,\n brand: Color? = nil,\n brandSolid: Color? = nil,\n brandSoft: Color? = nil,\n onBrand: Color? = nil,\n accent: Color? = nil,\n success: Color? = nil,\n warning: Color? = nil,\n danger: Color? = nil,\n onDanger: Color? = nil\n ) -> LumenColorPalette {\n LumenColorPalette(\n canvas: canvas ?? self.canvas,\n surface: surface ?? self.surface,\n surfaceMuted: surfaceMuted ?? self.surfaceMuted,\n surfaceStrong: surfaceStrong ?? self.surfaceStrong,\n line: line ?? self.line,\n ink: ink ?? self.ink,\n inkSoft: inkSoft ?? self.inkSoft,\n inkMuted: inkMuted ?? self.inkMuted,\n brand: brand ?? self.brand,\n brandSolid: brandSolid ?? self.brandSolid,\n brandSoft: brandSoft ?? self.brandSoft,\n onBrand: onBrand ?? self.onBrand,\n accent: accent ?? self.accent,\n success: success ?? self.success,\n warning: warning ?? self.warning,\n danger: danger ?? self.danger,\n onDanger: onDanger ?? self.onDanger\n )\n }\n}\n\nextension LumenTheme {\n func resolvedPreferredColorScheme(enforceColorScheme: Bool) -> ColorScheme? {\n guard enforceColorScheme else { return nil }\n\n return scheme == .dark ? .dark : .light\n }\n}\n\nprivate struct LumenThemeKey: EnvironmentKey {\n static let defaultValue = LumenTheme.light\n}\n\npublic extension EnvironmentValues {\n var lumenTheme: LumenTheme {\n get { self[LumenThemeKey.self] }\n set { self[LumenThemeKey.self] = newValue }\n }\n}\n\npublic extension View {\n func lumenTheme(\n _ theme: LumenTheme,\n enforceColorScheme: Bool = true\n ) -> some View {\n lumenTheme(theme, enforceColorScheme: enforceColorScheme, applyTint: true)\n }\n\n @ViewBuilder\n func lumenTheme(\n _ theme: LumenTheme,\n enforceColorScheme: Bool = true,\n applyTint: Bool\n ) -> some View {\n let preferredScheme = theme.resolvedPreferredColorScheme(enforceColorScheme: enforceColorScheme)\n // Emit the optional window preference from a background child so changing enforcement\n // does not move application content between conditional branches and reset its state.\n let themed = environment(\\.lumenTheme, theme).background {\n if let preferredScheme {\n Color.clear.preferredColorScheme(preferredScheme)\n }\n }\n\n if applyTint {\n themed.tint(theme.colors.brandSolid)\n } else {\n themed\n }\n }\n}\n",
|
|
60438
60570
|
"packages/swift/Sources/LumenUI/MacComponents.swift": "#if os(macOS)\nimport AppKit\nimport SwiftUI\n\npublic struct LumenShortcut: Hashable {\n public let display: String\n public let keyCode: UInt16\n public let modifiers: NSEvent.ModifierFlags\n\n public init(\n keyCode: UInt16,\n modifiers: NSEvent.ModifierFlags,\n display: String? = nil\n ) {\n self.keyCode = keyCode\n self.modifiers = modifiers.intersection(.deviceIndependentFlagsMask)\n self.display = display ?? Self.format(\n keyCode: keyCode,\n modifiers: modifiers,\n characters: nil\n )\n }\n\n public func hash(into hasher: inout Hasher) {\n hasher.combine(keyCode)\n hasher.combine(modifiers.rawValue)\n }\n\n public static func == (lhs: LumenShortcut, rhs: LumenShortcut) -> Bool {\n lhs.keyCode == rhs.keyCode && lhs.modifiers.rawValue == rhs.modifiers.rawValue\n }\n\n static func from(_ event: NSEvent) -> LumenShortcut {\n LumenShortcut(\n keyCode: event.keyCode,\n modifiers: event.modifierFlags,\n display: format(\n keyCode: event.keyCode,\n modifiers: event.modifierFlags,\n characters: event.charactersIgnoringModifiers\n )\n )\n }\n\n static func format(\n keyCode: UInt16,\n modifiers: NSEvent.ModifierFlags,\n characters: String?\n ) -> String {\n let flags = modifiers.intersection(.deviceIndependentFlagsMask)\n var parts: [String] = []\n\n if flags.contains(.control) { parts.append(\"⌃\") }\n if flags.contains(.option) { parts.append(\"⌥\") }\n if flags.contains(.shift) { parts.append(\"⇧\") }\n if flags.contains(.command) { parts.append(\"⌘\") }\n\n let specialKeys: [UInt16: String] = [\n 36: \"↩\",\n 48: \"⇥\",\n 49: \"Space\",\n 51: \"⌫\",\n 115: \"↖\",\n 116: \"⇞\",\n 119: \"↘\",\n 121: \"⇟\",\n 123: \"←\",\n 124: \"→\",\n 125: \"↓\",\n 126: \"↑\",\n ]\n\n if let specialKey = specialKeys[keyCode] {\n parts.append(specialKey)\n } else if let characters, let character = characters.first {\n parts.append(String(character).uppercased())\n } else {\n parts.append(\"Key \\(keyCode)\")\n }\n\n return parts.joined()\n }\n}\n\n@MainActor\nprivate final class LumenShortcutRecorderState: ObservableObject {\n @Published var errorMessage: String?\n @Published var isRecording = false\n var monitor: Any?\n}\n\npublic struct LumenShortcutRecorder: View {\n @Binding private var shortcut: LumenShortcut?\n @Environment(\\.lumenTheme) private var theme\n @StateObject private var state = LumenShortcutRecorderState()\n\n private let label: LocalizedStringKey\n private let validation: ((LumenShortcut) -> String?)?\n\n public init(\n _ label: LocalizedStringKey,\n shortcut: Binding<LumenShortcut?>,\n validation: ((LumenShortcut) -> String?)? = nil\n ) {\n self.label = label\n _shortcut = shortcut\n self.validation = validation\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n HStack(spacing: LumenSpacing.md) {\n Text(label)\n .font(.callout.weight(.semibold))\n .foregroundStyle(theme.colors.ink)\n\n Spacer(minLength: LumenSpacing.md)\n\n if state.isRecording {\n Text(\"Press keys…\")\n .font(.caption.monospaced())\n .foregroundStyle(theme.colors.inkMuted)\n\n LumenButton(\"Cancel\", intent: .quiet, size: .sm) {\n stopRecording()\n }\n } else {\n if let shortcut {\n Text(shortcut.display)\n .font(.caption.weight(.semibold).monospaced())\n .foregroundStyle(theme.colors.ink)\n .padding(.horizontal, LumenSpacing.sm)\n .padding(.vertical, LumenSpacing.xs)\n .background(theme.colors.surfaceStrong)\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm))\n .accessibilityLabel(\"Current shortcut \\(shortcut.display)\")\n }\n\n LumenButton(shortcut == nil ? \"Record…\" : \"Change…\", intent: .secondary, size: .sm) {\n startRecording()\n }\n\n if shortcut != nil {\n LumenButton(\"Clear\", intent: .quiet, size: .sm) {\n shortcut = nil\n state.errorMessage = nil\n }\n }\n }\n }\n\n if let errorMessage = state.errorMessage {\n HStack(spacing: LumenSpacing.sm) {\n LumenIcon(\n systemName: \"exclamationmark.triangle\",\n size: .sm,\n color: theme.colors.warning\n )\n Text(errorMessage)\n .font(.caption)\n .foregroundStyle(theme.colors.warning)\n }\n .accessibilityElement(children: .combine)\n }\n }\n .onDisappear {\n stopRecording()\n }\n }\n\n private func startRecording() {\n guard state.monitor == nil else { return }\n\n state.errorMessage = nil\n state.isRecording = true\n state.monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in\n handle(event)\n return nil\n }\n }\n\n private func stopRecording() {\n if let monitor = state.monitor {\n NSEvent.removeMonitor(monitor)\n }\n state.monitor = nil\n state.isRecording = false\n }\n\n private func handle(_ event: NSEvent) {\n defer { stopRecording() }\n\n if event.keyCode == 53 {\n return\n }\n\n let shortcut = LumenShortcut.from(event)\n let commandModifiers: NSEvent.ModifierFlags = [.command, .control, .option, .shift]\n\n guard !shortcut.modifiers.intersection(commandModifiers).isEmpty else {\n state.errorMessage = \"Include at least one modifier key.\"\n return\n }\n\n if let validationMessage = validation?(shortcut) {\n state.errorMessage = validationMessage\n return\n }\n\n self.shortcut = shortcut\n state.errorMessage = nil\n }\n}\n\npublic struct LumenSymbolOption: Hashable, Identifiable, Sendable {\n public let category: String\n public let label: String\n public let name: String\n\n public var id: String { name }\n\n public init(name: String, label: String, category: String) {\n self.name = name\n self.label = label\n self.category = category\n }\n\n public static let common: [LumenSymbolOption] = [\n LumenSymbolOption(name: \"folder\", label: \"Folder\", category: \"Files\"),\n LumenSymbolOption(name: \"doc\", label: \"Document\", category: \"Files\"),\n LumenSymbolOption(name: \"tray\", label: \"Tray\", category: \"Files\"),\n LumenSymbolOption(name: \"archivebox\", label: \"Archive\", category: \"Files\"),\n LumenSymbolOption(name: \"desktopcomputer\", label: \"Desktop\", category: \"Devices\"),\n LumenSymbolOption(name: \"laptopcomputer\", label: \"Laptop\", category: \"Devices\"),\n LumenSymbolOption(name: \"display\", label: \"Display\", category: \"Devices\"),\n LumenSymbolOption(name: \"iphone\", label: \"Phone\", category: \"Devices\"),\n LumenSymbolOption(name: \"hammer\", label: \"Build\", category: \"Work\"),\n LumenSymbolOption(name: \"wrench.and.screwdriver\", label: \"Tools\", category: \"Work\"),\n LumenSymbolOption(name: \"briefcase\", label: \"Business\", category: \"Work\"),\n LumenSymbolOption(name: \"chart.bar\", label: \"Analytics\", category: \"Work\"),\n LumenSymbolOption(name: \"person.2\", label: \"People\", category: \"People\"),\n LumenSymbolOption(name: \"bubble.left.and.bubble.right\", label: \"Messages\", category: \"People\"),\n LumenSymbolOption(name: \"video\", label: \"Video\", category: \"People\"),\n LumenSymbolOption(name: \"calendar\", label: \"Calendar\", category: \"Planning\"),\n LumenSymbolOption(name: \"checklist\", label: \"Tasks\", category: \"Planning\"),\n LumenSymbolOption(name: \"clock\", label: \"Time\", category: \"Planning\"),\n LumenSymbolOption(name: \"flag\", label: \"Milestone\", category: \"Planning\"),\n LumenSymbolOption(name: \"star\", label: \"Favorite\", category: \"General\"),\n LumenSymbolOption(name: \"heart\", label: \"Health\", category: \"General\"),\n LumenSymbolOption(name: \"house\", label: \"Home\", category: \"General\"),\n LumenSymbolOption(name: \"gearshape\", label: \"Settings\", category: \"General\"),\n ]\n}\n\npublic struct LumenSymbolPicker: View {\n @Binding private var selectedName: String\n @Environment(\\.lumenTheme) private var theme\n @State private var query = \"\"\n\n private let options: [LumenSymbolOption]\n private let title: LocalizedStringKey\n private let tint: Color?\n private let onSelection: ((String) -> Void)?\n\n public init(\n _ title: LocalizedStringKey = \"Choose a symbol\",\n selectedName: Binding<String>,\n options: [LumenSymbolOption] = LumenSymbolOption.common,\n tint: Color? = nil,\n onSelection: ((String) -> Void)? = nil\n ) {\n self.title = title\n _selectedName = selectedName\n self.options = options\n self.tint = tint\n self.onSelection = onSelection\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.md) {\n Text(title)\n .font(.headline)\n .foregroundStyle(theme.colors.ink)\n\n LumenSearchField(\"Search symbols\", text: $query)\n\n LumenDivider()\n\n if matchingOptions.isEmpty {\n LumenEmptyState(\n \"No symbols found\",\n systemName: \"magnifyingglass\",\n description: \"Try a different search term.\"\n )\n } else {\n ScrollView {\n LazyVStack(alignment: .leading, spacing: LumenSpacing.lg) {\n ForEach(categories, id: \\.self) { category in\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n Text(category)\n .font(.caption.weight(.semibold))\n .foregroundStyle(theme.colors.inkMuted)\n\n LazyVGrid(columns: columns, alignment: .leading, spacing: LumenSpacing.sm) {\n ForEach(matchingOptions.filter { $0.category == category }) { option in\n symbolButton(option)\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n private var categories: [String] {\n options.map(\\.category).reduce(into: []) { result, category in\n if !result.contains(category) && matchingOptions.contains(where: { $0.category == category }) {\n result.append(category)\n }\n }\n }\n\n private var columns: [GridItem] {\n [GridItem(.adaptive(minimum: 64, maximum: 88), spacing: LumenSpacing.sm)]\n }\n\n private var matchingOptions: [LumenSymbolOption] {\n guard !query.isEmpty else { return options }\n\n return options.filter {\n $0.label.localizedCaseInsensitiveContains(query)\n || $0.name.localizedCaseInsensitiveContains(query)\n || $0.category.localizedCaseInsensitiveContains(query)\n }\n }\n\n func select(_ name: String) {\n selectedName = name\n onSelection?(name)\n }\n\n private func symbolButton(_ option: LumenSymbolOption) -> some View {\n let isSelected = selectedName == option.name\n\n return Button {\n select(option.name)\n } label: {\n VStack(spacing: LumenSpacing.xs) {\n LumenIcon(\n systemName: option.name,\n size: .md,\n color: isSelected ? (tint ?? theme.colors.brand) : theme.colors.inkSoft\n )\n Text(option.label)\n .font(.caption2)\n .lineLimit(1)\n }\n .frame(maxWidth: .infinity, minHeight: 56)\n .padding(.horizontal, LumenSpacing.xs)\n .background(isSelected ? theme.colors.brandSoft : theme.colors.surfaceMuted)\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous)\n .stroke(isSelected ? (tint ?? theme.colors.brand) : theme.colors.line, lineWidth: 1)\n }\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.sm, style: .continuous))\n }\n .buttonStyle(.plain)\n .help(option.label)\n .accessibilityLabel(option.label)\n .accessibilityAddTraits(isSelected ? .isSelected : [])\n }\n}\n\npublic struct LumenSymbolPickerButton: View {\n @Binding private var selectedName: String\n @Environment(\\.lumenTheme) private var theme\n @State private var isPresented = false\n\n private let label: LocalizedStringKey\n private let options: [LumenSymbolOption]\n private let tint: Color?\n private let dismissOnSelection: Bool\n private let onSelection: ((String) -> Void)?\n\n public init(\n _ label: LocalizedStringKey = \"Choose a symbol\",\n selectedName: Binding<String>,\n options: [LumenSymbolOption] = LumenSymbolOption.common,\n tint: Color? = nil,\n dismissOnSelection: Bool = true,\n onSelection: ((String) -> Void)? = nil\n ) {\n self.label = label\n _selectedName = selectedName\n self.options = options\n self.tint = tint\n self.dismissOnSelection = dismissOnSelection\n self.onSelection = onSelection\n }\n\n public var body: some View {\n Button {\n isPresented = true\n } label: {\n ZStack(alignment: .bottomTrailing) {\n LumenIcon(systemName: selectedName, size: .lg, color: tint ?? theme.colors.brand)\n .frame(width: 42, height: 42)\n .background(theme.colors.brandSoft)\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.md, style: .continuous))\n\n Image(systemName: \"pencil.circle.fill\")\n .font(.system(size: 14, weight: .semibold))\n .foregroundStyle(theme.colors.onBrand, theme.colors.brandSolid)\n .offset(x: 4, y: 4)\n }\n }\n .buttonStyle(.plain)\n .accessibilityLabel(label)\n .help(Text(label))\n .popover(isPresented: $isPresented, arrowEdge: .bottom) {\n LumenSymbolPicker(label, selectedName: $selectedName, options: options, tint: tint) { name in\n if dismissOnSelection { isPresented = false }\n onSelection?(name)\n }\n .padding(LumenSpacing.lg)\n .frame(width: 400, height: 480)\n .lumenTheme(theme)\n }\n }\n}\n#endif\n",
|
|
60439
60571
|
"packages/swift/Sources/LumenUI/MediaComponents.swift": "import SwiftUI\n\npublic enum LumenImageFit: Sendable {\n case contain\n case cover\n\n var contentMode: ContentMode {\n switch self {\n case .contain: .fit\n case .cover: .fill\n }\n }\n}\n\npublic enum LumenImageRadius: Sendable {\n case full\n case lg\n case md\n case none\n case sm\n\n var value: CGFloat {\n switch self {\n case .full: LumenRadius.full\n case .lg: LumenRadius.lg\n case .md: LumenRadius.md\n case .none: 0\n case .sm: LumenRadius.sm\n }\n }\n}\n\npublic struct LumenImage<Content: View>: View {\n private let aspectRatio: CGFloat?\n private let content: Content\n private let fit: LumenImageFit\n private let label: String?\n private let radius: LumenImageRadius\n\n public init(\n aspectRatio: CGFloat? = nil,\n fit: LumenImageFit = .cover,\n radius: LumenImageRadius = .lg,\n label: String? = nil,\n @ViewBuilder content: () -> Content\n ) {\n if let aspectRatio, aspectRatio.isFinite, aspectRatio > 0 {\n self.aspectRatio = aspectRatio\n } else {\n self.aspectRatio = nil\n }\n\n self.content = content()\n self.fit = fit\n self.label = label\n self.radius = radius\n }\n\n public var body: some View {\n content\n .aspectRatio(aspectRatio, contentMode: fit.contentMode)\n .clipShape(RoundedRectangle(cornerRadius: radius.value, style: .continuous))\n .accessibilityElement(children: .ignore)\n .accessibilityHidden(label == nil)\n .accessibilityLabel(Text(label ?? \"\"))\n }\n}\n",
|
|
60440
60572
|
"packages/swift/Sources/LumenUI/MediaWorkspaceComponents.swift": "import SwiftUI\n\npublic struct LumenMediaViewportValue: Equatable, Sendable {\n public var zoom: Double\n public var x: Double\n public var y: Double\n\n public init(zoom: Double = 1, x: Double = 0, y: Double = 0) {\n self.zoom = zoom\n self.x = x\n self.y = y\n }\n\n public func normalized(maxZoom: Double = 4) -> Self {\n let limit = maxZoom.isFinite ? min(16, max(1, maxZoom)) : 4\n let scale = zoom.isFinite ? min(limit, max(1, zoom)) : 1\n return Self(zoom: scale,\n x: scale == 1 ? 0 : (x.isFinite ? min(1, max(-1, x)) : 0),\n y: scale == 1 ? 0 : (y.isFinite ? min(1, max(-1, y)) : 0))\n }\n\n public func applying(_ action: LumenMediaViewportAction, maxZoom: Double = 4) -> Self {\n var next = normalized(maxZoom: maxZoom)\n switch action {\n case .zoomIn: next.zoom += 0.25\n case .zoomOut: next.zoom -= 0.25\n case .fit: next = Self()\n case .left: next.x -= 0.25\n case .right: next.x += 0.25\n case .up: next.y -= 0.25\n case .down: next.y += 0.25\n }\n return next.normalized(maxZoom: maxZoom)\n }\n\n public func panning(dx: Double, dy: Double, width: Double, height: Double, maxZoom: Double = 4) -> Self {\n let current = normalized(maxZoom: maxZoom)\n let extent = (current.zoom - 1) / 2\n guard extent > 0, width.isFinite, height.isFinite, width > 0, height > 0 else { return current }\n return Self(zoom: current.zoom,\n x: current.x + (dx.isFinite ? dx / width / extent : 0),\n y: current.y + (dy.isFinite ? dy / height / extent : 0)).normalized(maxZoom: maxZoom)\n }\n}\n\npublic enum LumenMediaViewportAction: String, CaseIterable, Sendable {\n case zoomIn, zoomOut, fit, left, right, up, down\n}\n\npublic struct LumenMediaViewportLabels: Sendable {\n public var zoomIn: String\n public var zoomOut: String\n public var fit: String\n public var left: String\n public var right: String\n public var up: String\n public var down: String\n\n public init(zoomIn: String = \"Zoom in\", zoomOut: String = \"Zoom out\", fit: String = \"Fit to view\",\n left: String = \"Pan left\", right: String = \"Pan right\", up: String = \"Pan up\", down: String = \"Pan down\") {\n self.zoomIn = zoomIn\n self.zoomOut = zoomOut\n self.fit = fit\n self.left = left\n self.right = right\n self.up = up\n self.down = down\n }\n\n public func label(for action: LumenMediaViewportAction) -> String {\n switch action {\n case .zoomIn: zoomIn\n case .zoomOut: zoomOut\n case .fit: fit\n case .left: left\n case .right: right\n case .up: up\n case .down: down\n }\n }\n}\n\n#if os(iOS) || os(macOS) || os(visionOS)\n/// The application owns media loading and the viewport binding. Zoom is relative to fit.\npublic struct LumenMediaViewport<Content: View>: View {\n @Binding private var value: LumenMediaViewportValue\n @Environment(\\.lumenTheme) private var theme\n @Environment(\\.locale) private var locale\n @State private var dragStart: LumenMediaViewportValue?\n @State private var pinchStart: LumenMediaViewportValue?\n private let label: String\n private let maxZoom: Double\n private let ratio: CGFloat\n private let disabled: Bool\n private let labels: LumenMediaViewportLabels\n private let content: Content\n\n public init(_ label: String, value: Binding<LumenMediaViewportValue>, maxZoom: Double = 4,\n aspectRatio: CGFloat = 16 / 9, disabled: Bool = false,\n labels: LumenMediaViewportLabels = .init(), @ViewBuilder content: () -> Content) {\n self.label = label\n _value = value\n self.maxZoom = maxZoom\n ratio = aspectRatio.isFinite && (0.1...10).contains(aspectRatio) ? aspectRatio : 16 / 9\n self.disabled = disabled\n self.labels = labels\n self.content = content()\n }\n\n private var current: LumenMediaViewportValue { value.normalized(maxZoom: maxZoom) }\n private var zoomLabel: String { current.zoom.formatted(.percent.locale(locale).precision(.fractionLength(0))) }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n GeometryReader { geometry in\n content\n .frame(width: geometry.size.width, height: geometry.size.height)\n .scaleEffect(current.zoom)\n .offset(x: current.x * (current.zoom - 1) * geometry.size.width / 2,\n y: current.y * (current.zoom - 1) * geometry.size.height / 2)\n .frame(width: geometry.size.width, height: geometry.size.height)\n .clipped()\n .contentShape(Rectangle())\n .gesture(DragGesture(minimumDistance: 2).onChanged { drag in\n guard !disabled, current.zoom > 1, pinchStart == nil else { return }\n let start = dragStart ?? current\n dragStart = start\n value = start.panning(dx: drag.translation.width, dy: drag.translation.height,\n width: geometry.size.width, height: geometry.size.height, maxZoom: maxZoom)\n }.onEnded { _ in dragStart = nil }, including: disabled || current.zoom <= 1 ? .none : .all)\n .simultaneousGesture(MagnificationGesture().onChanged { scale in\n guard !disabled else { return }\n let start = pinchStart ?? current\n pinchStart = start\n dragStart = nil\n value = LumenMediaViewportValue(zoom: start.zoom * scale, x: start.x, y: start.y).normalized(maxZoom: maxZoom)\n }.onEnded { _ in pinchStart = nil }, including: disabled ? .none : .all)\n }\n .aspectRatio(ratio, contentMode: .fit)\n .background(theme.colors.surfaceMuted)\n .clipShape(RoundedRectangle(cornerRadius: LumenRadius.lg))\n .accessibilityLabel(Text(verbatim: label))\n .accessibilityValue(Text(verbatim: zoomLabel))\n Text(verbatim: label)\n Text(verbatim: zoomLabel).monospacedDigit()\n LazyVGrid(columns: [GridItem(.adaptive(minimum: 120))], alignment: .leading, spacing: LumenSpacing.sm) {\n ForEach(LumenMediaViewportAction.allCases, id: \\.self) { action in\n LumenButton(intent: .secondary,\n disabled: disabled || current.applying(action, maxZoom: maxZoom) == current,\n action: { value = current.applying(action, maxZoom: maxZoom) }) {\n Text(verbatim: labels.label(for: action))\n }\n }\n }\n }\n }\n}\n#endif\n\npublic enum LumenMediaThumbnailState: Sendable { case ready, loading, error }\n\nfunc resolveMediaThumbnailStateLabel(_ state: LumenMediaThumbnailState, label: String) -> String {\n if !label.isEmpty { return label }\n return state == .loading ? \"Loading\" : \"Unavailable\"\n}\n\npublic struct LumenMediaThumbnail<Content: View>: View {\n @Environment(\\.lumenTheme) private var theme\n private let label: String\n private let selected: Bool\n private let order: Int?\n private let state: LumenMediaThumbnailState\n private let stateLabel: String\n private let disabled: Bool\n private let onSelectionChange: (Bool) -> Void\n private let content: Content\n\n public init(_ label: String, selected: Bool, order: Int? = nil,\n state: LumenMediaThumbnailState = .ready, stateLabel: String = \"\",\n disabled: Bool = false, onSelectionChange: @escaping (Bool) -> Void,\n @ViewBuilder content: () -> Content) {\n self.label = label\n self.selected = selected\n self.order = order.flatMap { $0 > 0 ? $0 : nil }\n self.state = state\n self.stateLabel = resolveMediaThumbnailStateLabel(state, label: stateLabel)\n self.disabled = disabled\n self.onSelectionChange = onSelectionChange\n self.content = content()\n }\n\n public var body: some View {\n LumenButton(intent: .secondary, disabled: disabled || state != .ready,\n action: { onSelectionChange(!selected) }) {\n VStack(alignment: .leading, spacing: LumenSpacing.xs) {\n content.aspectRatio(1, contentMode: .fit).clipped().accessibilityHidden(true)\n HStack(alignment: .top) {\n Text(verbatim: label)\n if selected { LumenIcon(systemName: \"checkmark\", label: nil) }\n if let order { Text(verbatim: String(order)).monospacedDigit() }\n }\n if state != .ready { Text(verbatim: stateLabel) }\n }\n .padding(LumenSpacing.xs)\n }\n .overlay {\n RoundedRectangle(cornerRadius: LumenRadius.sm)\n .stroke(selected ? theme.colors.brand : theme.colors.line, lineWidth: selected ? 2 : 1)\n }\n .accessibilityLabel(Text(verbatim: label))\n .accessibilityValue(Text(verbatim: state == .ready ? order.map(String.init) ?? \"\" : stateLabel))\n .accessibilityAddTraits(selected ? .isSelected : [])\n }\n}\n\npublic struct LumenMediaFilmstrip<Content: View>: View {\n private let label: String\n private let selectionLabel: String\n private let content: Content\n\n public init(_ label: String, selectionLabel: String, @ViewBuilder content: () -> Content) {\n self.label = label\n self.selectionLabel = selectionLabel\n self.content = content()\n }\n\n public var body: some View {\n VStack(alignment: .leading, spacing: LumenSpacing.sm) {\n LumenText(.verbatim(label), variant: .label)\n LumenText(.verbatim(selectionLabel), variant: .caption)\n ScrollView(.horizontal) {\n HStack(alignment: .top, spacing: LumenSpacing.sm) { content }\n .padding(LumenSpacing.xs)\n }\n }\n }\n}\n",
|
|
@@ -61466,7 +61598,7 @@
|
|
|
61466
61598
|
},
|
|
61467
61599
|
"@santi020k/lumen-elements": {
|
|
61468
61600
|
"peerDependencies": {},
|
|
61469
|
-
"version": "4.
|
|
61601
|
+
"version": "4.2.0"
|
|
61470
61602
|
},
|
|
61471
61603
|
"@santi020k/lumen-icons-brand": {
|
|
61472
61604
|
"peerDependencies": {},
|
|
@@ -61476,11 +61608,11 @@
|
|
|
61476
61608
|
"peerDependencies": {
|
|
61477
61609
|
"react": ">=19"
|
|
61478
61610
|
},
|
|
61479
|
-
"version": "4.
|
|
61611
|
+
"version": "4.2.0"
|
|
61480
61612
|
},
|
|
61481
61613
|
"@santi020k/lumen-mcp": {
|
|
61482
61614
|
"peerDependencies": {},
|
|
61483
|
-
"version": "4.
|
|
61615
|
+
"version": "4.2.0"
|
|
61484
61616
|
},
|
|
61485
61617
|
"@santi020k/lumen-react": {
|
|
61486
61618
|
"peerDependencies": {
|
|
@@ -61515,9 +61647,9 @@
|
|
|
61515
61647
|
"swift": {
|
|
61516
61648
|
"package": "https://github.com/santi020k/lumen.git",
|
|
61517
61649
|
"revision": null,
|
|
61518
|
-
"tag": "v4.
|
|
61650
|
+
"tag": "v4.2.0"
|
|
61519
61651
|
},
|
|
61520
|
-
"version": "4.
|
|
61652
|
+
"version": "4.2.0"
|
|
61521
61653
|
},
|
|
61522
61654
|
"migration": {
|
|
61523
61655
|
"codemod": null,
|
|
@@ -61535,7 +61667,7 @@
|
|
|
61535
61667
|
}
|
|
61536
61668
|
}
|
|
61537
61669
|
},
|
|
61538
|
-
"rules": "# Lumen UI\n\nLumen is a multi-framework primitive UI system with Astro as the reference implementation.\n\nUse these files first when generating, reviewing, or migrating apps that use Lumen:\n\n- README.md: public overview and package strategy.\n- docs/ai-usage.md: AI-focused setup, package selection, and component selection.\n- https://lumen.santi020k.com/guides/build-ui-with-ai: tested Astro and React adoption example with a reusable prompt.\n- docs/ai-efficiency.md: reproducible AI token measurement, quality checks, and limits on efficiency claims.\n- skills/lumen-ui/SKILL.md: portable Agent Skill for building, reviewing, and theming Lumen interfaces.\n- docs/feature-roadmap.md: competitor-informed gaps and implementation priorities.\n- registry/lumen.registry.json: installable component, recipe, and documentation manifest.\n- packages/core/src/components.ts: authoritative shared component catalog.\n- packages/astro/components: reference component markup and prop shapes.\n- packages/astro/styles/lumen.css: standalone CSS contract for all frameworks.\n- @santi020k/lumen-mcp: Model Context Protocol server exposing snapshot provenance, structured component discovery, framework-aware usage/source, recipes, tolerant search, tokens, rules, and lumen:// resources for AI agents.\n\nInstall the portable Lumen Agent Skill:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nDefault usage:\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\nimport '@santi020k/lumen-astro/styles.css'\n---\n\n<UIPrimitives />\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\nRules for agents:\n\n- When Lumen MCP is connected, read `lumen://meta`, `lumen://diagnostics`, and `lumen://rules`, pass the target framework to `lumen_search`, and follow the framework behavior returned by `lumen_get_component`. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades when the client supports cached context.\n- Prefer Astro unless the user explicitly targets React or Web Components.\n- In React, use the visual components with behavior hooks for Dialog, Popover, DropdownMenu, Tabs, Select, Toast, and Tooltip instead of mounting the Astro runtime.\n- In Web Components, registered `lumen-*` elements include behavior parity for Dialog, Popover, DropdownMenu, Tabs, Select, Toast, and Tooltip, including ARIA state, keyboard paths, Escape/outside dismissal, focus handling, native select form participation, and the shared toast controller.\n- For React report filters, use `DateRangeInput` (`value`, `onValueChange`, localized `labels`, bounds and presets) or inline `DateRangeCalendar`. These React-specific compositions are documented in packages/react/README.md and the reporting example at /docs/web/reporting.\n- React record tables can opt into DataTableView from @santi020k/lumen-react/components/data-table-view for search, filters, visibility, density, pagination and controlled sorting. Read docs/data-table-views.md; applications retain cells, data fetching and domain actions.\n- Keep server-paginated DataTable rows in server order with `sortMode=\"manual\"`, controlled `sort` and `onSortChange`. Apply sorting before pagination in the application backend. Use Dialog dismissal policies for pending workflows.\n- Chart X identities must be unique. Use `xLabel` for abbreviated axes, `formatCategory` for full details, and explicit null gaps for missing observations. Read docs/data-visualization.md.\n- Use ImageComparison for before/after media with matching dimensions, alternative text and a visible localized control label. See docs/image-comparison.md for adapter contracts.\n- Read docs/migrating-to-lumen.md before upgrading to v4, especially date-trigger IDs, loading buttons, hidden semantics, copy feedback and native initializer changes.\n- Do not require Tailwind configuration for Lumen components.\n- Give each outer edge one border and radius owner. Flush images inside rounded frames use Image radius=\"none\"; Image defaults to radius=\"lg\". Use one divider at shared edges, account for padding on inset curves, and check phone/desktop corners and keyboard focus in both themes. Keep artwork clipping scoped so it does not cut off focus rings or overlays. See docs/ai-usage.md for styling rules.\n- Use public tokens: canvas, surface, surface-muted, surface-strong, line, ink, ink-soft, ink-muted, brand, brand-solid, brand-soft, on-brand, accent, success, warning, danger, on-danger.\n- Mount `UIPrimitives` once for interactive Astro primitives.\n- Use `glass` for glass surfaces. The obsolete overlay `surface=\"glass\"` alias is removed.\n- Treat schedule, data collection, rich text, theme builder, and advanced fields as supported surfaces with progressively enhanced behavior.\n- Use `lumen list`, `lumen show <component>`, and `lumen add <component>` for individually addressable registry components; component names also resolve from kebab-case aliases such as `data-table`.\n- Runtime CustomEvents used by `UIPrimitives`: `ui:data-table-selection-change` from `[data-ui-datatable]` with `{ values }`; `ui:schedule-change` from `[data-ui-schedule]` with `{ eventId, slot }`; `ui:virtual-list-range` from `[data-ui-virtual-list]` with `{ startIndex, endIndex }`; `ui:tag-remove` from tag groups with `{ value }`; `ui:editor-command` from rich text editors with `{ command }`; `ui:theme-change` from theme builders with `{ hue, tokens }`; `ui:theme-export` from theme builders with `{ css }`; `ui:validate`, `ui:invalid`, and `ui:valid` from `[data-ui-form]` for native Constraint Validation API enhancement; document-level `ui:toast`, `ui:toast-update`, and `ui:toast-dismiss` for runtime toasts; `ui:toast-action` from `[data-ui-toast]` when an action button is pressed.\n- Add `data-ui-form` to forms that should reflect `required`, `pattern`, `min`, `max`, `type`, and custom `setCustomValidity()` state into Field error slots on blur and submit. Use `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or `data-error-custom` to override browser validation messages.\n- Toasts use `ToastViewport` for explicit `placement` and `maxCount` configuration. With `UIPrimitives` or registered Web Components, use `window.LumenToast.create(detail)`, `.update(id, detail)`, and `.dismiss(id?)`, import `LumenToast` from `@santi020k/lumen-elements`, or dispatch the matching document events. Runtime toast `detail` supports `title`, `description`, `variant`, `duration`, `placement`, `max`, and `action`.\n\n## Content flow\n\n- Reuse content-flow-header, content-flow-settings, content-flow-list and content-flow-actions recipes; lumen_get_recipe returns complete framework source.\n- Container gutters grow from group to section spacing. Override --ui-container-gutter at the product boundary instead of adding nested page padding.\n- Prose and Typography trim their outer child margins and separate headings from preceding text. Card allows interactive overflow; AspectRatio owns media clipping.\n\n- Read docs/content-flow.md. Stack/Grid own sibling gaps; surfaces own padding; Field owns control feedback spacing. Do not add child margins for a relationship already controlled by a parent gap.\n- Use Stack/Grid gap=\"related\" (8px), \"group\" (16px, default), or \"section\" (32px). Size gaps match the canonical scale: xs=4, sm=8, md=12, lg=16, xl=24, 2xl=32, 3xl=48. CSS variables use rem. Retrieve spacing and spacingRoles through lumen_get_tokens.\n- Use CardHeader, CardContent and CardFooter. Card owns their gap; missing, hidden and empty parts leave no spacing. Use a nested Stack inside the body and allow footer actions to wrap. Card density is compact, comfortable (default), or spacious; it does not reduce control touch targets.\n- Keep Prose/Typography text rhythm separate from sibling gaps. Check long translated text, enlarged text, validation, optional parts and phone/desktop wrapping in both themes. Use one outer Container for page gutters and one border owner per visible edge.\n\nBefore using MCP contracts, call lumen_check_compatibility with resolved installed Lumen versions.\nIf versions differ, use installed public types and package README or a matching MCP package.\nNever silently upgrade an application. For an explicit v4 upgrade, read lumen_get_migration and\npreview lumen migrate v4 --dry-run --json; review dependency updates and application workarounds.\n\n- [React form workflows](docs/powerful-forms.md): optional dependent validation, edit tracking, steps, repeatable rows, and async checks.\n\n- WorldMap: reusable dotted/solid SVG map in Astro, React and Elements. Import country geometry explicitly from @santi020k/lumen-core/world-map-data; see docs/world-map.md.\n",
|
|
61670
|
+
"rules": "# Lumen UI\n\nLumen is a multi-framework primitive UI system with Astro as the reference implementation.\n\nUse these files first when generating, reviewing, or migrating apps that use Lumen:\n\n- README.md: public overview and package strategy.\n- docs/ai-usage.md: AI-focused setup, package selection, and component selection.\n- https://lumen.santi020k.com/guides/build-ui-with-ai: tested Astro and React adoption example with a reusable prompt.\n- docs/ai-efficiency.md: reproducible AI token measurement, quality checks, and limits on efficiency claims.\n- skills/lumen-ui/SKILL.md: portable Agent Skill for building, reviewing, and theming Lumen interfaces.\n- docs/feature-roadmap.md: competitor-informed gaps and implementation priorities.\n- registry/lumen.registry.json: installable component, recipe, and documentation manifest.\n- packages/core/src/components.ts: authoritative shared component catalog.\n- packages/astro/components: reference component markup and prop shapes.\n- packages/astro/styles/lumen.css: standalone CSS contract for all frameworks.\n- @santi020k/lumen-mcp: Model Context Protocol server exposing snapshot provenance, structured component discovery, framework-aware usage/source, recipes, tolerant search, tokens, rules, and lumen:// resources for AI agents.\n\nInstall the portable Lumen Agent Skill:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nDefault usage:\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\nimport '@santi020k/lumen-astro/styles.css'\n---\n\n<UIPrimitives />\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\nRules for agents:\n\n- When Lumen MCP is connected, read `lumen://meta`, `lumen://diagnostics`, and `lumen://rules`, pass the target framework to `lumen_search`, and follow the framework behavior returned by `lumen_get_component`. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades when the client supports cached context.\n- Prefer Astro unless the user explicitly targets React or Web Components.\n- In React, use the visual components with behavior hooks for Dialog, Popover, DropdownMenu, Tabs, Select, Toast, and Tooltip instead of mounting the Astro runtime.\n- In Web Components, registered `lumen-*` elements include behavior parity for Dialog, Popover, DropdownMenu, Tabs, Select, Toast, and Tooltip, including ARIA state, keyboard paths, Escape/outside dismissal, focus handling, native select form participation, and the shared toast controller.\n- For React report filters, use `DateRangeInput` (`value`, `onValueChange`, localized `labels`, bounds and presets) or inline `DateRangeCalendar`. These React-specific compositions are documented in packages/react/README.md and the reporting example at /docs/web/reporting.\n- React record tables can opt into DataTableView from @santi020k/lumen-react/components/data-table-view for search, filters, visibility, density, pagination and controlled sorting. Read docs/data-table-views.md; applications retain cells, data fetching and domain actions.\n- Keep server-paginated DataTable rows in server order with `sortMode=\"manual\"`, controlled `sort` and `onSortChange`. Apply sorting before pagination in the application backend. Use Dialog dismissal policies for pending workflows.\n- Chart X identities must be unique. Use `xLabel` for abbreviated axes, `formatCategory` for full details, and explicit null gaps for missing observations. Read docs/data-visualization.md.\n- Use ImageComparison for before/after media with matching dimensions, alternative text and a visible localized control label. See docs/image-comparison.md for adapter contracts.\n- Read docs/migrating-to-lumen.md before upgrading to v4, especially date-trigger IDs, loading buttons, hidden semantics, copy feedback and native initializer changes.\n- Do not require Tailwind configuration for Lumen components.\n- Give each outer edge one border and radius owner. Flush images inside rounded frames use Image radius=\"none\"; Image defaults to radius=\"lg\". Use one divider at shared edges, account for padding on inset curves, and check phone/desktop corners and keyboard focus in both themes. Keep artwork clipping scoped so it does not cut off focus rings or overlays. See docs/ai-usage.md for styling rules.\n- Use public tokens: canvas, surface, surface-muted, surface-strong, line, ink, ink-soft, ink-muted, brand, brand-solid, brand-soft, on-brand, accent, success, warning, danger, on-danger.\n- Default to `Select` for web product option lists and `Combobox` for searchable choices. Use `NativeSelect` only for an explicit browser or platform picker requirement. Keep native fallbacks and verify the enhanced list with the matching framework behavior enabled.\n- Mount `UIPrimitives` once for interactive Astro primitives.\n- Use `glass` for glass surfaces. The obsolete overlay `surface=\"glass\"` alias is removed.\n- Treat schedule, data collection, rich text, theme builder, and advanced fields as supported surfaces with progressively enhanced behavior.\n- Use `lumen list`, `lumen show <component>`, and `lumen add <component>` for individually addressable registry components; component names also resolve from kebab-case aliases such as `data-table`.\n- Runtime CustomEvents used by `UIPrimitives`: `ui:data-table-selection-change` from `[data-ui-datatable]` with `{ values }`; `ui:schedule-change` from `[data-ui-schedule]` with `{ eventId, slot }`; `ui:virtual-list-range` from `[data-ui-virtual-list]` with `{ startIndex, endIndex }`; `ui:tag-remove` from tag groups with `{ value }`; `ui:editor-command` from rich text editors with `{ command }`; `ui:theme-change` from theme builders with `{ hue, tokens }`; `ui:theme-export` from theme builders with `{ css }`; `ui:validate`, `ui:invalid`, and `ui:valid` from `[data-ui-form]` for native Constraint Validation API enhancement; document-level `ui:toast`, `ui:toast-update`, and `ui:toast-dismiss` for runtime toasts; `ui:toast-action` from `[data-ui-toast]` when an action button is pressed.\n- Add `data-ui-form` to forms that should reflect `required`, `pattern`, `min`, `max`, `type`, and custom `setCustomValidity()` state into Field error slots on blur and submit. Use `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or `data-error-custom` to override browser validation messages.\n- Toasts use `ToastViewport` for explicit `placement` and `maxCount` configuration. With `UIPrimitives` or registered Web Components, use `window.LumenToast.create(detail)`, `.update(id, detail)`, and `.dismiss(id?)`, import `LumenToast` from `@santi020k/lumen-elements`, or dispatch the matching document events. Runtime toast `detail` supports `title`, `description`, `variant`, `duration`, `placement`, `max`, and `action`.\n\n## Content flow\n\n- Reuse content-flow-header, content-flow-settings, content-flow-list and content-flow-actions recipes; lumen_get_recipe returns complete framework source.\n- Container gutters grow from group to section spacing. Override --ui-container-gutter at the product boundary instead of adding nested page padding.\n- Prose and Typography trim their outer child margins and separate headings from preceding text. Card allows interactive overflow; AspectRatio owns media clipping.\n\n- Read docs/content-flow.md. Stack/Grid own sibling gaps; surfaces own padding; Field owns control feedback spacing. Do not add child margins for a relationship already controlled by a parent gap.\n- Use Stack/Grid gap=\"related\" (8px), \"group\" (16px, default), or \"section\" (32px). Size gaps match the canonical scale: xs=4, sm=8, md=12, lg=16, xl=24, 2xl=32, 3xl=48. CSS variables use rem. Retrieve spacing and spacingRoles through lumen_get_tokens.\n- Use CardHeader, CardContent and CardFooter. Card owns their gap; missing, hidden and empty parts leave no spacing. Use a nested Stack inside the body and allow footer actions to wrap. Card density is compact, comfortable (default), or spacious; it does not reduce control touch targets.\n- Keep Prose/Typography text rhythm separate from sibling gaps. Check long translated text, enlarged text, validation, optional parts and phone/desktop wrapping in both themes. Use one outer Container for page gutters and one border owner per visible edge.\n\nBefore using MCP contracts, call lumen_check_compatibility with resolved installed Lumen versions.\nIf versions differ, use installed public types and package README or a matching MCP package.\nNever silently upgrade an application. For an explicit v4 upgrade, read lumen_get_migration and\npreview lumen migrate v4 --dry-run --json; review dependency updates and application workarounds.\n\n- [React form workflows](docs/powerful-forms.md): optional dependent validation, edit tracking, steps, repeatable rows, and async checks.\n\n- WorldMap: reusable dotted/solid SVG map in Astro, React and Elements. Import country geometry explicitly from @santi020k/lumen-core/world-map-data; see docs/world-map.md.\n",
|
|
61539
61671
|
"tokens": {
|
|
61540
61672
|
"presets": {
|
|
61541
61673
|
"attribute": "data-lumen-preset",
|