@astryxdesign/cli 0.1.1 → 0.1.2-canary.2149fa3

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 (113) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/docs/theme.doc.mjs +2 -2
  3. package/docs/working-with-ai.doc.mjs +6 -6
  4. package/package.json +19 -8
  5. package/src/api/discover.mjs +78 -26
  6. package/src/api/index.mjs +1 -0
  7. package/src/api/layout.mjs +301 -0
  8. package/src/api/layout.test.mjs +238 -0
  9. package/src/api/template.mjs +191 -50
  10. package/src/api/template.test.mjs +2 -0
  11. package/src/api/theme-add.mjs +182 -0
  12. package/src/codemods/__tests__/registry.test.mjs +1 -0
  13. package/src/codemods/registry.mjs +1 -0
  14. package/src/codemods/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +120 -0
  15. package/src/codemods/transforms/v0.1.2/index.mjs +19 -0
  16. package/src/codemods/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +136 -0
  17. package/src/commands/build-theme.import-path.test.mjs +22 -2
  18. package/src/commands/build-theme.mjs +185 -16
  19. package/src/commands/gap-report.mjs +17 -9
  20. package/src/commands/gap-report.test.mjs +21 -16
  21. package/src/commands/init.mjs +34 -8
  22. package/src/commands/init.next-steps.test.mjs +46 -0
  23. package/src/commands/layout.mjs +139 -0
  24. package/src/commands/swizzle.mjs +51 -23
  25. package/src/commands/upgrade.mjs +2 -71
  26. package/src/config.mjs +31 -0
  27. package/src/config.test.mjs +24 -0
  28. package/src/index.mjs +6 -0
  29. package/src/lib/config-schema.mjs +119 -0
  30. package/src/lib/config.mjs +34 -7
  31. package/src/lib/config.test.mjs +51 -2
  32. package/src/lib/error-codes.mjs +11 -0
  33. package/src/lib/integrations.mjs +155 -0
  34. package/src/lib/integrations.test.mjs +154 -0
  35. package/src/lib/levenshtein.mjs +29 -0
  36. package/src/lib/manifest.mjs +10 -0
  37. package/src/lib/package-scanner.mjs +31 -7
  38. package/src/lib/string-utils.mjs +5 -14
  39. package/src/lib/xle/browser.d.ts +100 -0
  40. package/src/lib/xle/browser.mjs +120 -0
  41. package/src/lib/xle/expand.mjs +622 -0
  42. package/src/lib/xle/parse.mjs +581 -0
  43. package/src/lib/xle/print.mjs +174 -0
  44. package/src/lib/xle/registry-core.mjs +170 -0
  45. package/src/lib/xle/registry.mjs +237 -0
  46. package/src/lib/xle/splice.mjs +137 -0
  47. package/src/lib/xle/validate.mjs +356 -0
  48. package/src/lib/xle/xle.test.mjs +333 -0
  49. package/src/types/config.d.ts +99 -0
  50. package/src/utils/github.mjs +12 -27
  51. package/templates/blocks/components/Card/ClickableCardWithNestedButton.doc.mjs +1 -1
  52. package/templates/blocks/components/Card/SelectableCardMulti.doc.mjs +1 -1
  53. package/templates/blocks/components/ChatComposerDrawer/ChatComposerDrawerWithProgress.tsx +1 -1
  54. package/templates/blocks/components/ChatDictationButton/ChatDictationButtonShowcase.tsx +3 -3
  55. package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyShowcase.doc.mjs +15 -0
  56. package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyShowcase.tsx +26 -0
  57. package/templates/blocks/components/DateInput/DateInputDateRange.doc.mjs +2 -2
  58. package/templates/blocks/components/FileInput/FileInputShowcase.tsx +4 -2
  59. package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.doc.mjs +14 -0
  60. package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.tsx +43 -0
  61. package/templates/blocks/components/MultiSelector/MultiSelectorShowcase.tsx +6 -2
  62. package/templates/blocks/components/NumberInput/NumberInputShowcase.tsx +6 -2
  63. package/templates/blocks/components/Outline/OutlineShowcase.doc.mjs +15 -0
  64. package/templates/blocks/components/Outline/OutlineShowcase.tsx +22 -0
  65. package/templates/blocks/components/RadioList/RadioListShowcase.tsx +8 -1
  66. package/templates/blocks/components/SegmentedControl/SegmentedControlShowcase.tsx +3 -1
  67. package/templates/blocks/components/Selector/SelectorShowcase.tsx +4 -1
  68. package/templates/blocks/components/Slider/SliderShowcase.tsx +10 -1
  69. package/templates/blocks/components/Tab/TabShowcase.tsx +3 -1
  70. package/templates/blocks/components/TabList/TabListShowcase.tsx +3 -1
  71. package/templates/blocks/components/TabMenu/TabMenuShowcase.tsx +3 -1
  72. package/templates/blocks/components/Table/ColumnResizeHookUsage.doc.mjs +14 -0
  73. package/templates/blocks/components/Table/ColumnResizeHookUsage.tsx +59 -0
  74. package/templates/blocks/components/Table/StickyColumnsHookUsage.doc.mjs +14 -0
  75. package/templates/blocks/components/Table/StickyColumnsHookUsage.tsx +104 -0
  76. package/templates/blocks/components/Text/TextColors.tsx +20 -5
  77. package/templates/blocks/components/TextArea/TextAreaShowcase.tsx +4 -2
  78. package/templates/blocks/components/TextInput/TextInputShowcase.tsx +4 -2
  79. package/templates/blocks/components/Thumbnail/ThumbnailDisabled.tsx +11 -6
  80. package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +43 -7
  81. package/templates/blocks/components/Thumbnail/ThumbnailRemovable.tsx +40 -3
  82. package/templates/blocks/components/Thumbnail/ThumbnailShowcase.tsx +6 -5
  83. package/templates/blocks/components/Thumbnail/ThumbnailStates.tsx +11 -6
  84. package/templates/blocks/components/Timestamp/TimestampColors.tsx +2 -2
  85. package/templates/blocks/components/ToggleButton/ToggleButtonGroup.doc.mjs +1 -1
  86. package/templates/blocks/components/Tokenizer/TokenizerShowcase.tsx +8 -6
  87. package/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +2 -2
  88. package/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +23 -14
  89. package/templates/blocks/components/Typeahead/TypeaheadShowcase.tsx +6 -2
  90. package/templates/pages/shell-nav/page.tsx +321 -0
  91. package/templates/pages/shell-nav/template.doc.mjs +12 -0
  92. package/templates/pages/shell-side-nav/page.tsx +242 -0
  93. package/templates/pages/shell-side-nav/template.doc.mjs +12 -0
  94. package/templates/pages/shell-top-nav/page.tsx +224 -0
  95. package/templates/pages/shell-top-nav/template.doc.mjs +12 -0
  96. package/templates/pages/theme-showcase/page.tsx +8 -19
  97. package/templates/themes/butter/butterTheme.ts +916 -0
  98. package/templates/themes/butter/icons.tsx +77 -0
  99. package/templates/themes/chocolate/chocolateTheme.ts +230 -0
  100. package/templates/themes/chocolate/icons.tsx +77 -0
  101. package/templates/themes/gothic/gothicTheme.ts +657 -0
  102. package/templates/themes/gothic/icons.tsx +77 -0
  103. package/templates/themes/manifest.json +90 -0
  104. package/templates/themes/matcha/icons.tsx +67 -0
  105. package/templates/themes/matcha/matchaTheme.ts +247 -0
  106. package/templates/themes/neutral/icons.tsx +77 -0
  107. package/templates/themes/neutral/neutralTheme.ts +603 -0
  108. package/templates/themes/stone/icons.tsx +77 -0
  109. package/templates/themes/stone/stoneTheme.ts +652 -0
  110. package/templates/themes/y2k/icons.tsx +67 -0
  111. package/templates/themes/y2k/y2kTheme.ts +617 -0
  112. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.doc.mjs +0 -14
  113. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.tsx +0 -57
@@ -7,10 +7,47 @@ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
7
7
  import {Stack} from '@astryxdesign/core/Layout';
8
8
  import {Text} from '@astryxdesign/core/Text';
9
9
 
10
+ // Demo imagery is inlined as same-origin data URIs so the example is
11
+ // self-contained. Thumbnail's remove-button overlay uses useImageMode, which
12
+ // FETCHES the image to sample pixels (APCA) for contrast — a cross-origin CDN
13
+ // URL without CORS headers cannot be fetched/sampled, so contrast detection
14
+ // fails silently in hosted previews. These scenes are shared verbatim across
15
+ // the Thumbnail examples and span a luminance ramp (night → snow) so the
16
+ // overlay's contrast adaptation is visible.
17
+ const NIGHT_FOREST =
18
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX18+H08uDy8d/y8N/v7t/m5NXl49Tk4tPc3NDX1cnW1cnW1cjW1MjV1MjV1MfR0cXIyLzEw7m8vLC3t68wM0QvM0MvM0IvMkIuMkEtMEErLz8iJjkhJTggJTcgJDcfJDYfIzYeIjUcITMbHzIWHiwYGzIXGjEWGjAWGTAVGS8VGC8UGC4UGC0UFy4TFy0TFywTFi0SFiwRFSsQFSkNFR4QFCoPFCkPFCgPEykOEygOEigNEicNEScMESYIDw8ECgr+gzZIAAABqElEQVR42uWQ61aCQBRGD5GylNBMKS9FZgNdNE0cyUvB+79VM8MoF9Fgplau2j/8nO8c9hqAD0ngDwjeJYH1lrfBWoCtYDboNu/Zv7qQYNap1bpCN1gxFh1dNe5WAsCScWqoWttbChAKvHZZ0W6XQoIFpW8ooFmLCG2Rl1BgaZAU5Ac8yg0RlC9nngCRQDFuhQRzinWmkCuYk3lxQoFrlskVqlcTMcEFvYIKoFbNfmEFuIypWaGGkt66tqZuEbjAHZl6SQVF1WqNvpDAHVktXTvR9JbgDZii1+tZI7cYMJXkCASvksB4L5VxHmAsibzgRRIYbjgfCgFDSeBZkh8W1HMIniSBR0mOQPCQA3RgBk4M5DhNZwdEcbKgLXy5iDiZg5gApRYbqTql2FZgMxAKeEWSNUFYx3s2SfU28HPWItrpySSwE2cEdqrYtxgmxgHGScFmHrAGk8SxczIxZnNC1McFOGshShyb4yxB9sI3CXDGnH0MLsD7F5LvvptEgH12Zg1Pn2WuHsjvrwt8308t+nwxT38sgiAE8/RTeagH+vx/F8SeF0Ja8Ak14ia/LgmpZQAAAABJRU5ErkJggg==';
19
+ const MISTY_VALLEY =
20
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEWPnaqJmKSIlaKHlaGGlaKGk6CEkp6Ckp+Cj5uAj51/jZp6i5h8ipd7iZZ4hpR1g5FzhpdzhJN0g5FuhZdsgpVqgZRpgJNpf5JmfpFufYxufYtlfJBpe4tke49neo1ieo5kd4tndoZidopheY1geIxfd4tmdYVddYpcdIlbc4hgcoNebn5ZcodYcYZXb4VVboRUbYNTbIJaantWaXxRa4FQaYBPaH9NZ35UZXZQY3dMZn1LZXxKZHxMYHRKXnIoOEh3WzsBAAABeElEQVR42u3T11oCQQyG4R9FEREFqVJEgh17xbZ7/3cl29gpmQGeeOh7OF+Ss8G3EL6E8CmEdyG8CeFVCC9CeBTCgxDuhXAnhFsh3AjhSgiXQrgQwtlayHrB6TqIyHzCREMTH4rpbxip5nnkRintEUNF0oc8yqnP6Oey3ueQSnlHN6P0ro0Mi4BOSu8dA1mygnbC7G0NMdKEVszuLQXx4oZmhO3NDLlEEY05R280PDGbgD0RaBeI/Bdg7weBMkDLwF4PtBOcwH0gCJZf0DPYdd8Jo8K177hgZbjW+RN2hWffusBVnMR+XE5yfMRx5MPtOOOI0Xd+9ot/rTOiVrtepuabwcDwNFgP6przSH0dqKiOUhUfvaKcO8yVncyM0sK+psSyK4qpPUvRwlUUYrusgoaviG27YMFXNz38I0nc8PNPAFtC2BFClRFWV4cDU5g4WEE0hJ4q1PU8sgmMF0LOmKN2TBOh21RjRsxms3C5WYwrCIX+D/zBgV8HCFv4gVKf1AAAAABJRU5ErkJggg==';
21
+ const GOLDEN_SUNSET =
22
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAkFBMVEX/+uv/7sj6tFr3sVn1r1nzrVjxq1jvqVftp1frpVbpo1bnoVXln1XjnVThm1TfmVPdl1PblVLZk1LXkVLVj1HTjVHRi1DPiVDNh0/LhU/Jg07HgU7Ff03DfU3Be0y/eUy9d0u7dUu5c0u2cEq0bkqybEmwakmuaEisZkiqZEeoYkemYEakXkaiXEVgLjQ8HCjaJlMkAAABI0lEQVR42u3Mx3qDMBSE0Z/03nsvdhxjcnn/twsYjISR5MUkO5+V9N2ZYUPEpogtEdsidkTsitgTsS/iQMShiCMRxyJORJyKOBNxLuJCxKWIKxHXIm5E3Iq4E3Ev4kHEo4gnEc8iXkS8inhLymrJBO8J2UIiw0dc5sRDfEbVRWr1o/pbMMWox9yz6zcLIzMbBTD2VaHu7fr1gjXGA3w5baj5+H0w6x09TDpdaP7rDZhn0sd3y89Y9fcHbPnoYdroZ2w6dQNmg6OHfG4QyrsBC8gdZpVQxtqF4M1mjepFURThTLNgMYsePxaX2WqY6D8GSm2grK2slbGBciHZ9hOE2smNpTuxenhieCdeH06E7qTqvYnImWTbbUSPlKL1wHrgTwZ+AWq+3H5MpRkwAAAAAElFTkSuQmCC';
23
+ const SNOWY_PEAKS =
24
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX////6/P/4+//g7vjf7fbf7fTe7fje7fTe7PTd7Pjc7Pfd7PTc6/Xc6/Pb6/fb6/Xb6vTa6/fa6/Xa6vfZ6vfZ6vba6vTZ6fXY6vfY6vbY6ffY6fXX6fbX6fTW6fbX6PXW6PbW6PTV6PbV6PXU6PbV5/XU5/bU5/TU5vXT5/bS5/XT5vTS5vXS5vTR5vXR5fXQ5fXP5fXP5PXO5PXO5PTN5PTN4/TM4/TL4/TM4vTL4vTK4vTO3OqqvtKbrsFcfGgMVnjaAAAC7klEQVR42u2Ta3eaQBCGp0kbTbBBMWDMoiCWEOsFtV4SdcP//1edYQUWwSye9kNOT58POVlm3mdmUWG/3+9et5vVYj6bjoMgGE9n88Vqs33d7SsBcX67WmI+EKBhudpWNcBu94bzlyGO9weEj0uES9zhbVcFSPMYdxzmOKi4xACbNa0/DgY91rGIDusNgjFdY72tAKzXq0U4pTyGm80m/iXDNFys1psqApwf4nwc32x+J9DRYbhDuMRrqIF5vD+zKK0R5LBYfIvFcqUEBZPRsNex9IaW0NCtTm84mqDglxIIQ3z/uEBDqydoDVrBH4d4CyUwowUsA+fXa4I67mBYtAJeQglM4jeg0wLfBLSCLt5COFcC4xffYVZ8gVSABos5/sskVAOjYNjvWrqWxGOFplvd/jD4OZkpgSAYiA1yAtpgGIzGEyW0wYCZeqOeGer1hm6yQUXBD8+1uySofU2okaBre97zSA34nuswk74GqYC+CCZzXM9/VgO+77o2M+LPUcTpUzSYXS3/DB7m4ztomrhEjb7LeAPbdb0KgOe5fTIY6Y8B51O+Xynvgeu6js2Yaep6Q6DrpsmY7biVABthXdPEFfRj3sBTl9nl8JNzLMANSGEahm5QGuNn8/zEAE8xD0g7hrfbdHgqhxO5J/B4BEMtBOv43+M5YkHuCTxk0HwuDGfgvNAAbZljQ/sMnEOhAVoSPKFVCuYBThvgXiKdcF8GlwRZB+Tz8IEhLhca4C6F8yusH0TDXYFjPjEkj+E2gecm8NsT+IkgaQBVQ6F80gA3Ai4uANkl+I0ElYlDjNwAhXyp4Zg/Cg4FAS+dwAsLiPq7PAGuEc5LJ3B+LUjLSf2Q1aGk4V1uyJVTQdYAqgZ5v7T+nu0IuXwmkAwf1yE3IJuQjZDLkiBpAK5o4BUEV6UNh5J8SR1y9VzDoZgvqasFV2frByFQNCgFX/6Q/4J/SxDJTy84/DVBFEmlSw6fRhBFUumiw2cRRJFUuuzwG+qew9oBo+n5AAAAAElFTkSuQmCC';
25
+
10
26
  const IMAGES = [
11
- {id: 1, src: 'https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-1.png', alt: 'Dark cityscape at night', label: 'dark-city.jpg'},
12
- {id: 2, src: 'https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png', alt: 'Bright snowy landscape', label: 'snow.jpg'},
13
- {id: 3, src: 'https://lookaside.facebook.com/assets/astryx/moody-home-vertical-1.png', alt: 'Warm sunset over mountains', label: 'sunset.jpg'},
27
+ {
28
+ id: 1,
29
+ src: NIGHT_FOREST,
30
+ alt: 'Forest at night under a crescent moon',
31
+ label: 'forest-night.jpg',
32
+ },
33
+ {
34
+ id: 2,
35
+ src: MISTY_VALLEY,
36
+ alt: 'Misty mountain valley',
37
+ label: 'misty-valley.jpg',
38
+ },
39
+ {
40
+ id: 3,
41
+ src: GOLDEN_SUNSET,
42
+ alt: 'Golden sunset over mountains',
43
+ label: 'golden-sunset.jpg',
44
+ },
45
+ {
46
+ id: 4,
47
+ src: SNOWY_PEAKS,
48
+ alt: 'Snowy mountain peaks',
49
+ label: 'snowy-peaks.jpg',
50
+ },
14
51
  ];
15
52
 
16
53
  export default function ThumbnailRemovable() {
@@ -4,12 +4,13 @@
4
4
 
5
5
  import {Thumbnail} from '@astryxdesign/core/Thumbnail';
6
6
 
7
+ // Inline same-origin data URI so the example is self-contained — see
8
+ // ThumbnailRemovable for why image-backed Thumbnails avoid cross-origin URLs.
9
+ const SNOWY_PEAKS =
10
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX////6/P/4+//g7vjf7fbf7fTe7fje7fTe7PTd7Pjc7Pfd7PTc6/Xc6/Pb6/fb6/Xb6vTa6/fa6/Xa6vfZ6vfZ6vba6vTZ6fXY6vfY6vbY6ffY6fXX6fbX6fTW6fbX6PXW6PbW6PTV6PbV6PXU6PbV5/XU5/bU5/TU5vXT5/bS5/XT5vTS5vXS5vTR5vXR5fXQ5fXP5fXP5PXO5PXO5PTN5PTN4/TM4/TL4/TM4vTL4vTK4vTO3OqqvtKbrsFcfGgMVnjaAAAC7klEQVR42u2Ta3eaQBCGp0kbTbBBMWDMoiCWEOsFtV4SdcP//1edYQUWwSye9kNOT58POVlm3mdmUWG/3+9et5vVYj6bjoMgGE9n88Vqs33d7SsBcX67WmI+EKBhudpWNcBu94bzlyGO9weEj0uES9zhbVcFSPMYdxzmOKi4xACbNa0/DgY91rGIDusNgjFdY72tAKzXq0U4pTyGm80m/iXDNFys1psqApwf4nwc32x+J9DRYbhDuMRrqIF5vD+zKK0R5LBYfIvFcqUEBZPRsNex9IaW0NCtTm84mqDglxIIQ3z/uEBDqydoDVrBH4d4CyUwowUsA+fXa4I67mBYtAJeQglM4jeg0wLfBLSCLt5COFcC4xffYVZ8gVSABos5/sskVAOjYNjvWrqWxGOFplvd/jD4OZkpgSAYiA1yAtpgGIzGEyW0wYCZeqOeGer1hm6yQUXBD8+1uySofU2okaBre97zSA34nuswk74GqYC+CCZzXM9/VgO+77o2M+LPUcTpUzSYXS3/DB7m4ztomrhEjb7LeAPbdb0KgOe5fTIY6Y8B51O+Xynvgeu6js2Yaep6Q6DrpsmY7biVABthXdPEFfRj3sBTl9nl8JNzLMANSGEahm5QGuNn8/zEAE8xD0g7hrfbdHgqhxO5J/B4BEMtBOv43+M5YkHuCTxk0HwuDGfgvNAAbZljQ/sMnEOhAVoSPKFVCuYBThvgXiKdcF8GlwRZB+Tz8IEhLhca4C6F8yusH0TDXYFjPjEkj+E2gecm8NsT+IkgaQBVQ6F80gA3Ai4uANkl+I0ElYlDjNwAhXyp4Zg/Cg4FAS+dwAsLiPq7PAGuEc5LJ3B+LUjLSf2Q1aGk4V1uyJVTQdYAqgZ5v7T+nu0IuXwmkAwf1yE3IJuQjZDLkiBpAK5o4BUEV6UNh5J8SR1y9VzDoZgvqasFV2frByFQNCgFX/6Q/4J/SxDJTy84/DVBFEmlSw6fRhBFUumiw2cRRJFUuuzwG+qew9oBo+n5AAAAAElFTkSuQmCC';
11
+
7
12
  export default function ThumbnailShowcase() {
8
13
  return (
9
- <Thumbnail
10
- src="https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png"
11
- alt="Sample image"
12
- label="photo.jpg"
13
- />
14
+ <Thumbnail src={SNOWY_PEAKS} alt="Snowy mountain peaks" label="snowy-peaks.jpg" />
14
15
  );
15
16
  }
@@ -6,6 +6,11 @@ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
6
6
  import {Stack} from '@astryxdesign/core/Layout';
7
7
  import {Text} from '@astryxdesign/core/Text';
8
8
 
9
+ // Inline same-origin data URI so the example is self-contained — see
10
+ // ThumbnailRemovable for why image-backed Thumbnails avoid cross-origin URLs.
11
+ const MISTY_VALLEY =
12
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEWPnaqJmKSIlaKHlaGGlaKGk6CEkp6Ckp+Cj5uAj51/jZp6i5h8ipd7iZZ4hpR1g5FzhpdzhJN0g5FuhZdsgpVqgZRpgJNpf5JmfpFufYxufYtlfJBpe4tke49neo1ieo5kd4tndoZidopheY1geIxfd4tmdYVddYpcdIlbc4hgcoNebn5ZcodYcYZXb4VVboRUbYNTbIJaantWaXxRa4FQaYBPaH9NZ35UZXZQY3dMZn1LZXxKZHxMYHRKXnIoOEh3WzsBAAABeElEQVR42u3T11oCQQyG4R9FEREFqVJEgh17xbZ7/3cl29gpmQGeeOh7OF+Ss8G3EL6E8CmEdyG8CeFVCC9CeBTCgxDuhXAnhFsh3AjhSgiXQrgQwtlayHrB6TqIyHzCREMTH4rpbxip5nnkRintEUNF0oc8yqnP6Oey3ueQSnlHN6P0ro0Mi4BOSu8dA1mygnbC7G0NMdKEVszuLQXx4oZmhO3NDLlEEY05R280PDGbgD0RaBeI/Bdg7weBMkDLwF4PtBOcwH0gCJZf0DPYdd8Jo8K177hgZbjW+RN2hWffusBVnMR+XE5yfMRx5MPtOOOI0Xd+9ot/rTOiVrtepuabwcDwNFgP6przSH0dqKiOUhUfvaKcO8yVncyM0sK+psSyK4qpPUvRwlUUYrusgoaviG27YMFXNz38I0nc8PNPAFtC2BFClRFWV4cDU5g4WEE0hJ4q1PU8sgmMF0LOmKN2TBOh21RjRsxms3C5WYwrCIX+D/zBgV8HCFv4gVKf1AAAAABJRU5ErkJggg==';
13
+
9
14
  export default function ThumbnailStates() {
10
15
  return (
11
16
  <Stack direction="vertical" gap={4}>
@@ -28,10 +33,10 @@ export default function ThumbnailStates() {
28
33
  </Stack>
29
34
  <Stack direction="vertical" gap={1} hAlign="center">
30
35
  <Thumbnail
31
- src="https://lookaside.facebook.com/assets/astryx/moody-home-vertical-1.png"
32
- alt="Mountain landscape"
36
+ src={MISTY_VALLEY}
37
+ alt="Misty mountain valley"
33
38
  isLoading
34
- label="landscape.jpg"
39
+ label="misty-valley.jpg"
35
40
  />
36
41
  <Text type="supporting" color="secondary">
37
42
  Uploading
@@ -39,9 +44,9 @@ export default function ThumbnailStates() {
39
44
  </Stack>
40
45
  <Stack direction="vertical" gap={1} hAlign="center">
41
46
  <Thumbnail
42
- src="https://lookaside.facebook.com/assets/astryx/moody-home-vertical-1.png"
43
- alt="Mountain landscape"
44
- label="landscape.jpg"
47
+ src={MISTY_VALLEY}
48
+ alt="Misty mountain valley"
49
+ label="misty-valley.jpg"
45
50
  />
46
51
  <Text type="supporting" color="secondary">
47
52
  Loaded
@@ -31,9 +31,9 @@ export default function TimestampColors() {
31
31
  </Stack>
32
32
  <Stack direction="vertical" gap={1}>
33
33
  <Text type="supporting" color="secondary">
34
- Active
34
+ Accent
35
35
  </Text>
36
- <Timestamp value={DATE} format="date_time" color="active" />
36
+ <Timestamp value={DATE} format="date_time" color="accent" />
37
37
  </Stack>
38
38
  </Stack>
39
39
  );
@@ -3,7 +3,7 @@
3
3
  /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
4
  export const doc = {
5
5
  type: 'block',
6
- exampleFor: 'ToggleButton',
6
+ exampleFor: 'ToggleButtonGroup',
7
7
  name: 'ToggleButton — Group',
8
8
  displayName: 'ToggleButton — Group',
9
9
  description: 'Toggle button groups in single-select and multi-select modes. Single selection acts as a view mode switcher; multiple selection forms a formatting toolbar.',
@@ -2,8 +2,9 @@
2
2
 
3
3
  'use client';
4
4
 
5
+ import {useState} from 'react';
5
6
  import {Tokenizer} from '@astryxdesign/core/Tokenizer';
6
- import type {SearchSource} from '@astryxdesign/core/Typeahead';
7
+ import type {SearchableItem, SearchSource} from '@astryxdesign/core/Typeahead';
7
8
 
8
9
  const source: SearchSource = {
9
10
  search: () => [],
@@ -11,16 +12,17 @@ const source: SearchSource = {
11
12
  };
12
13
 
13
14
  export default function TokenizerShowcase() {
15
+ const [value, setValue] = useState<SearchableItem[]>([
16
+ {id: '1', label: 'Design'},
17
+ {id: '2', label: 'Engineering'},
18
+ ]);
14
19
  return (
15
20
  <Tokenizer
16
21
  label="Tags"
17
22
  placeholder="Search..."
18
23
  searchSource={source}
19
- value={[
20
- {id: '1', label: 'Design'},
21
- {id: '2', label: 'Engineering'},
22
- ]}
23
- onChange={() => {}}
24
+ value={value}
25
+ onChange={setValue}
24
26
  style={{width: 400}}
25
27
  />
26
28
  );
@@ -7,8 +7,8 @@ export const doc = {
7
7
  name: 'Toolbar — Table Filter',
8
8
  displayName: 'Toolbar — Table Filter',
9
9
  description:
10
- 'A compact toolbar with a search input, filter buttons, and an overflow menu. Use above a data table to let users search, filter, and access view options.',
10
+ 'A compact toolbar with a search input, Status and Priority filter selectors, and an overflow menu. Use above a data table to let users search, filter, and access view options.',
11
11
  isReady: true,
12
12
  aspectRatio: 16 / 9,
13
- componentsUsed: ['Toolbar', 'Button', 'Icon', 'TextInput', 'MoreMenu', 'Layout', 'Table'],
13
+ componentsUsed: ['Toolbar', 'Selector', 'TextInput', 'MoreMenu', 'Layout', 'Table'],
14
14
  };
@@ -2,19 +2,20 @@
2
2
 
3
3
  'use client';
4
4
 
5
+ import {useState} from 'react';
5
6
  import {Toolbar} from '@astryxdesign/core/Toolbar';
6
- import {Button} from '@astryxdesign/core/Button';
7
- import {Icon} from '@astryxdesign/core/Icon';
7
+ import {Selector} from '@astryxdesign/core/Selector';
8
8
  import {TextInput} from '@astryxdesign/core/TextInput';
9
9
  import {MoreMenu} from '@astryxdesign/core/MoreMenu';
10
10
  import {Stack} from '@astryxdesign/core/Layout';
11
11
  import {Table} from '@astryxdesign/core/Table';
12
- import {
13
- MagnifyingGlassIcon,
14
- ChevronDownIcon,
15
- } from '@heroicons/react/24/outline';
12
+ import {MagnifyingGlassIcon} from '@heroicons/react/24/outline';
16
13
 
17
14
  export default function ToolbarTableFilter() {
15
+ const [search, setSearch] = useState('');
16
+ const [status, setStatus] = useState<string | null>(null);
17
+ const [priority, setPriority] = useState<string | null>(null);
18
+
18
19
  return (
19
20
  <Stack direction="vertical" style={{width: '100%'}}>
20
21
  <Toolbar
@@ -27,19 +28,27 @@ export default function ToolbarTableFilter() {
27
28
  label="Search"
28
29
  isLabelHidden
29
30
  placeholder="Search..."
30
- value=""
31
- onChange={() => {}}
31
+ value={search}
32
+ onChange={setSearch}
32
33
  startIcon={MagnifyingGlassIcon}
33
34
  />
34
- <Button
35
+ <Selector
35
36
  label="Status"
36
- variant="secondary"
37
- endContent={<Icon icon={ChevronDownIcon} />}
37
+ isLabelHidden
38
+ placeholder="Status"
39
+ hasClear
40
+ value={status}
41
+ onChange={setStatus}
42
+ options={['Open', 'In progress', 'Done']}
38
43
  />
39
- <Button
44
+ <Selector
40
45
  label="Priority"
41
- variant="secondary"
42
- endContent={<Icon icon={ChevronDownIcon} />}
46
+ isLabelHidden
47
+ placeholder="Priority"
48
+ hasClear
49
+ value={priority}
50
+ onChange={setPriority}
51
+ options={['High', 'Medium', 'Low']}
43
52
  />
44
53
  </>
45
54
  }
@@ -1,5 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ 'use client';
4
+
5
+ import {useState} from 'react';
3
6
  import {Typeahead} from '@astryxdesign/core/Typeahead';
4
7
  import type {SearchableItem, SearchSource} from '@astryxdesign/core/Typeahead';
5
8
 
@@ -21,14 +24,15 @@ const fruitSource: SearchSource = {
21
24
  };
22
25
 
23
26
  export default function TypeaheadShowcase() {
27
+ const [value, setValue] = useState<SearchableItem | null>(null);
24
28
  return (
25
29
  <div style={{width: 320}}>
26
30
  <Typeahead
27
31
  label="Fruit"
28
32
  placeholder="Search fruits..."
29
33
  searchSource={fruitSource}
30
- value={null}
31
- onChange={() => {}}
34
+ value={value}
35
+ onChange={setValue}
32
36
  />
33
37
  </div>
34
38
  );
@@ -0,0 +1,321 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Fragment, useState, useMemo, useEffect} from 'react';
6
+ import {AppShell} from '@astryxdesign/core/AppShell';
7
+ import {Layout, LayoutHeader, LayoutContent} from '@astryxdesign/core/Layout';
8
+ import {TopNav} from '@astryxdesign/core/TopNav';
9
+ import {DropdownMenu, DropdownMenuItem} from '@astryxdesign/core/DropdownMenu';
10
+ import {CommandPalette} from '@astryxdesign/core/CommandPalette';
11
+ import {createStaticSource} from '@astryxdesign/core/Typeahead';
12
+ import {Divider} from '@astryxdesign/core/Divider';
13
+ import {Kbd} from '@astryxdesign/core/Kbd';
14
+ import {SideNav} from '@astryxdesign/core/SideNav';
15
+ import {TreeList} from '@astryxdesign/core/TreeList';
16
+ import type {TreeListItemData} from '@astryxdesign/core/TreeList';
17
+ import {Icon} from '@astryxdesign/core/Icon';
18
+ import {Text} from '@astryxdesign/core/Text';
19
+ import {IconButton} from '@astryxdesign/core/IconButton';
20
+ import {Button} from '@astryxdesign/core/Button';
21
+ import {TextInput} from '@astryxdesign/core/TextInput';
22
+ import {Card} from '@astryxdesign/core/Card';
23
+ import {Stack, VStack, HStack} from '@astryxdesign/core/Stack';
24
+ import {
25
+ PlayIcon,
26
+ MagnifyingGlassIcon,
27
+ FolderIcon,
28
+ DocumentTextIcon,
29
+ } from '@heroicons/react/24/outline';
30
+
31
+ const noop = () => {};
32
+
33
+ const folder = (
34
+ id: string,
35
+ children: TreeListItemData[],
36
+ isExpanded = true,
37
+ ): TreeListItemData => ({
38
+ id,
39
+ label: <Text maxLines={1}>{id}</Text>,
40
+ startContent: <Icon icon={FolderIcon} size="xsm" />,
41
+ isExpanded,
42
+ children,
43
+ });
44
+
45
+ const file = (id: string, isSelected = false): TreeListItemData => ({
46
+ id,
47
+ label: <Text maxLines={1}>{id}</Text>,
48
+ startContent: <Icon icon={DocumentTextIcon} size="xsm" />,
49
+ isSelected,
50
+ });
51
+
52
+ const FILE_TREE: TreeListItemData[] = [
53
+ folder('src', [
54
+ folder('components', [
55
+ file('AppShell.tsx', true),
56
+ file('TopNav.tsx'),
57
+ file('SideNav.tsx'),
58
+ ]),
59
+ folder('hooks', [file('useTheme.ts'), file('useResizable.ts')]),
60
+ file('index.tsx'),
61
+ file('App.tsx'),
62
+ ]),
63
+ folder('public', [file('favicon.ico'), file('robots.txt')], false),
64
+ file('package.json'),
65
+ file('tsconfig.json'),
66
+ file('README.md'),
67
+ ];
68
+
69
+ // Each menu is split into groups; groups are separated by a divider.
70
+ // `[label, shortcut]` — the shortcut renders as a single combined Kbd
71
+ // (e.g. ⌘N); an empty shortcut renders no Kbd.
72
+ type MenuEntry = [label: string, shortcut: string];
73
+
74
+ const MENU_WIDTH = 280;
75
+
76
+ const MENUS: {label: string; groups: MenuEntry[][]}[] = [
77
+ {
78
+ label: 'File',
79
+ groups: [
80
+ [
81
+ ['New File', '⌘N'],
82
+ ['New Window', '⇧⌘N'],
83
+ ],
84
+ [
85
+ ['Open...', '⌘O'],
86
+ ['Save', '⌘S'],
87
+ ['Save As...', '⇧⌘S'],
88
+ ],
89
+ [['Close Editor', '⌘W']],
90
+ ],
91
+ },
92
+ {
93
+ label: 'Edit',
94
+ groups: [
95
+ [
96
+ ['Undo', '⌘Z'],
97
+ ['Redo', '⇧⌘Z'],
98
+ ],
99
+ [
100
+ ['Cut', '⌘X'],
101
+ ['Copy', '⌘C'],
102
+ ['Paste', '⌘V'],
103
+ ],
104
+ [['Find', '⌘F']],
105
+ ],
106
+ },
107
+ {
108
+ label: 'View',
109
+ groups: [
110
+ [['Command Palette', '⇧⌘P']],
111
+ [
112
+ ['Explorer', '⇧⌘E'],
113
+ ['Search', '⇧⌘F'],
114
+ ],
115
+ [
116
+ ['Toggle Terminal', '⌃`'],
117
+ ['Zen Mode', '⌘K'],
118
+ ],
119
+ ],
120
+ },
121
+ {
122
+ label: 'Window',
123
+ groups: [
124
+ [
125
+ ['Minimize', '⌘M'],
126
+ ['Zoom', ''],
127
+ ],
128
+ [
129
+ ['Next Tab', '⌃⇥'],
130
+ ['Previous Tab', '⌃⇧⇥'],
131
+ ],
132
+ [['Bring All to Front', '']],
133
+ ],
134
+ },
135
+ {
136
+ label: 'Help',
137
+ groups: [
138
+ [
139
+ ['Documentation', ''],
140
+ ['Release Notes', ''],
141
+ ['Report Issue', ''],
142
+ ['About', ''],
143
+ ],
144
+ ],
145
+ },
146
+ ];
147
+
148
+ const CODE_LINES = [
149
+ '38%',
150
+ '62%',
151
+ '54%',
152
+ '0%',
153
+ '46%',
154
+ '70%',
155
+ '58%',
156
+ '34%',
157
+ '0%',
158
+ '50%',
159
+ '66%',
160
+ '42%',
161
+ '60%',
162
+ '28%',
163
+ ];
164
+
165
+ const EDITOR_TABS = ['AppShell.tsx', 'TopNav.tsx', 'theme.ts'];
166
+
167
+ const COMMANDS = [
168
+ {id: 'new-file', label: 'New File'},
169
+ {id: 'open-file', label: 'Open File…'},
170
+ {id: 'save-all', label: 'Save All'},
171
+ {id: 'find-in-files', label: 'Find in Files'},
172
+ {id: 'toggle-terminal', label: 'Toggle Terminal'},
173
+ {id: 'go-to-symbol', label: 'Go to Symbol…'},
174
+ {id: 'appshell', label: 'AppShell.tsx'},
175
+ {id: 'topnav', label: 'TopNav.tsx'},
176
+ {id: 'sidenav', label: 'SideNav.tsx'},
177
+ {id: 'use-theme', label: 'useTheme.ts'},
178
+ {id: 'theme', label: 'theme.ts'},
179
+ ];
180
+
181
+ export default function ShellNav() {
182
+ const [isPaletteOpen, setIsPaletteOpen] = useState(false);
183
+ const searchSource = useMemo(() => createStaticSource(COMMANDS), []);
184
+
185
+ useEffect(() => {
186
+ const handleKeyDown = (e: KeyboardEvent) => {
187
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
188
+ e.preventDefault();
189
+ setIsPaletteOpen(true);
190
+ }
191
+ };
192
+ window.addEventListener('keydown', handleKeyDown);
193
+ return () => window.removeEventListener('keydown', handleKeyDown);
194
+ }, []);
195
+
196
+ return (
197
+ <>
198
+ <AppShell
199
+ contentPadding={0}
200
+ topNav={
201
+ <TopNav
202
+ label="Astryx Studio menu bar"
203
+ startContent={
204
+ <>
205
+ {MENUS.map(menu => (
206
+ <DropdownMenu
207
+ key={menu.label}
208
+ button={{label: menu.label, variant: 'ghost', size: 'sm'}}
209
+ hasChevron={false}
210
+ menuWidth={MENU_WIDTH}>
211
+ {menu.groups.map((group, gi) => (
212
+ <Fragment key={gi}>
213
+ {gi > 0 && <Divider />}
214
+ {group.map(([label, shortcut]) => (
215
+ <DropdownMenuItem
216
+ key={label}
217
+ label={label}
218
+ onClick={noop}
219
+ endContent={
220
+ shortcut ? <Kbd keys={shortcut} /> : undefined
221
+ }
222
+ />
223
+ ))}
224
+ </Fragment>
225
+ ))}
226
+ </DropdownMenu>
227
+ ))}
228
+ </>
229
+ }
230
+ endContent={
231
+ <>
232
+ <Stack onClick={() => setIsPaletteOpen(true)}>
233
+ <TextInput
234
+ label="Search files and commands"
235
+ isLabelHidden
236
+ size="sm"
237
+ width={240}
238
+ startIcon={MagnifyingGlassIcon}
239
+ placeholder="Search files and commands…"
240
+ value=""
241
+ onChange={() => {}}
242
+ />
243
+ </Stack>
244
+ <IconButton
245
+ label="Run project"
246
+ tooltip="Run"
247
+ variant="ghost"
248
+ icon={<Icon icon={PlayIcon} size="sm" />}
249
+ />
250
+ <Button label="Share" variant="secondary" />
251
+ </>
252
+ }
253
+ />
254
+ }
255
+ sideNav={
256
+ <SideNav
257
+ resizable={{defaultWidth: 240, minWidth: 180, maxWidth: 400}}>
258
+ <TreeList items={FILE_TREE} density="compact" />
259
+ </SideNav>
260
+ }>
261
+ <Layout
262
+ height="fill"
263
+ header={
264
+ <LayoutHeader hasDivider padding={6}>
265
+ <HStack gap={2}>
266
+ {EDITOR_TABS.map(tab => (
267
+ <Card
268
+ key={tab}
269
+ variant="muted"
270
+ padding={0}
271
+ width={132}
272
+ height={36}
273
+ />
274
+ ))}
275
+ </HStack>
276
+ </LayoutHeader>
277
+ }
278
+ content={
279
+ <LayoutContent padding={6}>
280
+ <VStack gap={2}>
281
+ {CODE_LINES.map((width, i) =>
282
+ width === '0%' ? (
283
+ <Card
284
+ key={i}
285
+ variant="muted"
286
+ padding={0}
287
+ width={1}
288
+ height={14}
289
+ />
290
+ ) : (
291
+ <HStack key={i} gap={3} vAlign="center">
292
+ <Card
293
+ variant="muted"
294
+ padding={0}
295
+ width={20}
296
+ height={14}
297
+ />
298
+ <Card
299
+ variant="muted"
300
+ padding={0}
301
+ width={width}
302
+ height={14}
303
+ />
304
+ </HStack>
305
+ ),
306
+ )}
307
+ </VStack>
308
+ </LayoutContent>
309
+ }
310
+ />
311
+ </AppShell>
312
+ <CommandPalette
313
+ isOpen={isPaletteOpen}
314
+ onOpenChange={setIsPaletteOpen}
315
+ searchSource={searchSource}
316
+ label="Search files and commands"
317
+ onValueChange={() => setIsPaletteOpen(false)}
318
+ />
319
+ </>
320
+ );
321
+ }
@@ -0,0 +1,12 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'page',
6
+ name: 'Shell Nav',
7
+ displayName: 'Shell Nav',
8
+ description:
9
+ 'A top + side nav app shell with a File/Edit/View/Window/Help menu bar plus command-palette search, a resizable file-tree SideNav, and a static grey-card tabbed code editor.',
10
+ isReady: true,
11
+ category: 'Shell - Top Nav + Left Sidebar',
12
+ };