@multiplatform.one/components 7.81.2 → 7.82.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/dist/cjs/appShell/AppShell.cjs +1 -9
  2. package/dist/cjs/appShell/AppShell.native.js +1 -9
  3. package/dist/cjs/appShell/AppShell.native.js.map +1 -1
  4. package/dist/cjs/appShell/BottomChromeClearance.cjs +41 -0
  5. package/dist/cjs/appShell/BottomChromeClearance.native.js +45 -0
  6. package/dist/cjs/appShell/BottomChromeClearance.native.js.map +1 -0
  7. package/dist/cjs/appShell/bottomChrome.cjs +1 -12
  8. package/dist/cjs/appShell/bottomChrome.native.js +1 -13
  9. package/dist/cjs/appShell/bottomChrome.native.js.map +1 -1
  10. package/dist/cjs/appShell/index.cjs +3 -1
  11. package/dist/cjs/appShell/index.native.js +2 -0
  12. package/dist/cjs/appShell/index.native.js.map +1 -1
  13. package/dist/cjs/code/CodeBlock/index.cjs +38 -31
  14. package/dist/cjs/code/CodeBlock/index.native.js +38 -31
  15. package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
  16. package/dist/cjs/layouts/EmptyState.cjs +9 -43
  17. package/dist/cjs/layouts/EmptyState.native.js +9 -45
  18. package/dist/cjs/layouts/EmptyState.native.js.map +1 -1
  19. package/dist/cjs/layouts/barTitleSpan.cjs +54 -0
  20. package/dist/cjs/layouts/barTitleSpan.native.js +78 -0
  21. package/dist/cjs/layouts/barTitleSpan.native.js.map +1 -0
  22. package/dist/cjs/layouts/page.cjs +134 -49
  23. package/dist/cjs/layouts/page.native.js +147 -53
  24. package/dist/cjs/layouts/page.native.js.map +1 -1
  25. package/dist/esm/Accordion/index.native.js.map +1 -1
  26. package/dist/esm/AnimateHeight/index.native.js.map +1 -1
  27. package/dist/esm/Avatar/getInitials.native.js.map +1 -1
  28. package/dist/esm/Avatar/index.native.js.map +1 -1
  29. package/dist/esm/Badge/index.native.js.map +1 -1
  30. package/dist/esm/Button.native.js.map +1 -1
  31. package/dist/esm/Carousel/index.native.js.map +1 -1
  32. package/dist/esm/CheckGlyph/MenuCheckColumn.native.js.map +1 -1
  33. package/dist/esm/CheckGlyph/index.native.js.map +1 -1
  34. package/dist/esm/Chip/index.native.js.map +1 -1
  35. package/dist/esm/Collapsible.native.js.map +1 -1
  36. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  37. package/dist/esm/CopyField/CopyMenuButton.native.js.map +1 -1
  38. package/dist/esm/CopyField/index.native.js.map +1 -1
  39. package/dist/esm/Dialog.native.js.map +1 -1
  40. package/dist/esm/Diff/diff.native.js.map +1 -1
  41. package/dist/esm/Diff/index.native.js.map +1 -1
  42. package/dist/esm/DotIndicator/index.native.js.map +1 -1
  43. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  44. package/dist/esm/DropdownMenu/menuSettle.native.js.map +1 -1
  45. package/dist/esm/ElapsedProgress/index.native.js.map +1 -1
  46. package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
  47. package/dist/esm/Heading.native.js.map +1 -1
  48. package/dist/esm/Kbd/index.native.js.map +1 -1
  49. package/dist/esm/Kbd/keys.native.js.map +1 -1
  50. package/dist/esm/Lightbox/MediaControl.native.js.map +1 -1
  51. package/dist/esm/Lightbox/index.gnome.native.js.map +1 -1
  52. package/dist/esm/Lightbox/shared.native.js.map +1 -1
  53. package/dist/esm/Lightbox/useLightbox.native.js.map +1 -1
  54. package/dist/esm/MachineLiteral/index.native.js.map +1 -1
  55. package/dist/esm/Material/index.native.js.map +1 -1
  56. package/dist/esm/Meter/index.native.js.map +1 -1
  57. package/dist/esm/Meter/meter.native.js.map +1 -1
  58. package/dist/esm/OverflowRow/index.native.js.map +1 -1
  59. package/dist/esm/Pagination.native.js.map +1 -1
  60. package/dist/esm/Popover.native.js.map +1 -1
  61. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  62. package/dist/esm/Quote/index.native.js.map +1 -1
  63. package/dist/esm/SchemeToggle/index.native.js.map +1 -1
  64. package/dist/esm/ScrollView/revealFocusedField.native.js.map +1 -1
  65. package/dist/esm/Separator/index.native.js.map +1 -1
  66. package/dist/esm/Sheet.native.js.map +1 -1
  67. package/dist/esm/SheetModal/SheetHeader.native.js.map +1 -1
  68. package/dist/esm/SheetModal/SheetNavigation.native.js.map +1 -1
  69. package/dist/esm/SheetModal/detents.native.js.map +1 -1
  70. package/dist/esm/SheetModal/useSheetFrameFocus.native.js.map +1 -1
  71. package/dist/esm/Skeleton/index.native.js.map +1 -1
  72. package/dist/esm/SkipLink/index.native.js.map +1 -1
  73. package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
  74. package/dist/esm/SortableList/index.native.js.map +1 -1
  75. package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
  76. package/dist/esm/SortableList/useUserOrder.native.js.map +1 -1
  77. package/dist/esm/Tabs/index.native.js.map +1 -1
  78. package/dist/esm/Text.native.js.map +1 -1
  79. package/dist/esm/ThemePresetPicker/index.native.js.map +1 -1
  80. package/dist/esm/Toast/index.native.js.map +1 -1
  81. package/dist/esm/Toast/store.native.js.map +1 -1
  82. package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
  83. package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
  84. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  85. package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
  86. package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
  87. package/dist/esm/Video/VideoFrame.native.js.map +1 -1
  88. package/dist/esm/Video/types.native.js.map +1 -1
  89. package/dist/esm/Wheel/index.native.js.map +1 -1
  90. package/dist/esm/Wizard/index.native.js.map +1 -1
  91. package/dist/esm/actions/ActionBar.native.js.map +1 -1
  92. package/dist/esm/actions/DangerSection.native.js.map +1 -1
  93. package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
  94. package/dist/esm/actions/HeaderActions.native.js.map +1 -1
  95. package/dist/esm/actions/StandardAction.native.js.map +1 -1
  96. package/dist/esm/actions/devWarn.native.js.map +1 -1
  97. package/dist/esm/actions/index.native.js.map +1 -1
  98. package/dist/esm/actions/severity.native.js.map +1 -1
  99. package/dist/esm/appShell/AppShell.mjs +2 -10
  100. package/dist/esm/appShell/AppShell.mjs.map +1 -1
  101. package/dist/esm/appShell/AppShell.native.js +2 -10
  102. package/dist/esm/appShell/AppShell.native.js.map +1 -1
  103. package/dist/esm/appShell/BottomChromeClearance.mjs +16 -0
  104. package/dist/esm/appShell/BottomChromeClearance.mjs.map +1 -0
  105. package/dist/esm/appShell/BottomChromeClearance.native.js +17 -0
  106. package/dist/esm/appShell/BottomChromeClearance.native.js.map +1 -0
  107. package/dist/esm/appShell/Dock.native.js.map +1 -1
  108. package/dist/esm/appShell/MpoTabHost.native.js.map +1 -1
  109. package/dist/esm/appShell/ScopeMenu.native.js.map +1 -1
  110. package/dist/esm/appShell/SettingsSplit.native.js.map +1 -1
  111. package/dist/esm/appShell/Springboard.native.js.map +1 -1
  112. package/dist/esm/appShell/SystemBar.native.js.map +1 -1
  113. package/dist/esm/appShell/TabBar.native.js.map +1 -1
  114. package/dist/esm/appShell/appTile.native.js.map +1 -1
  115. package/dist/esm/appShell/bottomChrome.mjs +3 -13
  116. package/dist/esm/appShell/bottomChrome.mjs.map +1 -1
  117. package/dist/esm/appShell/bottomChrome.native.js +3 -14
  118. package/dist/esm/appShell/bottomChrome.native.js.map +1 -1
  119. package/dist/esm/appShell/index.mjs +2 -1
  120. package/dist/esm/appShell/index.mjs.map +1 -1
  121. package/dist/esm/appShell/index.native.js +2 -1
  122. package/dist/esm/appShell/index.native.js.map +1 -1
  123. package/dist/esm/charts/Axis.native.js.map +1 -1
  124. package/dist/esm/charts/BarChart.native.js.map +1 -1
  125. package/dist/esm/charts/ChartDataView.native.js.map +1 -1
  126. package/dist/esm/charts/ChartSurface.native.js.map +1 -1
  127. package/dist/esm/charts/LineChart.native.js.map +1 -1
  128. package/dist/esm/charts/PieChart.native.js.map +1 -1
  129. package/dist/esm/charts/Sparkline.native.js.map +1 -1
  130. package/dist/esm/charts/accessibility.native.js.map +1 -1
  131. package/dist/esm/charts/chartHeight.native.js.map +1 -1
  132. package/dist/esm/charts/composeChart.native.js.map +1 -1
  133. package/dist/esm/charts/index.native.js.map +1 -1
  134. package/dist/esm/charts/math.native.js.map +1 -1
  135. package/dist/esm/charts/series.native.js.map +1 -1
  136. package/dist/esm/charts/svg.native.js.map +1 -1
  137. package/dist/esm/charts/types.native.js.map +1 -1
  138. package/dist/esm/charts/useChartTheme.native.js.map +1 -1
  139. package/dist/esm/code/Code/index.native.js.map +1 -1
  140. package/dist/esm/code/CodeBlock/index.mjs +39 -32
  141. package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
  142. package/dist/esm/code/CodeBlock/index.native.js +39 -32
  143. package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
  144. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  145. package/dist/esm/code/Highlighter/houseSyntaxTheme.native.js.map +1 -1
  146. package/dist/esm/code/Highlighter/index.native.js.map +1 -1
  147. package/dist/esm/code/Highlighter/namedGroupMatchAll.native.js.map +1 -1
  148. package/dist/esm/code/LogView.native.js.map +1 -1
  149. package/dist/esm/code/Pre/index.native.js.map +1 -1
  150. package/dist/esm/code/index.native.js.map +1 -1
  151. package/dist/esm/componentColors.native.js.map +1 -1
  152. package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
  153. package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
  154. package/dist/esm/feedback/copy.native.js.map +1 -1
  155. package/dist/esm/feedback/index.native.js.map +1 -1
  156. package/dist/esm/feedback/notify.native.js.map +1 -1
  157. package/dist/esm/feedback/route.native.js.map +1 -1
  158. package/dist/esm/feedback/store.native.js.map +1 -1
  159. package/dist/esm/formLayouts/FormLayouts.native.js.map +1 -1
  160. package/dist/esm/formLayouts/PreferencePanel.native.js.map +1 -1
  161. package/dist/esm/formLayouts/index.native.js.map +1 -1
  162. package/dist/esm/hooks/index.native.js.map +1 -1
  163. package/dist/esm/hooks/useClipboard.native.js.map +1 -1
  164. package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
  165. package/dist/esm/hooks/useHoldCue.native.js.map +1 -1
  166. package/dist/esm/icons/CheckRegular.native.js.map +1 -1
  167. package/dist/esm/icons/MinusRegular.native.js.map +1 -1
  168. package/dist/esm/icons/index.native.js.map +1 -1
  169. package/dist/esm/icons/lucide.native.js.map +1 -1
  170. package/dist/esm/icons/themed.native.js.map +1 -1
  171. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  172. package/dist/esm/images/Image/framing.native.js.map +1 -1
  173. package/dist/esm/images/Image/mediaRadius.native.js.map +1 -1
  174. package/dist/esm/images/Image/shared.native.js.map +1 -1
  175. package/dist/esm/images/Image/types.native.js.map +1 -1
  176. package/dist/esm/images/index.native.js.map +1 -1
  177. package/dist/esm/index.native.js.map +1 -1
  178. package/dist/esm/knobImmuneExports.native.js.map +1 -1
  179. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  180. package/dist/esm/layouts/Debug/index.native.js.map +1 -1
  181. package/dist/esm/layouts/EmptyState.mjs +9 -43
  182. package/dist/esm/layouts/EmptyState.mjs.map +1 -1
  183. package/dist/esm/layouts/EmptyState.native.js +9 -45
  184. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  185. package/dist/esm/layouts/IdentityPlate.native.js.map +1 -1
  186. package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
  187. package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
  188. package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
  189. package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
  190. package/dist/esm/layouts/StatusDot.native.js.map +1 -1
  191. package/dist/esm/layouts/StatusGlyphStrip.native.js.map +1 -1
  192. package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
  193. package/dist/esm/layouts/attachments.native.js.map +1 -1
  194. package/dist/esm/layouts/barTitleSpan.mjs +27 -0
  195. package/dist/esm/layouts/barTitleSpan.mjs.map +1 -0
  196. package/dist/esm/layouts/barTitleSpan.native.js +48 -0
  197. package/dist/esm/layouts/barTitleSpan.native.js.map +1 -0
  198. package/dist/esm/layouts/bottomAccessory.native.js.map +1 -1
  199. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  200. package/dist/esm/layouts/index.native.js.map +1 -1
  201. package/dist/esm/layouts/page.mjs +136 -51
  202. package/dist/esm/layouts/page.mjs.map +1 -1
  203. package/dist/esm/layouts/page.native.js +149 -55
  204. package/dist/esm/layouts/page.native.js.map +1 -1
  205. package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
  206. package/dist/esm/layouts/screenScroll.native.js.map +1 -1
  207. package/dist/esm/layouts/shellSidebarSlot.native.js.map +1 -1
  208. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  209. package/dist/esm/layouts/tags.native.js.map +1 -1
  210. package/dist/esm/layouts/timeline.native.js.map +1 -1
  211. package/dist/esm/layouts/toolbar.native.js.map +1 -1
  212. package/dist/esm/shared/actionScan.native.js.map +1 -1
  213. package/dist/esm/shared/i18n.native.js.map +1 -1
  214. package/dist/esm/shared/restoreRetries.native.js.map +1 -1
  215. package/dist/esm/shared/t.native.js.map +1 -1
  216. package/dist/esm/shared/textRidesText.native.js.map +1 -1
  217. package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
  218. package/dist/esm/surfaces.native.js.map +1 -1
  219. package/dist/esm/tamagui.native.js.map +1 -1
  220. package/dist/esm/testSetup.native.js.map +1 -1
  221. package/dist/esm/utils/setTinted.native.js.map +1 -1
  222. package/dist/esm/utils/unwrapText.native.js.map +1 -1
  223. package/dist/esm/views/Calendar.native.js.map +1 -1
  224. package/dist/esm/views/Dashboard.native.js.map +1 -1
  225. package/dist/esm/views/Gantt.native.js.map +1 -1
  226. package/dist/esm/views/GanttDateGrip.native.js.map +1 -1
  227. package/dist/esm/views/ImageGrid.native.js.map +1 -1
  228. package/dist/esm/views/List.native.js.map +1 -1
  229. package/dist/esm/views/ListGroup.native.js.map +1 -1
  230. package/dist/esm/views/ListRow.native.js.map +1 -1
  231. package/dist/esm/views/Matrix.native.js.map +1 -1
  232. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  233. package/dist/esm/views/ShortcutCard.native.js.map +1 -1
  234. package/dist/esm/views/TreeView.native.js.map +1 -1
  235. package/dist/esm/views/calendarMath.native.js.map +1 -1
  236. package/dist/esm/views/ganttIntervals.native.js.map +1 -1
  237. package/dist/esm/views/ganttMath.native.js.map +1 -1
  238. package/dist/esm/views/index.native.js.map +1 -1
  239. package/dist/esm/views/kanbanDnd.native.js.map +1 -1
  240. package/dist/esm/views/matrixMath.native.js.map +1 -1
  241. package/dist/esm/views/sameNameScope.native.js.map +1 -1
  242. package/dist/esm/views/treeViewDnd.native.js.map +1 -1
  243. package/dist/esm/views/treeViewLayout.native.js.map +1 -1
  244. package/dist/esm/views/treeViewMotion.native.js.map +1 -1
  245. package/dist/esm/views/treeViewReveal.native.js.map +1 -1
  246. package/dist/esm/views/usePageFlowLoadMore.native.js.map +1 -1
  247. package/dist/esm/views/widgetChrome.native.js.map +1 -1
  248. package/dist/jsx/appShell/AppShell.mjs +2 -10
  249. package/dist/jsx/appShell/AppShell.mjs.map +1 -1
  250. package/dist/jsx/appShell/AppShell.native.js +1 -9
  251. package/dist/jsx/appShell/AppShell.native.js.map +1 -1
  252. package/dist/jsx/appShell/BottomChromeClearance.mjs +16 -0
  253. package/dist/jsx/appShell/BottomChromeClearance.mjs.map +1 -0
  254. package/dist/jsx/appShell/BottomChromeClearance.native.js +45 -0
  255. package/dist/jsx/appShell/BottomChromeClearance.native.js.map +1 -0
  256. package/dist/jsx/appShell/bottomChrome.mjs +3 -13
  257. package/dist/jsx/appShell/bottomChrome.mjs.map +1 -1
  258. package/dist/jsx/appShell/bottomChrome.native.js +1 -13
  259. package/dist/jsx/appShell/bottomChrome.native.js.map +1 -1
  260. package/dist/jsx/appShell/index.mjs +2 -1
  261. package/dist/jsx/appShell/index.mjs.map +1 -1
  262. package/dist/jsx/appShell/index.native.js +2 -0
  263. package/dist/jsx/appShell/index.native.js.map +1 -1
  264. package/dist/jsx/code/CodeBlock/index.mjs +39 -32
  265. package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
  266. package/dist/jsx/code/CodeBlock/index.native.js +38 -31
  267. package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
  268. package/dist/jsx/layouts/EmptyState.mjs +9 -43
  269. package/dist/jsx/layouts/EmptyState.mjs.map +1 -1
  270. package/dist/jsx/layouts/EmptyState.native.js +9 -45
  271. package/dist/jsx/layouts/EmptyState.native.js.map +1 -1
  272. package/dist/jsx/layouts/barTitleSpan.mjs +27 -0
  273. package/dist/jsx/layouts/barTitleSpan.mjs.map +1 -0
  274. package/dist/jsx/layouts/barTitleSpan.native.js +78 -0
  275. package/dist/jsx/layouts/barTitleSpan.native.js.map +1 -0
  276. package/dist/jsx/layouts/page.mjs +136 -51
  277. package/dist/jsx/layouts/page.mjs.map +1 -1
  278. package/dist/jsx/layouts/page.native.js +147 -53
  279. package/dist/jsx/layouts/page.native.js.map +1 -1
  280. package/package.json +9 -9
  281. package/src/appShell/AppShell.spec.tsx +11 -7
  282. package/src/appShell/AppShell.tsx +2 -9
  283. package/src/appShell/BottomChromeClearance.spec.tsx +42 -0
  284. package/src/appShell/BottomChromeClearance.tsx +15 -0
  285. package/src/appShell/bottomChrome.ts +3 -24
  286. package/src/appShell/index.ts +1 -0
  287. package/src/code/CodeBlock/CodeBlock.copy.spec.tsx +13 -0
  288. package/src/code/CodeBlock/index.tsx +46 -30
  289. package/src/layouts/EmptyState.spec.tsx +2 -0
  290. package/src/layouts/EmptyState.tsx +6 -39
  291. package/src/layouts/PageHeader.stories.tsx +238 -38
  292. package/src/layouts/barTitleSpan.spec.ts +35 -0
  293. package/src/layouts/barTitleSpan.ts +44 -0
  294. package/src/layouts/glyphCentre.spec.tsx +218 -0
  295. package/src/layouts/navBar.spec.tsx +58 -2
  296. package/src/layouts/page.tsx +128 -45
  297. package/types/appShell/AppShell.d.ts.map +1 -1
  298. package/types/appShell/BottomChromeClearance.d.ts +10 -0
  299. package/types/appShell/BottomChromeClearance.d.ts.map +1 -0
  300. package/types/appShell/bottomChrome.d.ts +1 -11
  301. package/types/appShell/bottomChrome.d.ts.map +1 -1
  302. package/types/appShell/index.d.ts +1 -0
  303. package/types/appShell/index.d.ts.map +1 -1
  304. package/types/code/CodeBlock/index.d.ts.map +1 -1
  305. package/types/layouts/EmptyState.d.ts.map +1 -1
  306. package/types/layouts/barTitleSpan.d.ts +29 -0
  307. package/types/layouts/barTitleSpan.d.ts.map +1 -0
  308. package/types/layouts/page.d.ts +6 -1
  309. package/types/layouts/page.d.ts.map +1 -1
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { LinearGradient } from "@tamagui/linear-gradient";
10
- import { ClipboardIcon } from "@phosphor-icons/react";
10
+ import { CopyIcon } from "@phosphor-icons/react";
11
11
  import {
12
12
  MIN_PRESS_TARGET,
13
13
  useTouchSurface,
@@ -30,6 +30,7 @@ import { Code } from "../Code";
30
30
  import { HighlightedCode } from "../HighlightedCode";
31
31
  import { useShikiTokens } from "../Highlighter";
32
32
  import { Pre } from "../Pre";
33
+ import { componentColors } from "../../componentColors";
33
34
 
34
35
  /** Visible lines before the "show more" clamp (content constraint, LC-18). */
35
36
  const VISIBLE_LINES = 22;
@@ -131,7 +132,7 @@ export function CodeBlock({
131
132
  const capSquare = sizeRecipeForToken(String(codeSize), { touch }).height;
132
133
 
133
134
  return (
134
- <YStack position="relative" marginBottom="$4">
135
+ <YStack position="relative">
135
136
  <ErrorBoundary>
136
137
  <YStack
137
138
  ref={clampRef}
@@ -160,9 +161,18 @@ export function CodeBlock({
160
161
  <Spacer size="$4" />
161
162
  </LinearGradient>
162
163
  )}
163
- <Pre {...knobProps.borderRadius} padding={0} marginBottom={0}>
164
+ <Pre
165
+ {...knobProps.borderRadius}
166
+ padding={0}
167
+ marginBottom={0}
168
+ flexDirection="row"
169
+ backgroundColor={componentColors.code.background}
170
+ borderWidth={0}
171
+ data-code-surface=""
172
+ >
164
173
  <ScrollView
165
- style={{ width: "100%" }}
174
+ flex={1}
175
+ minWidth={0}
166
176
  contentContainerStyle={{ minWidth: "100%" }}
167
177
  horizontal
168
178
  showsHorizontalScrollIndicator={false}
@@ -185,33 +195,39 @@ export function CodeBlock({
185
195
  </HighlightedCode>
186
196
  </Code>
187
197
  </ScrollView>
188
- </Pre>
189
- {showCopy && (
190
- <XStack position="absolute" top={0} insetInlineEnd={0} zIndex={zIndex.localTop}>
191
- <Input.Button
192
- glyphRing
193
- size={codeSize}
194
- type="button"
195
- aria-label="Copy to clipboard"
196
- data-press-min={MIN_PRESS_TARGET}
197
- hitSlop={capSquare < MIN_PRESS_TARGET ? pressTargetHitSlop(capSquare) : undefined}
198
- onPress={activateCopy}
199
- onKeyDown={
200
- ((e: KeyboardEvent) => {
201
- if (e.key === "Enter" || e.key === " ") {
202
- e.preventDefault();
203
- e.stopPropagation();
204
- activateCopy();
205
- }
206
- }) as unknown as () => void
207
- }
198
+ {showCopy && (
199
+ <XStack
200
+ flexShrink={0}
201
+ alignSelf={lines > 1 ? "flex-start" : "center"}
202
+ zIndex={zIndex.localTop}
203
+ data-code-copy=""
208
204
  >
209
- <Input.Icon aria-hidden>
210
- <ClipboardIcon />
211
- </Input.Icon>
212
- </Input.Button>
213
- </XStack>
214
- )}
205
+ <Input.Button
206
+ glyphRing
207
+ size={codeSize}
208
+ color={componentColors.text.primary}
209
+ type="button"
210
+ aria-label="Copy to clipboard"
211
+ data-press-min={MIN_PRESS_TARGET}
212
+ hitSlop={capSquare < MIN_PRESS_TARGET ? pressTargetHitSlop(capSquare) : undefined}
213
+ onPress={activateCopy}
214
+ onKeyDown={
215
+ ((e: KeyboardEvent) => {
216
+ if (e.key === "Enter" || e.key === " ") {
217
+ e.preventDefault();
218
+ e.stopPropagation();
219
+ activateCopy();
220
+ }
221
+ }) as unknown as () => void
222
+ }
223
+ >
224
+ <Input.Icon aria-hidden color={componentColors.text.primary}>
225
+ <CopyIcon weight="bold" />
226
+ </Input.Icon>
227
+ </Input.Button>
228
+ </XStack>
229
+ )}
230
+ </Pre>
215
231
  </YStack>
216
232
  </ErrorBoundary>
217
233
  <NotifyRegion scope="field" region={region} size="$3" />
@@ -102,5 +102,7 @@ describe("EmptyState state anatomy (LC-171)", () => {
102
102
  expect(command.textContent).toContain("shc tenant create <name>");
103
103
  expect(screen.getByLabelText("Copy to clipboard")).toBeTruthy();
104
104
  expect(document.querySelectorAll("[data-empty-intent] p")).toHaveLength(0);
105
+ const surface = command.querySelector("[data-code-surface]");
106
+ expect(surface?.contains(screen.getByLabelText("Copy to clipboard"))).toBe(true);
105
107
  });
106
108
  });
@@ -10,8 +10,7 @@
10
10
  * not a second description. `intent="error"` is sober failure chrome
11
11
  * (DG-ST-01) — never for a successful empty dataset.
12
12
  */
13
- import { Button, Spinner } from "@multiplatform.one/forms";
14
- import { CopyIcon } from "@phosphor-icons/react";
13
+ import { Spinner } from "@multiplatform.one/forms";
15
14
  import {
16
15
  IconSlot,
17
16
  Intent,
@@ -26,7 +25,6 @@ import {
26
25
  H2,
27
26
  Paragraph,
28
27
  type SizeTokens,
29
- Text,
30
28
  View,
31
29
  XStack,
32
30
  YStack,
@@ -34,8 +32,7 @@ import {
34
32
  type YStackProps,
35
33
  } from "tamagui";
36
34
  import { componentColors, sectionHeading } from "../componentColors";
37
- import { NotifyRegion } from "../feedback/NotifyHost";
38
- import { useCopyFeedback } from "../feedback/copy";
35
+ import { CodeBlock } from "../code/CodeBlock";
39
36
 
40
37
  export interface EmptyStateProps extends Omit<YStackProps, "size"> {
41
38
  /** One short title: what is true now ("No Pokémon yet"), not an instruction. */
@@ -102,43 +99,13 @@ function StateSymbol({
102
99
  }
103
100
 
104
101
  /**
105
- * The refusal gate's one command: mono, one line, with copy. The confirm is
106
- * notify() and the glyph never flips (DG-FB-01 / LC-16).
102
+ * The refusal gate's one command, as a code block with its copy inside
103
+ * (LC-267).
107
104
  */
108
105
  function CommandLine({ command }: { command: string }) {
109
- const { knobProps } = useResolvedKnobs();
110
- const { region, copy } = useCopyFeedback();
111
106
  return (
112
- <YStack data-empty-command="" alignItems="center" {...readingWidthStyle()}>
113
- <XStack
114
- alignItems="center"
115
- maxWidth="100%"
116
- backgroundColor="$color3"
117
- paddingLeft="$3"
118
- paddingRight="$1"
119
- paddingVertical="$1"
120
- {...knobProps.containerRadius}
121
- {...knobProps.gapSm}
122
- >
123
- <Text
124
- {...knobProps.body}
125
- fontFamily="$mono"
126
- color={componentColors.text.primary}
127
- numberOfLines={1}
128
- flexShrink={1}
129
- >
130
- {command}
131
- </Text>
132
- <Button
133
- nested
134
- chromeless
135
- flexShrink={0}
136
- icon={<CopyIcon />}
137
- aria-label="Copy to clipboard"
138
- onPress={() => void copy(command)}
139
- />
140
- </XStack>
141
- <NotifyRegion scope="field" region={region} size="$3" />
107
+ <YStack data-empty-command="" alignSelf="center" {...readingWidthStyle()}>
108
+ <CodeBlock language="text">{command}</CodeBlock>
142
109
  </YStack>
143
110
  );
144
111
  }
@@ -1,27 +1,30 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
2
2
  // Catalog Button (G-15 story honesty / LC-18): raw tamagui buttons ignored
3
3
  // the knobs and pinned a non-house radius (UX-P06).
4
- import { Button, IconButton, Progress } from "@multiplatform.one/forms";
4
+ import { Button, IconButton } from "@multiplatform.one/forms";
5
5
  import {
6
- CopyIcon,
7
- DotsThreeIcon,
6
+ CpuIcon,
7
+ DatabaseIcon,
8
8
  MagnifyingGlassIcon,
9
+ MemoryIcon,
9
10
  PlusIcon,
10
11
  SignOutIcon,
11
- TrashIcon,
12
12
  UserIcon,
13
13
  } from "@phosphor-icons/react";
14
14
  import { Preset } from "@multiplatform.one/theme";
15
15
  import type { ReactNode } from "react";
16
- import { XStack, YStack } from "tamagui";
16
+ import { Circle, Theme, View, XStack, YStack } from "tamagui";
17
17
  import { Avatar } from "../Avatar";
18
18
  import { Card } from "../Card";
19
+ import { themed } from "../icons/themed";
20
+ import { Chip } from "../Chip";
21
+ import { CodeBlock } from "../code/CodeBlock";
19
22
  import { DropdownMenu } from "../DropdownMenu";
20
23
  import { Popover } from "../Popover";
21
24
  import { SchemeToggle } from "../SchemeToggle";
22
25
  import { PopoverContent } from "../surfaces";
23
26
  import { Tabs } from "../Tabs";
24
- import { PageBackButton, PageHeader, Screen, ScreenToolbar } from "./page";
27
+ import { PageBackButton, PageHeader, PageSection, Screen, ScreenToolbar } from "./page";
25
28
  import { Paragraph, SizableText } from "../Text";
26
29
 
27
30
  const meta: Meta<typeof PageHeader> = {
@@ -174,23 +177,237 @@ export const LongTitleNarrow: Story = {
174
177
  ),
175
178
  };
176
179
 
177
- // FZ-8020 LC-176: the SHC console Nodes screen with every icon-button
178
- // context, drawn by the platform with no use-site paint. Its bar (back,
179
- // search, add) takes the accent, on Liquid Glass on iOS 26; content actions
180
- // (the inline copy, the card's overflow) are bold glyphs in the text colour;
181
- // the delete keeps its intent ink; the tile's favourite rides the media plate.
182
- function Meter({ label, value, detail }: { label: string; value: number; detail: string }) {
180
+ // FZ-8442: the SHC console's Nodes screen as it is now (shc
181
+ // features/console/src/manage/app/NodesPane.tsx and ManageApp.tsx's
182
+ // NodesPlace): sites as sections, each node a front panel whose whole face
183
+ // links to its detail, where the verbs live. The "+" opens one panel over
184
+ // itself holding the join command as a code block (LC-112, LC-267). The bar
185
+ // keeps every LC-176 icon-button context.
186
+ const faceplate = {
187
+ ear: 18,
188
+ screw: 9,
189
+ slot: 1.5,
190
+ led: 14,
191
+ ledGap: 2,
192
+ digitWidth: 8,
193
+ digitHeight: 14,
194
+ segment: 2,
195
+ digitGap: 2,
196
+ window: 6,
197
+ } as const;
198
+ const LED_COUNT = 20;
199
+ const bayGlyph = { cpu: themed(CpuIcon), memory: themed(MemoryIcon), disk: themed(DatabaseIcon) };
200
+
201
+ function Screw({ angle }: { angle: number }) {
202
+ const inner = faceplate.screw - 2;
203
+ return (
204
+ <Circle
205
+ size={faceplate.screw}
206
+ borderWidth={1}
207
+ borderColor="$color9"
208
+ backgroundColor="$color6"
209
+ position="relative"
210
+ >
211
+ <View
212
+ position="absolute"
213
+ top={(inner - faceplate.slot) / 2}
214
+ left={0}
215
+ width={inner}
216
+ height={faceplate.slot}
217
+ backgroundColor="$color11"
218
+ rotate={`${angle}deg`}
219
+ />
220
+ </Circle>
221
+ );
222
+ }
223
+
224
+ function RackEar({ side, angles }: { side: "left" | "right"; angles: [number, number] }) {
225
+ return (
226
+ <YStack
227
+ width={faceplate.ear}
228
+ alignSelf="stretch"
229
+ alignItems="center"
230
+ justifyContent="space-between"
231
+ paddingVertical="$3"
232
+ backgroundColor="$color4"
233
+ borderColor="$color6"
234
+ borderLeftWidth={side === "right" ? 1 : 0}
235
+ borderRightWidth={side === "left" ? 1 : 0}
236
+ aria-hidden
237
+ >
238
+ <Screw angle={angles[0]} />
239
+ <Screw angle={angles[1]} />
240
+ </YStack>
241
+ );
242
+ }
243
+
244
+ const segmentsOf: Record<string, string> = {
245
+ "0": "abcdef",
246
+ "1": "bc",
247
+ "2": "abdeg",
248
+ "3": "abcdg",
249
+ "4": "bcfg",
250
+ "5": "acdfg",
251
+ "6": "acdefg",
252
+ "7": "abc",
253
+ "8": "abcdefg",
254
+ "9": "abcdfg",
255
+ };
256
+
257
+ function segmentBox(name: string, width: number) {
258
+ const { digitHeight: h, segment: t } = faceplate;
259
+ const middle = (h - t) / 2;
260
+ const stroke = (h + t) / 2;
261
+ const boxes: Record<string, { top: number; left: number; width: number; height: number }> = {
262
+ a: { top: 0, left: 0, width, height: t },
263
+ b: { top: 0, left: width - t, width: t, height: stroke },
264
+ c: { top: middle, left: width - t, width: t, height: stroke },
265
+ d: { top: h - t, left: 0, width, height: t },
266
+ e: { top: middle, left: 0, width: t, height: stroke },
267
+ f: { top: 0, left: 0, width: t, height: stroke },
268
+ g: { top: middle, left: 0, width, height: t },
269
+ };
270
+ return boxes[name]!;
271
+ }
272
+
273
+ function AddressDisplay({ address }: { address: string }) {
274
+ return (
275
+ <Theme name="dark">
276
+ <XStack
277
+ role="img"
278
+ aria-label={address}
279
+ alignItems="center"
280
+ gap={faceplate.digitGap}
281
+ backgroundColor="$color1"
282
+ borderColor="$color6"
283
+ borderWidth={1}
284
+ borderRadius="$2"
285
+ padding={faceplate.window}
286
+ >
287
+ {[...address].map((char, index) => {
288
+ if (char === "." || char === ":")
289
+ return (
290
+ <YStack
291
+ key={index}
292
+ width={faceplate.segment}
293
+ height={faceplate.digitHeight}
294
+ justifyContent={char === "." ? "flex-end" : "space-evenly"}
295
+ >
296
+ <View
297
+ width={faceplate.segment}
298
+ height={faceplate.segment}
299
+ backgroundColor="$blue10"
300
+ />
301
+ {char === ":" ? (
302
+ <View
303
+ width={faceplate.segment}
304
+ height={faceplate.segment}
305
+ backgroundColor="$blue10"
306
+ />
307
+ ) : null}
308
+ </YStack>
309
+ );
310
+ const width = char === "1" ? faceplate.segment : faceplate.digitWidth;
311
+ return (
312
+ <View key={index} width={width} height={faceplate.digitHeight} position="relative">
313
+ {[...(segmentsOf[char] ?? "")].map((name) => (
314
+ <View
315
+ key={name}
316
+ position="absolute"
317
+ {...segmentBox(name, width)}
318
+ backgroundColor="$blue10"
319
+ />
320
+ ))}
321
+ </View>
322
+ );
323
+ })}
324
+ </XStack>
325
+ </Theme>
326
+ );
327
+ }
328
+
329
+ const ledInk = { lit: "$color11", warning: "$orange9", critical: "$red9" } as const;
330
+ const lampAt = (index: number) =>
331
+ index >= LED_COUNT * 0.9 ? "critical" : index >= LED_COUNT * 0.7 ? "warning" : "lit";
332
+
333
+ function LedBar({
334
+ bay,
335
+ label,
336
+ percent,
337
+ amount,
338
+ }: {
339
+ bay: keyof typeof bayGlyph;
340
+ label: string;
341
+ percent: number;
342
+ amount: string;
343
+ }) {
344
+ const lit = Math.round((percent / 100) * LED_COUNT);
345
+ const Glyph = bayGlyph[bay];
183
346
  return (
184
- <YStack gap="$2">
185
- <XStack justifyContent="space-between">
186
- <SizableText color="$color11">{label}</SizableText>
187
- <SizableText>{detail}</SizableText>
347
+ <YStack gap="$1.5" role="meter" aria-label={label} aria-valuenow={percent}>
348
+ <XStack alignItems="center" gap="$2">
349
+ <Glyph size={16} weight="bold" color="$color11" />
350
+ <SizableText flex={1} size="$2" fontVariant={["tabular-nums"]} numberOfLines={1}>
351
+ {amount}
352
+ </SizableText>
353
+ <SizableText size="$2" fontWeight="700" fontVariant={["tabular-nums"]}>
354
+ {`${percent}%`}
355
+ </SizableText>
356
+ </XStack>
357
+ <XStack gap={faceplate.ledGap} aria-hidden>
358
+ {Array.from({ length: LED_COUNT }, (_, index) => (
359
+ <View
360
+ key={index}
361
+ flex={1}
362
+ height={faceplate.led}
363
+ borderRadius={1}
364
+ backgroundColor={ledInk[lampAt(index)]}
365
+ opacity={index < lit ? 1 : 0.18}
366
+ />
367
+ ))}
188
368
  </XStack>
189
- <Progress value={value} />
190
369
  </YStack>
191
370
  );
192
371
  }
193
372
 
373
+ function NodeFaceplate() {
374
+ return (
375
+ <Card padding="$0" overflow="hidden" flexDirection="row" alignItems="stretch">
376
+ <RackEar side="left" angles={[38, 121]} />
377
+ <YStack flex={1} gap="$3" padding="$3">
378
+ <XStack alignItems="center" justifyContent="space-between" gap="$2.5">
379
+ <XStack gap="$1.5" alignItems="center" aria-hidden>
380
+ <Circle size="$0.75" backgroundColor="$green9" />
381
+ <Circle size="$0.5" backgroundColor="$green9" />
382
+ </XStack>
383
+ <AddressDisplay address="10.64.96.1:4003" />
384
+ </XStack>
385
+ <XStack alignItems="center" gap="$2.5">
386
+ <SizableText size="$4" fontWeight="700" numberOfLines={1}>
387
+ incus01
388
+ </SizableText>
389
+ <SizableText size="$1" fontWeight="600">
390
+ incus
391
+ </SizableText>
392
+ </XStack>
393
+ <LedBar bay="cpu" label="CPU" percent={12} amount="2.83/24 cores" />
394
+ <LedBar bay="memory" label="Memory" percent={41} amount="25.3/61.9 GiB" />
395
+ <LedBar bay="disk" label="Disk" percent={74} amount="352/476 GiB" />
396
+ <XStack gap="$1.5" flexWrap="wrap">
397
+ <Chip size="$2">AMD Ryzen 9 7900</Chip>
398
+ <Chip size="$2">x86_64</Chip>
399
+ </XStack>
400
+ <Avatar.Group max={7} size={36} circular={false}>
401
+ {["pokemon", "keycloak", "nextcloud", "fizzy", "gitlab", "openobserve"].map((app) => (
402
+ <Avatar key={app} name={app} />
403
+ ))}
404
+ </Avatar.Group>
405
+ </YStack>
406
+ <RackEar side="right" angles={[92, 7]} />
407
+ </Card>
408
+ );
409
+ }
410
+
194
411
  // LC-112 (FZ-8665 Round 45): each icon trigger's panel covers its button with
195
412
  // no X, and a press outside closes it. The join panel's title sits on the
196
413
  // panel's padding.
@@ -209,7 +426,7 @@ function AddNode() {
209
426
  </Popover.Header>
210
427
  <YStack paddingHorizontal="$4" gap="$3">
211
428
  <Paragraph color="$color11">Run this on the new machine.</Paragraph>
212
- <SizableText fontFamily="$mono">shc node join</SizableText>
429
+ <CodeBlock language="text">shc node join</CodeBlock>
213
430
  </YStack>
214
431
  </YStack>
215
432
  </PopoverContent>
@@ -258,26 +475,9 @@ function NodesScreen() {
258
475
  }
259
476
  >
260
477
  <PageHeader title="Nodes" divider={false} actions={<AddNode />} />
261
- <XStack alignItems="center">
262
- <SizableText fontFamily="$mono" color="$color11">
263
- shc node join
264
- </SizableText>
265
- <IconButton inline icon={CopyIcon} aria-label="Copy" />
266
- </XStack>
267
- <Card gap="$4">
268
- <XStack justifyContent="space-between" alignItems="center">
269
- <SizableText size="$6" fontWeight="600">
270
- incus01
271
- </SizableText>
272
- <XStack alignItems="center">
273
- <IconButton icon={DotsThreeIcon} aria-label="More for incus01" />
274
- <IconButton error icon={TrashIcon} aria-label="Remove incus01" />
275
- </XStack>
276
- </XStack>
277
- <SizableText color="$color11">10.64.96.1:4003</SizableText>
278
- <Meter label="CPU" value={12} detail="2.83/24 cores 12%" />
279
- <Meter label="Mem" value={41} detail="25.3/61.9 GiB 41%" />
280
- </Card>
478
+ <PageSection title="Unplaced" description="1 node" headingLevel={3}>
479
+ <NodeFaceplate />
480
+ </PageSection>
281
481
  </Screen>
282
482
  );
283
483
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * LC-157 (FZ-8442): the bar's inline title is centred on the bar while it
3
+ * fits clear of both side groups, and slides into the span between them when
4
+ * the wider group would touch it (Clay's "Inbox" beside four trailing glyphs).
5
+ */
6
+ import { describe, expect, it } from "vitest";
7
+ import { BAR_TITLE_GAP, barTitleSpan, sideExtents } from "./barTitleSpan";
8
+
9
+ describe("bar title span (LC-157, FZ-8442)", () => {
10
+ it("centres on the bar when the title fits clear of both groups", () => {
11
+ const span = barTitleSpan({ row: 358, leftEnd: 44, rightStart: 314, title: 60 });
12
+ expect(span.left).toBe(span.right);
13
+ expect(span.left).toBe(44 + BAR_TITLE_GAP);
14
+ });
15
+
16
+ it("slides between the groups when centring would hide it", () => {
17
+ const span = barTitleSpan({ row: 358, leftEnd: 44, rightStart: 130, title: 45 });
18
+ expect(span).toEqual({ left: 44 + BAR_TITLE_GAP, right: 358 - 130 + BAR_TITLE_GAP });
19
+ expect(358 - span.left - span.right).toBeGreaterThanOrEqual(45);
20
+ });
21
+
22
+ it("reads the groups by side, so a right-to-left bar mirrors", () => {
23
+ const ltr = sideExtents(358, { x: 16, width: 44 }, { x: 250, width: 92 });
24
+ const rtl = sideExtents(358, { x: 298, width: 44 }, { x: 16, width: 92 });
25
+ expect(ltr).toEqual({ leftEnd: 60, rightStart: 250 });
26
+ expect(rtl).toEqual({ leftEnd: 108, rightStart: 298 });
27
+ });
28
+
29
+ it("an empty group takes no room", () => {
30
+ expect(sideExtents(358, { x: 16, width: 44 }, { x: 342, width: 0 })).toEqual({
31
+ leftEnd: 60,
32
+ rightStart: 0,
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,44 @@
1
+ /** The space kept between the bar's inline title and either side group. */
2
+ export const BAR_TITLE_GAP = 8;
3
+
4
+ export interface BarTitleBox {
5
+ /** The bar row's width. */
6
+ row: number;
7
+ /** Where the group on the left ends, from the row's left edge; 0 when empty. */
8
+ leftEnd: number;
9
+ /** Where the group on the right starts, from the row's left edge; 0 when empty. */
10
+ rightStart: number;
11
+ /** The title's natural one-line width. */
12
+ title: number;
13
+ }
14
+
15
+ /**
16
+ * Where the bar's inline title sits. Like UIKit it is centred on the bar
17
+ * while it fits clear of both side groups; when the wider group would touch
18
+ * it, it centres in the span left between the groups instead of vanishing.
19
+ */
20
+ export function barTitleSpan(box: BarTitleBox): { left: number; right: number } {
21
+ const left = box.leftEnd + BAR_TITLE_GAP;
22
+ const right = (box.rightStart ? box.row - box.rightStart : 0) + BAR_TITLE_GAP;
23
+ const centred = Math.max(left, right);
24
+ return box.row - 2 * centred >= box.title ? { left: centred, right: centred } : { left, right };
25
+ }
26
+
27
+ type Extent = { x: number; width: number };
28
+
29
+ /** The bar's two side groups as physical left and right extents (RTL-safe). */
30
+ export function sideExtents(
31
+ row: number,
32
+ a: Extent,
33
+ b: Extent,
34
+ ): Pick<BarTitleBox, "leftEnd" | "rightStart"> {
35
+ const groups = [a, b].filter((group) => group.width > 0);
36
+ let leftEnd = 0;
37
+ let rightStart = 0;
38
+ for (const group of groups) {
39
+ if (group.x + group.width / 2 < row / 2)
40
+ leftEnd = Math.max(leftEnd, Math.ceil(group.x + group.width));
41
+ else rightStart = rightStart ? Math.min(rightStart, Math.floor(group.x)) : Math.floor(group.x);
42
+ }
43
+ return { leftEnd, rightStart };
44
+ }