@adea-ai/ui 0.104.2 → 0.105.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +11 -0
  2. package/dist/components/layout/split-layout/geometry.js +1 -1
  3. package/dist/components/layout/split-layout/index.js +2 -1
  4. package/dist/components/layout/split-layout/model.d.ts +2 -20
  5. package/dist/components/layout/split-layout/model.d.ts.map +1 -1
  6. package/dist/components/layout/split-layout/model.js +2 -14
  7. package/dist/components/layout/split-layout/model.js.map +1 -1
  8. package/dist/components/layout/split-layout/split-layout.js +1 -1
  9. package/dist/components/layout/split-layout/tree.d.ts +21 -0
  10. package/dist/components/layout/split-layout/tree.d.ts.map +1 -0
  11. package/dist/components/layout/split-layout/tree.js +18 -0
  12. package/dist/components/layout/split-layout/tree.js.map +1 -0
  13. package/dist/components/ui/kbd/index.d.ts +1 -1
  14. package/dist/components/ui/kbd/index.d.ts.map +1 -1
  15. package/dist/components/ui/kbd/index.js +2 -2
  16. package/dist/components/ui/kbd/kbd.d.ts +40 -2
  17. package/dist/components/ui/kbd/kbd.d.ts.map +1 -1
  18. package/dist/components/ui/kbd/kbd.js +74 -7
  19. package/dist/components/ui/kbd/kbd.js.map +1 -1
  20. package/dist/index.js +4 -3
  21. package/dist/r/about-dialog.json +1 -1
  22. package/dist/r/accordion.json +1 -1
  23. package/dist/r/account-menu.json +1 -1
  24. package/dist/r/alert.json +1 -1
  25. package/dist/r/appearance-editor.json +2 -2
  26. package/dist/r/breadcrumb.json +1 -1
  27. package/dist/r/calendar.json +1 -1
  28. package/dist/r/carousel.json +1 -1
  29. package/dist/r/catalog-browser.json +1 -1
  30. package/dist/r/checkbox.json +1 -1
  31. package/dist/r/code-block.json +1 -1
  32. package/dist/r/combobox.json +1 -1
  33. package/dist/r/command.json +1 -1
  34. package/dist/r/context-menu.json +1 -1
  35. package/dist/r/conversation.json +1 -1
  36. package/dist/r/detail-panel.json +1 -1
  37. package/dist/r/dialog.json +1 -1
  38. package/dist/r/dropdown-menu.json +1 -1
  39. package/dist/r/empty.json +1 -1
  40. package/dist/r/floating-preview.json +1 -1
  41. package/dist/r/help-center.json +1 -1
  42. package/dist/r/lib.json +1 -1
  43. package/dist/r/menubar.json +1 -1
  44. package/dist/r/native-select.json +1 -1
  45. package/dist/r/navigation-menu.json +1 -1
  46. package/dist/r/pagination.json +1 -1
  47. package/dist/r/radio-group.json +1 -1
  48. package/dist/r/registry.json +47 -42
  49. package/dist/r/resizable.json +1 -1
  50. package/dist/r/scene-controls.json +1 -1
  51. package/dist/r/select.json +1 -1
  52. package/dist/r/sheet.json +1 -1
  53. package/dist/r/sidebar-nav.json +1 -1
  54. package/dist/r/spinner.json +1 -1
  55. package/dist/r/split-layout.json +6 -1
  56. package/dist/r/src/components/layout/split-layout/model.ts +11 -30
  57. package/dist/r/src/components/layout/split-layout/tree.ts +51 -0
  58. package/dist/r/src/components/ui/kbd/index.ts +10 -1
  59. package/dist/r/src/components/ui/kbd/kbd.tsx +77 -14
  60. package/dist/r/stat.json +1 -1
  61. package/dist/r/theming.json +2 -2
  62. package/dist/r/toast.json +1 -1
  63. package/dist/r/top-bar.json +1 -1
  64. package/dist/r/tree.json +1 -1
  65. package/dist/r/update-dialog.json +1 -1
  66. package/package.json +13 -7
  67. package/registry.json +47 -42
  68. package/src/components/layout/split-layout/model.ts +11 -30
  69. package/src/components/layout/split-layout/tree.ts +51 -0
  70. package/src/components/ui/kbd/index.ts +10 -1
  71. package/src/components/ui/kbd/kbd.tsx +77 -14
package/registry.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "description": "",
11
11
  "dependencies": [
12
12
  "@kobalte/core@^0.13.14",
13
- "lucide-solid@^1.48.0"
13
+ "lucide-solid@^1.51.0"
14
14
  ],
15
15
  "registryDependencies": [
16
16
  "lib"
@@ -34,7 +34,7 @@
34
34
  "title": "Alert",
35
35
  "description": "",
36
36
  "dependencies": [
37
- "lucide-solid@^1.48.0"
37
+ "lucide-solid@^1.51.0"
38
38
  ],
39
39
  "registryDependencies": [
40
40
  "lib"
@@ -175,7 +175,7 @@
175
175
  "title": "Breadcrumb",
176
176
  "description": "",
177
177
  "dependencies": [
178
- "lucide-solid@^1.48.0"
178
+ "lucide-solid@^1.51.0"
179
179
  ],
180
180
  "registryDependencies": [
181
181
  "lib"
@@ -247,7 +247,7 @@
247
247
  "description": "",
248
248
  "dependencies": [
249
249
  "@corvu/calendar@^0.1.2",
250
- "lucide-solid@^1.48.0"
250
+ "lucide-solid@^1.51.0"
251
251
  ],
252
252
  "registryDependencies": [
253
253
  "@adea-ai/ui/button",
@@ -297,7 +297,7 @@
297
297
  "dependencies": [
298
298
  "embla-carousel",
299
299
  "embla-carousel-solid",
300
- "lucide-solid@^1.48.0"
300
+ "lucide-solid@^1.51.0"
301
301
  ],
302
302
  "registryDependencies": [
303
303
  "@adea-ai/ui/button",
@@ -353,7 +353,7 @@
353
353
  "description": "",
354
354
  "dependencies": [
355
355
  "@kobalte/core@^0.13.14",
356
- "lucide-solid@^1.48.0"
356
+ "lucide-solid@^1.51.0"
357
357
  ],
358
358
  "registryDependencies": [
359
359
  "lib"
@@ -377,7 +377,7 @@
377
377
  "title": "Code Block",
378
378
  "description": "",
379
379
  "dependencies": [
380
- "lucide-solid@^1.48.0"
380
+ "lucide-solid@^1.51.0"
381
381
  ],
382
382
  "registryDependencies": [
383
383
  "@adea-ai/ui/button",
@@ -427,7 +427,7 @@
427
427
  "description": "",
428
428
  "dependencies": [
429
429
  "@kobalte/core@^0.13.14",
430
- "lucide-solid@^1.48.0"
430
+ "lucide-solid@^1.51.0"
431
431
  ],
432
432
  "registryDependencies": [
433
433
  "lib"
@@ -458,7 +458,7 @@
458
458
  "dependencies": [
459
459
  "@kobalte/core@^0.13.14",
460
460
  "cmdk-solid@^1.2.0",
461
- "lucide-solid@^1.48.0"
461
+ "lucide-solid@^1.51.0"
462
462
  ],
463
463
  "registryDependencies": [
464
464
  "@adea-ai/ui/dialog",
@@ -484,7 +484,7 @@
484
484
  "description": "",
485
485
  "dependencies": [
486
486
  "@kobalte/core@^0.13.14",
487
- "lucide-solid@^1.48.0"
487
+ "lucide-solid@^1.51.0"
488
488
  ],
489
489
  "registryDependencies": [
490
490
  "lib"
@@ -508,7 +508,7 @@
508
508
  "title": "Detail Panel",
509
509
  "description": "",
510
510
  "dependencies": [
511
- "lucide-solid@^1.48.0"
511
+ "lucide-solid@^1.51.0"
512
512
  ],
513
513
  "registryDependencies": [
514
514
  "@adea-ai/ui/button",
@@ -534,7 +534,7 @@
534
534
  "description": "",
535
535
  "dependencies": [
536
536
  "@kobalte/core@^0.13.14",
537
- "lucide-solid@^1.48.0"
537
+ "lucide-solid@^1.51.0"
538
538
  ],
539
539
  "registryDependencies": [
540
540
  "@adea-ai/ui/typography",
@@ -607,7 +607,7 @@
607
607
  "description": "",
608
608
  "dependencies": [
609
609
  "@kobalte/core@^0.13.14",
610
- "lucide-solid@^1.48.0"
610
+ "lucide-solid@^1.51.0"
611
611
  ],
612
612
  "registryDependencies": [
613
613
  "lib"
@@ -631,7 +631,7 @@
631
631
  "title": "Empty",
632
632
  "description": "",
633
633
  "dependencies": [
634
- "lucide-solid@^1.48.0"
634
+ "lucide-solid@^1.51.0"
635
635
  ],
636
636
  "registryDependencies": [
637
637
  "@adea-ai/ui/action-button",
@@ -886,7 +886,7 @@
886
886
  "description": "",
887
887
  "dependencies": [
888
888
  "@kobalte/core@^0.13.14",
889
- "lucide-solid@^1.48.0"
889
+ "lucide-solid@^1.51.0"
890
890
  ],
891
891
  "registryDependencies": [
892
892
  "lib"
@@ -933,7 +933,7 @@
933
933
  "title": "Native Select",
934
934
  "description": "A themed wrapper around the browser's native `<select>` element.",
935
935
  "dependencies": [
936
- "lucide-solid@^1.48.0"
936
+ "lucide-solid@^1.51.0"
937
937
  ],
938
938
  "registryDependencies": [
939
939
  "lib"
@@ -958,7 +958,7 @@
958
958
  "description": "",
959
959
  "dependencies": [
960
960
  "@kobalte/core@^0.13.14",
961
- "lucide-solid@^1.48.0"
961
+ "lucide-solid@^1.51.0"
962
962
  ],
963
963
  "registryDependencies": [
964
964
  "lib"
@@ -983,7 +983,7 @@
983
983
  "description": "",
984
984
  "dependencies": [
985
985
  "@kobalte/core@^0.13.14",
986
- "lucide-solid@^1.48.0"
986
+ "lucide-solid@^1.51.0"
987
987
  ],
988
988
  "registryDependencies": [
989
989
  "lib"
@@ -1056,7 +1056,7 @@
1056
1056
  "description": "",
1057
1057
  "dependencies": [
1058
1058
  "@kobalte/core@^0.13.14",
1059
- "lucide-solid@^1.48.0"
1059
+ "lucide-solid@^1.51.0"
1060
1060
  ],
1061
1061
  "registryDependencies": [
1062
1062
  "lib"
@@ -1081,7 +1081,7 @@
1081
1081
  "description": "",
1082
1082
  "dependencies": [
1083
1083
  "@corvu/resizable@^0.2.5",
1084
- "lucide-solid@^1.48.0"
1084
+ "lucide-solid@^1.51.0"
1085
1085
  ],
1086
1086
  "registryDependencies": [
1087
1087
  "lib"
@@ -1128,7 +1128,7 @@
1128
1128
  "description": "",
1129
1129
  "dependencies": [
1130
1130
  "@kobalte/core@^0.13.14",
1131
- "lucide-solid@^1.48.0"
1131
+ "lucide-solid@^1.51.0"
1132
1132
  ],
1133
1133
  "registryDependencies": [
1134
1134
  "lib"
@@ -1177,7 +1177,7 @@
1177
1177
  "description": "",
1178
1178
  "dependencies": [
1179
1179
  "@kobalte/core@^0.13.14",
1180
- "lucide-solid@^1.48.0"
1180
+ "lucide-solid@^1.51.0"
1181
1181
  ],
1182
1182
  "registryDependencies": [
1183
1183
  "@adea-ai/ui/dialog",
@@ -1249,7 +1249,7 @@
1249
1249
  "title": "Spinner",
1250
1250
  "description": "",
1251
1251
  "dependencies": [
1252
- "lucide-solid@^1.48.0"
1252
+ "lucide-solid@^1.51.0"
1253
1253
  ],
1254
1254
  "registryDependencies": [
1255
1255
  "lib"
@@ -1413,7 +1413,7 @@
1413
1413
  "description": "",
1414
1414
  "dependencies": [
1415
1415
  "@kobalte/core@^0.13.14",
1416
- "lucide-solid@^1.48.0"
1416
+ "lucide-solid@^1.51.0"
1417
1417
  ],
1418
1418
  "registryDependencies": [
1419
1419
  "lib"
@@ -1561,7 +1561,7 @@
1561
1561
  "title": "Floating Preview",
1562
1562
  "description": "",
1563
1563
  "dependencies": [
1564
- "lucide-solid@^1.48.0"
1564
+ "lucide-solid@^1.51.0"
1565
1565
  ],
1566
1566
  "registryDependencies": [
1567
1567
  "@adea-ai/ui/action-button",
@@ -1689,7 +1689,7 @@
1689
1689
  "dependencies": [
1690
1690
  "@corvu/resizable@^0.2.5",
1691
1691
  "@kobalte/core@^0.13.14",
1692
- "lucide-solid@^1.48.0"
1692
+ "lucide-solid@^1.51.0"
1693
1693
  ],
1694
1694
  "registryDependencies": [
1695
1695
  "lib"
@@ -1719,7 +1719,7 @@
1719
1719
  "description": "Issue-backed binary layout with stable opaque leaf owners; scoped persistence belongs to the host.",
1720
1720
  "dependencies": [
1721
1721
  "@corvu/resizable@^0.2.5",
1722
- "lucide-solid@^1.48.0"
1722
+ "lucide-solid@^1.51.0"
1723
1723
  ],
1724
1724
  "registryDependencies": [
1725
1725
  "@adea-ai/ui/button",
@@ -1750,6 +1750,11 @@
1750
1750
  "path": "src/components/layout/split-layout/split-layout.tsx",
1751
1751
  "type": "registry:ui",
1752
1752
  "target": "components/layout/split-layout/split-layout.tsx"
1753
+ },
1754
+ {
1755
+ "path": "src/components/layout/split-layout/tree.ts",
1756
+ "type": "registry:ui",
1757
+ "target": "components/layout/split-layout/tree.ts"
1753
1758
  }
1754
1759
  ]
1755
1760
  },
@@ -1781,7 +1786,7 @@
1781
1786
  "title": "Top Bar",
1782
1787
  "description": "",
1783
1788
  "dependencies": [
1784
- "lucide-solid@^1.48.0"
1789
+ "lucide-solid@^1.51.0"
1785
1790
  ],
1786
1791
  "registryDependencies": [
1787
1792
  "lib"
@@ -1827,7 +1832,7 @@
1827
1832
  "title": "About Dialog",
1828
1833
  "description": "Shared app identity and version support dialog.",
1829
1834
  "dependencies": [
1830
- "lucide-solid@^1.48.0"
1835
+ "lucide-solid@^1.51.0"
1831
1836
  ],
1832
1837
  "registryDependencies": [
1833
1838
  "@adea-ai/ui/action-button",
@@ -1853,7 +1858,7 @@
1853
1858
  "title": "Account Menu",
1854
1859
  "description": "",
1855
1860
  "dependencies": [
1856
- "lucide-solid@^1.48.0"
1861
+ "lucide-solid@^1.51.0"
1857
1862
  ],
1858
1863
  "registryDependencies": [
1859
1864
  "@adea-ai/ui/button",
@@ -1905,9 +1910,9 @@
1905
1910
  "title": "Appearance Editor",
1906
1911
  "description": "",
1907
1912
  "dependencies": [
1908
- "@adea-ai/themes@^0.9.5",
1913
+ "@adea-ai/themes@^0.9.6",
1909
1914
  "@kobalte/core@^0.13.14",
1910
- "lucide-solid@^1.48.0"
1915
+ "lucide-solid@^1.51.0"
1911
1916
  ],
1912
1917
  "registryDependencies": [
1913
1918
  "@adea-ai/ui/button",
@@ -1957,7 +1962,7 @@
1957
1962
  "description": "",
1958
1963
  "dependencies": [
1959
1964
  "class-variance-authority@^0.7.1",
1960
- "lucide-solid@^1.48.0"
1965
+ "lucide-solid@^1.51.0"
1961
1966
  ],
1962
1967
  "registryDependencies": [
1963
1968
  "@adea-ai/ui/badge",
@@ -2003,7 +2008,7 @@
2003
2008
  "title": "Help Center",
2004
2009
  "description": "Shared keyboard shortcuts and project resource page.",
2005
2010
  "dependencies": [
2006
- "lucide-solid@^1.48.0"
2011
+ "lucide-solid@^1.51.0"
2007
2012
  ],
2008
2013
  "registryDependencies": [
2009
2014
  "@adea-ai/ui/action-button",
@@ -2062,7 +2067,7 @@
2062
2067
  "title": "Scene Controls",
2063
2068
  "description": "Shared host-neutral scene movement, jump and zoom controls.",
2064
2069
  "dependencies": [
2065
- "lucide-solid@^1.48.0"
2070
+ "lucide-solid@^1.51.0"
2066
2071
  ],
2067
2072
  "registryDependencies": [
2068
2073
  "@adea-ai/ui/action-button"
@@ -2121,7 +2126,7 @@
2121
2126
  "title": "Stat",
2122
2127
  "description": "",
2123
2128
  "dependencies": [
2124
- "lucide-solid@^1.48.0"
2129
+ "lucide-solid@^1.51.0"
2125
2130
  ],
2126
2131
  "registryDependencies": [
2127
2132
  "@adea-ai/ui/typography",
@@ -2146,7 +2151,7 @@
2146
2151
  "title": "Tree",
2147
2152
  "description": "Keyboard-accessible tree rows with virtualization-aware focus handoff.",
2148
2153
  "dependencies": [
2149
- "lucide-solid@^1.48.0"
2154
+ "lucide-solid@^1.51.0"
2150
2155
  ],
2151
2156
  "registryDependencies": [
2152
2157
  "lib"
@@ -2175,7 +2180,7 @@
2175
2180
  "title": "Update Dialog",
2176
2181
  "description": "",
2177
2182
  "dependencies": [
2178
- "lucide-solid@^1.48.0"
2183
+ "lucide-solid@^1.51.0"
2179
2184
  ],
2180
2185
  "registryDependencies": [
2181
2186
  "@adea-ai/ui/action-button",
@@ -2229,7 +2234,7 @@
2229
2234
  "description": "",
2230
2235
  "dependencies": [
2231
2236
  "@kobalte/core@^0.13.14",
2232
- "lucide-solid@^1.48.0"
2237
+ "lucide-solid@^1.51.0"
2233
2238
  ],
2234
2239
  "registryDependencies": [
2235
2240
  "@adea-ai/ui/action-button",
@@ -2352,8 +2357,8 @@
2352
2357
  "title": "Theming",
2353
2358
  "description": "",
2354
2359
  "dependencies": [
2355
- "@adea-ai/themes@^0.9.5",
2356
- "lucide-solid@^1.48.0"
2360
+ "@adea-ai/themes@^0.9.6",
2361
+ "lucide-solid@^1.51.0"
2357
2362
  ],
2358
2363
  "registryDependencies": [
2359
2364
  "@adea-ai/ui/button",
@@ -2460,7 +2465,7 @@
2460
2465
  "title": "Class-name and token helpers",
2461
2466
  "description": "The private helpers the components import: the class-name merge, the variant recipes, the token manifest and the theme bridge. Installed automatically as a peer of any component that needs it.",
2462
2467
  "dependencies": [
2463
- "@adea-ai/themes@^0.9.5",
2468
+ "@adea-ai/themes@^0.9.6",
2464
2469
  "class-variance-authority@^0.7.1",
2465
2470
  "cn@^0.4.0"
2466
2471
  ],
@@ -15,21 +15,18 @@
15
15
  * See NOTICE and docs/research/dev-view-donor-audit.md.
16
16
  */
17
17
 
18
- /** A visual identity only. Extend this with host-owned payload; UI never interprets it. */
19
- export type SplitLayoutLeaf = Readonly<{ kind: 'leaf'; id: string }>
20
- export type SplitLayoutBranch<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
21
- kind: 'split'
22
- id: string
23
- direction: 'row' | 'column'
24
- ratio: number
25
- children: readonly [SplitLayoutNode<L>, SplitLayoutNode<L>]
26
- }>
27
- export type SplitLayoutNode<L extends SplitLayoutLeaf = SplitLayoutLeaf> = L | SplitLayoutBranch<L>
18
+ import {
19
+ MAX_LAYOUT_LEAVES,
20
+ MAX_LAYOUT_DEPTH,
21
+ MIN_SPLIT_RATIO,
22
+ MAX_SPLIT_RATIO,
23
+ listLeaves,
24
+ countLeaves,
25
+ layoutDepth,
26
+ } from './tree'
27
+ import type { SplitLayoutLeaf, SplitLayoutBranch, SplitLayoutNode } from './tree'
28
28
 
29
- export const MAX_LAYOUT_LEAVES = 8
30
- export const MAX_LAYOUT_DEPTH = 8
31
- export const MIN_SPLIT_RATIO = 0.1
32
- export const MAX_SPLIT_RATIO = 0.9
29
+ export * from './tree'
33
30
 
34
31
  export type SplitLayoutState<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
35
32
  center: SplitLayoutNode<L>
@@ -44,22 +41,6 @@ export type SplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readon
44
41
  splitId: string
45
42
  }>
46
43
 
47
- export function listLeaves<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): readonly L[] {
48
- return node.kind === 'leaf'
49
- ? [node]
50
- : [...listLeaves(node.children[0]), ...listLeaves(node.children[1])]
51
- }
52
-
53
- export function countLeaves<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): number {
54
- return node.kind === 'leaf' ? 1 : countLeaves(node.children[0]) + countLeaves(node.children[1])
55
- }
56
-
57
- export function layoutDepth<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): number {
58
- return node.kind === 'leaf'
59
- ? 1
60
- : 1 + Math.max(layoutDepth(node.children[0]), layoutDepth(node.children[1]))
61
- }
62
-
63
44
  function replaceLeaf<L extends SplitLayoutLeaf>(
64
45
  node: SplitLayoutNode<L>,
65
46
  leafId: string,
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright (c) 2026 Michael Yong
3
+ * Copyright (c) 2026 Muxy
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Portions of the binary split behavior are substantially translated from:
7
+ * - get-bb/bb apps/app/src/lib/split-layout/ops.ts (MIT), revision
8
+ * 52a9256373d4d36f9b60e9e2a7f333464091a2ac.
9
+ * - muxy-app/muxy Muxy/Models/Workspace/SplitNode.swift (MIT), revision
10
+ * 5c5be8697c57a2fe70cda97fdbaf7c912e2e31b6.
11
+ * Adapted first in Adea packages/dev-view/src/layout/operations.ts at
12
+ * 0322ab09ff5e9775bfddc0bf2d81e1df436dbef7. Extracted here with opaque
13
+ * host-owned leaves and an injected final-pane placeholder; immutable strict
14
+ * binary operations, accepted limits and undoable close are preserved.
15
+ * See NOTICE and docs/research/dev-view-donor-audit.md.
16
+ */
17
+
18
+ // Read-only tree shape, limits, and traversal form a small import boundary.
19
+ // Persistence decoders can use these without loading pane-editing operations.
20
+
21
+ /** A visual identity only. Extend this with host-owned payload; UI never interprets it. */
22
+ export type SplitLayoutLeaf = Readonly<{ kind: 'leaf'; id: string }>
23
+ export type SplitLayoutBranch<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
24
+ kind: 'split'
25
+ id: string
26
+ direction: 'row' | 'column'
27
+ ratio: number
28
+ children: readonly [SplitLayoutNode<L>, SplitLayoutNode<L>]
29
+ }>
30
+ export type SplitLayoutNode<L extends SplitLayoutLeaf = SplitLayoutLeaf> = L | SplitLayoutBranch<L>
31
+
32
+ export const MAX_LAYOUT_LEAVES = 8
33
+ export const MAX_LAYOUT_DEPTH = 8
34
+ export const MIN_SPLIT_RATIO = 0.1
35
+ export const MAX_SPLIT_RATIO = 0.9
36
+
37
+ export function listLeaves<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): readonly L[] {
38
+ return node.kind === 'leaf'
39
+ ? [node]
40
+ : [...listLeaves(node.children[0]), ...listLeaves(node.children[1])]
41
+ }
42
+
43
+ export function countLeaves<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): number {
44
+ return node.kind === 'leaf' ? 1 : countLeaves(node.children[0]) + countLeaves(node.children[1])
45
+ }
46
+
47
+ export function layoutDepth<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): number {
48
+ return node.kind === 'leaf'
49
+ ? 1
50
+ : 1 + Math.max(layoutDepth(node.children[0]), layoutDepth(node.children[1]))
51
+ }
@@ -1 +1,10 @@
1
- export { Kbd, KbdGroup } from './kbd'
1
+ export {
2
+ Kbd,
3
+ KbdChord,
4
+ KbdGroup,
5
+ kbdChordKeys,
6
+ kbdGroupVariants,
7
+ kbdVariants,
8
+ type KbdChordProps,
9
+ type KbdSize,
10
+ } from './kbd'
@@ -1,5 +1,6 @@
1
1
  import type { ComponentProps } from 'solid-js'
2
- import { splitProps } from 'solid-js'
2
+ import { For, splitProps } from 'solid-js'
3
+ import { cva, type VariantProps } from '#lib/variants'
3
4
  import { cn } from '#lib/utils'
4
5
 
5
6
  /**
@@ -9,32 +10,94 @@ import { cn } from '#lib/utils'
9
10
  * `<kbd>` so the semantics survive copy-paste, and the mono face is what makes
10
11
  * a shortcut visually distinct from the sentence around it.
11
12
  *
13
+ * `size="compact"` exists for the dense rows a default cap would outweigh — a
14
+ * rail search row, a sidebar shortcut hint. It drops a height rung, and it
15
+ * lightens the fill and weight with it because at that size a solid muted cap
16
+ * reads heavier than the row text it sits in. The default rendering is every
17
+ * cap that is not inside such a row, and it is untouched by the rung.
18
+ *
12
19
  * `KbdGroup` exists because a shortcut is a sequence: typing `⌘` then `K`
13
20
  * needs a gap between them that is a layout concern, not punctuation.
14
21
  */
15
- export function Kbd(props: ComponentProps<'kbd'>) {
16
- const [local, rest] = splitProps(props, ['class'])
22
+ export const kbdVariants = cva(
23
+ 'bg-muted text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-sm border border-border px-1 font-mono text-2xs font-medium select-none',
24
+ {
25
+ variants: {
26
+ size: {
27
+ default: '',
28
+ compact: 'h-4 min-w-4 bg-muted/40 px-0.5 font-normal leading-none',
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ size: 'default',
33
+ },
34
+ }
35
+ )
36
+
37
+ export type KbdSize = NonNullable<VariantProps<typeof kbdVariants>['size']>
38
+
39
+ export function Kbd(props: ComponentProps<'kbd'> & { size?: KbdSize }) {
40
+ const [local, rest] = splitProps(props, ['size', 'class'])
17
41
 
18
42
  return (
19
- <kbd
20
- data-slot="kbd"
21
- class={cn(
22
- 'bg-muted text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-sm border border-border px-1 font-mono text-2xs font-medium select-none',
23
- local.class
24
- )}
25
- {...rest}
26
- />
43
+ <kbd data-slot="kbd" class={cn(kbdVariants({ size: local.size }), local.class)} {...rest} />
27
44
  )
28
45
  }
29
46
 
30
- export function KbdGroup(props: ComponentProps<'span'>) {
31
- const [local, rest] = splitProps(props, ['class'])
47
+ export const kbdGroupVariants = cva('inline-flex items-center gap-1', {
48
+ variants: {
49
+ size: {
50
+ default: '',
51
+ /** The gap is what separates caps, so it tightens with them. */
52
+ compact: 'gap-0.5',
53
+ },
54
+ },
55
+ defaultVariants: {
56
+ size: 'default',
57
+ },
58
+ })
59
+
60
+ export function KbdGroup(props: ComponentProps<'span'> & { size?: KbdSize }) {
61
+ const [local, rest] = splitProps(props, ['size', 'class'])
32
62
 
33
63
  return (
34
64
  <span
35
65
  data-slot="kbd-group"
36
- class={cn('inline-flex items-center gap-1', local.class)}
66
+ class={cn(kbdGroupVariants({ size: local.size }), local.class)}
37
67
  {...rest}
38
68
  />
39
69
  )
40
70
  }
71
+
72
+ /**
73
+ * Split a written chord into one entry per key cap. Every character is a key:
74
+ * `⌘K` renders as `⌘` + `K`, and a longer chord like `⇧⌘P` stays three caps
75
+ * wide, which matches how the system draws chords in menus.
76
+ */
77
+ export function kbdChordKeys(keys: string): string[] {
78
+ return [...keys]
79
+ }
80
+
81
+ export type KbdChordProps = Omit<ComponentProps<'span'>, 'children'> & {
82
+ /** The chord as it is drawn, e.g. `"⌘K"` or `"⇧⌘P"`. One cap per character. */
83
+ keys: string
84
+ size?: KbdSize
85
+ }
86
+
87
+ /**
88
+ * KbdChord.
89
+ *
90
+ * A written chord drawn as caps, one per character. Decoration only: the
91
+ * composition renders `aria-hidden`, so the accessible name and the parseable
92
+ * `aria-keyshortcuts` stay on the control the chord belongs to. That is why
93
+ * there is no children — the chord says what was drawn, nothing more.
94
+ */
95
+ export function KbdChord(props: KbdChordProps) {
96
+ const [local, rest] = splitProps(props, ['keys', 'size', 'class'])
97
+
98
+ return (
99
+ <KbdGroup {...rest} size={local.size} class={local.class} aria-hidden="true">
100
+ <For each={kbdChordKeys(local.keys)}>{(key) => <Kbd size={local.size}>{key}</Kbd>}</For>
101
+ </KbdGroup>
102
+ )
103
+ }