@ghxstship/xos-ui 0.0.0-stage → 4.16.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 (487) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -2
  3. package/package.json +76 -4
  4. package/scripts/generate-stories.mjs +85 -0
  5. package/src/AIDisclosureBadge/README.md +7 -0
  6. package/src/AccessGridMatrix/README.md +3 -0
  7. package/src/Accordion/README.md +7 -0
  8. package/src/ActivityFeedItem/README.md +3 -0
  9. package/src/AgencySlate/README.md +3 -0
  10. package/src/AppShell/README.md +5 -0
  11. package/src/ApplicationForm/README.md +3 -0
  12. package/src/AssertionRankBadge/README.md +5 -0
  13. package/src/AvailabilityCalendar/README.md +3 -0
  14. package/src/Avatar/Avatar.tsx +56 -0
  15. package/src/Avatar/README.md +3 -0
  16. package/src/Avatar/avatar-class.ts +19 -0
  17. package/src/AvatarStack/README.md +3 -0
  18. package/src/Badge/README.md +3 -0
  19. package/src/Banner/README.md +3 -0
  20. package/src/BidSheet/README.md +3 -0
  21. package/src/Board/README.md +6 -0
  22. package/src/BrandMark/BrandMark.tsx +95 -0
  23. package/src/BrandMark/README.md +7 -0
  24. package/src/BrandSlot/README.md +6 -0
  25. package/src/Breadcrumbs/README.md +3 -0
  26. package/src/BudgetGrid/README.md +10 -0
  27. package/src/BulkActionBar/README.md +3 -0
  28. package/src/Button/Button.tsx +100 -0
  29. package/src/Button/README.md +10 -0
  30. package/src/Button/button-class.ts +54 -0
  31. package/src/ButtonLink/README.md +7 -0
  32. package/src/Calendar/README.md +3 -0
  33. package/src/Card/README.md +3 -0
  34. package/src/Chart/README.md +9 -0
  35. package/src/Charts/Calendar.tsx +228 -0
  36. package/src/Charts/Chart.tsx +547 -0
  37. package/src/Charts/Charts.tsx +20 -0
  38. package/src/Charts/Charts.types.ts +40 -0
  39. package/src/Charts/Gallery.tsx +25 -0
  40. package/src/Charts/Heatmap.tsx +116 -0
  41. package/src/Charts/OrgChart.tsx +53 -0
  42. package/src/Charts/ResourceSchedule.tsx +91 -0
  43. package/src/Charts/Sparkline.tsx +59 -0
  44. package/src/Charts/Timeline.tsx +777 -0
  45. package/src/Charts/categories.ts +21 -0
  46. package/src/Charts/timeline-plan.ts +191 -0
  47. package/src/Checkbox/README.md +6 -0
  48. package/src/ChecklistRunner/README.md +3 -0
  49. package/src/Chips/Badge.tsx +27 -0
  50. package/src/Chips/Chips.shared.tsx +34 -0
  51. package/src/Chips/Chips.tsx +21 -0
  52. package/src/Chips/Chips.types.ts +35 -0
  53. package/src/Chips/DepartmentChip.tsx +16 -0
  54. package/src/Chips/PhaseChip.tsx +16 -0
  55. package/src/Chips/PropertyChip.tsx +15 -0
  56. package/src/Chips/RecordKindChip.tsx +14 -0
  57. package/src/Chips/StateChip.tsx +19 -0
  58. package/src/Chips/Tag.tsx +47 -0
  59. package/src/Chips/URIDChip.tsx +14 -0
  60. package/src/Chips/chipClass.tsx +8 -0
  61. package/src/Chips/codeClass.tsx +6 -0
  62. package/src/ClockButton/README.md +6 -0
  63. package/src/CodeSample/README.md +7 -0
  64. package/src/ColorPicker/README.md +5 -0
  65. package/src/Combobox/README.md +7 -0
  66. package/src/CommandMenu/CommandMenu.tsx +179 -0
  67. package/src/CommandMenu/README.md +5 -0
  68. package/src/ComparisonTable/README.md +9 -0
  69. package/src/Compass/ChecklistRunner.tsx +128 -0
  70. package/src/Compass/ClockButton.tsx +57 -0
  71. package/src/Compass/Compass.shared.tsx +58 -0
  72. package/src/Compass/Compass.tsx +26 -0
  73. package/src/Compass/Compass.types.ts +79 -0
  74. package/src/Compass/DaySheetView.tsx +104 -0
  75. package/src/Compass/ForceUpdateScreen.tsx +29 -0
  76. package/src/Compass/LiveActivity.tsx +60 -0
  77. package/src/Compass/OfflineBanner.tsx +31 -0
  78. package/src/Compass/PhotoCapture.tsx +69 -0
  79. package/src/Compass/QuickIncidentSheet.tsx +46 -0
  80. package/src/Compass/ScanSheet.tsx +83 -0
  81. package/src/Compass/ShiftCard.tsx +55 -0
  82. package/src/Compass/SignaturePad.tsx +71 -0
  83. package/src/Compass/SyncConflictSheet.tsx +60 -0
  84. package/src/ConsentBanner/ConsentBanner.tsx +210 -0
  85. package/src/ConsentBanner/ConsentBannerStatic.tsx +163 -0
  86. package/src/ConsentBanner/ConsentSettings.tsx +213 -0
  87. package/src/ConsentBanner/README.md +101 -0
  88. package/src/ConsentBanner/consent.ts +238 -0
  89. package/src/ConsentBanner/index.ts +40 -0
  90. package/src/ConsentBanner/static.ts +11 -0
  91. package/src/ConsentBanner/useConsent.ts +160 -0
  92. package/src/ContextMenu/README.md +3 -0
  93. package/src/CoordinateMatrix/README.md +6 -0
  94. package/src/CredentialBadge/README.md +7 -0
  95. package/src/CurrencyInput/README.md +7 -0
  96. package/src/DataTable/DataTable.tsx +1034 -0
  97. package/src/DataTable/DataTableResizer.tsx +72 -0
  98. package/src/DataTable/DataTableVirtualizer.tsx +58 -0
  99. package/src/DataTable/README.md +9 -0
  100. package/src/DatePicker/README.md +5 -0
  101. package/src/DateRangePicker/README.md +3 -0
  102. package/src/DaySheetView/README.md +3 -0
  103. package/src/DepartmentChip/README.md +3 -0
  104. package/src/DepartmentGlyph/README.md +18 -0
  105. package/src/Dialog/README.md +3 -0
  106. package/src/DiffViewer/README.md +3 -0
  107. package/src/Divider/README.md +3 -0
  108. package/src/Drawer/README.md +3 -0
  109. package/src/DropdownMenu/README.md +3 -0
  110. package/src/EPKViewer/README.md +3 -0
  111. package/src/Editing/ColorPicker.tsx +125 -0
  112. package/src/Editing/DiffViewer.tsx +49 -0
  113. package/src/Editing/Editing.tsx +29 -0
  114. package/src/Editing/Editing.types.ts +71 -0
  115. package/src/Editing/FilterBuilder.tsx +72 -0
  116. package/src/Editing/QuickAdd.tsx +102 -0
  117. package/src/Editing/QuickAddChips.tsx +180 -0
  118. package/src/Editing/contrastRatio.tsx +11 -0
  119. package/src/Editing/parseQuickAdd.tsx +187 -0
  120. package/src/Editing/quickAddCore.ts +204 -0
  121. package/src/Editing/timeShorthand.ts +157 -0
  122. package/src/Editing/useQuickAdd.ts +201 -0
  123. package/src/EmergencyCodeCard/README.md +10 -0
  124. package/src/EmptyState/README.md +5 -0
  125. package/src/EngagementTimeline/README.md +3 -0
  126. package/src/ErrorState/README.md +3 -0
  127. package/src/FAQ/README.md +7 -0
  128. package/src/FeatureGrid/README.md +7 -0
  129. package/src/Feedback/Banner.tsx +40 -0
  130. package/src/Feedback/EmptyState.tsx +32 -0
  131. package/src/Feedback/ErrorState.tsx +32 -0
  132. package/src/Feedback/Feedback.shared.tsx +38 -0
  133. package/src/Feedback/Feedback.tsx +18 -0
  134. package/src/Feedback/Feedback.types.ts +40 -0
  135. package/src/Feedback/InlineAlert.tsx +19 -0
  136. package/src/Feedback/RefusalNotice.tsx +46 -0
  137. package/src/Feedback/RegionBoundary.tsx +102 -0
  138. package/src/Feedback/Toast.tsx +31 -0
  139. package/src/Feedback/ToastRegion.tsx +26 -0
  140. package/src/Feedback/UndoToast.tsx +97 -0
  141. package/src/FeedbackWidget/README.md +3 -0
  142. package/src/Field/Field.tsx +81 -0
  143. package/src/FileDrop/README.md +3 -0
  144. package/src/FilePreviewer/README.md +3 -0
  145. package/src/FilterBuilder/README.md +3 -0
  146. package/src/ForceUpdateScreen/README.md +3 -0
  147. package/src/Forms/Checkbox.tsx +14 -0
  148. package/src/Forms/CurrencyInput.tsx +40 -0
  149. package/src/Forms/Forms.shared.tsx +5 -0
  150. package/src/Forms/Forms.tsx +20 -0
  151. package/src/Forms/Forms.types.ts +33 -0
  152. package/src/Forms/Input.tsx +28 -0
  153. package/src/Forms/Money.tsx +15 -0
  154. package/src/Forms/NumberInput.tsx +85 -0
  155. package/src/Forms/Radio.tsx +31 -0
  156. package/src/Forms/Select.tsx +54 -0
  157. package/src/Forms/Switch.tsx +14 -0
  158. package/src/Forms/Textarea.tsx +30 -0
  159. package/src/Gallery/README.md +5 -0
  160. package/src/GateLifecycleRail/README.md +7 -0
  161. package/src/GateReadinessPanel/README.md +8 -0
  162. package/src/Gateway/AgencySlate.tsx +76 -0
  163. package/src/Gateway/ApplicationForm.tsx +48 -0
  164. package/src/Gateway/AvailabilityCalendar.tsx +82 -0
  165. package/src/Gateway/EPKViewer.tsx +77 -0
  166. package/src/Gateway/EngagementTimeline.tsx +43 -0
  167. package/src/Gateway/Gateway.shared.tsx +5 -0
  168. package/src/Gateway/Gateway.tsx +30 -0
  169. package/src/Gateway/Gateway.types.ts +69 -0
  170. package/src/Gateway/OnboardingPacket.tsx +61 -0
  171. package/src/Gateway/OpportunityCard.tsx +73 -0
  172. package/src/Gateway/OpportunityFilters.tsx +64 -0
  173. package/src/Gateway/PaymentTracker.tsx +71 -0
  174. package/src/Gateway/PayoutDetailsForm.tsx +62 -0
  175. package/src/Gateway/ProfileEditor.tsx +45 -0
  176. package/src/Gateway/ProgressBar.tsx +30 -0
  177. package/src/Gateway/RatingDialog.tsx +83 -0
  178. package/src/Gateway/UpNextCard.tsx +76 -0
  179. package/src/Glyphs/Glyphs.tsx +66 -0
  180. package/src/Grids/BidSheet.tsx +63 -0
  181. package/src/Grids/BudgetGrid.tsx +164 -0
  182. package/src/Grids/Grids.shared.tsx +5 -0
  183. package/src/Grids/Grids.tsx +11 -0
  184. package/src/Grids/Grids.types.ts +38 -0
  185. package/src/Grids/SpreadsheetGrid.tsx +445 -0
  186. package/src/Grids/columnTotal.tsx +14 -0
  187. package/src/Grids/glForCode.tsx +11 -0
  188. package/src/Grids/parseCell.tsx +16 -0
  189. package/src/Help/FeedbackWidget.tsx +89 -0
  190. package/src/Help/Help.tsx +14 -0
  191. package/src/Help/Help.types.ts +51 -0
  192. package/src/Help/HelpPanel.tsx +116 -0
  193. package/src/Help/NotificationCenter.tsx +100 -0
  194. package/src/Help/OnboardingChecklist.tsx +57 -0
  195. package/src/Help/ShortcutEditor.tsx +81 -0
  196. package/src/Help/WhatsNew.tsx +33 -0
  197. package/src/HelpPanel/README.md +5 -0
  198. package/src/HoverCard/README.md +3 -0
  199. package/src/Icon/Icon.tsx +47 -0
  200. package/src/Icon/README.md +9 -0
  201. package/src/IconButton/README.md +6 -0
  202. package/src/Identity/BrandSlot.tsx +20 -0
  203. package/src/Identity/BrandSlotFrame.tsx +38 -0
  204. package/src/Identity/CredentialBadge.tsx +92 -0
  205. package/src/Identity/Identity.tsx +14 -0
  206. package/src/Identity/Identity.types.ts +25 -0
  207. package/src/Identity/PresenceIndicator.tsx +37 -0
  208. package/src/Identity/QRCode.tsx +22 -0
  209. package/src/Identity/WhiteLabelPreview.tsx +33 -0
  210. package/src/Identity/qrPath.tsx +16 -0
  211. package/src/Identity/tokenVariable.tsx +9 -0
  212. package/src/InlineAlert/README.md +3 -0
  213. package/src/Input/README.md +8 -0
  214. package/src/Kbd/Kbd.tsx +30 -0
  215. package/src/Kbd/README.md +6 -0
  216. package/src/Layout/AppShell.tsx +151 -0
  217. package/src/Layout/Layout.tsx +14 -0
  218. package/src/Layout/Layout.types.ts +43 -0
  219. package/src/Layout/PageHeader.tsx +22 -0
  220. package/src/Layout/ResizablePanel.tsx +81 -0
  221. package/src/Layout/ScrollRegion.tsx +107 -0
  222. package/src/Layout/SplitView.tsx +78 -0
  223. package/src/Layout/Stepper.tsx +39 -0
  224. package/src/LegalText/LegalText.tsx +289 -0
  225. package/src/LegalText/README.md +10 -0
  226. package/src/LegalText/index.ts +29 -0
  227. package/src/LegalText/legal-markdown.ts +581 -0
  228. package/src/Link/Link.tsx +25 -0
  229. package/src/Link/README.md +5 -0
  230. package/src/Link/link-class.ts +10 -0
  231. package/src/LiveActivity/README.md +3 -0
  232. package/src/LogoSlotWall/README.md +7 -0
  233. package/src/MapView/README.md +5 -0
  234. package/src/Marketing/AIDisclosureBadge.tsx +31 -0
  235. package/src/Marketing/ButtonLink.tsx +32 -0
  236. package/src/Marketing/CodeSample.tsx +47 -0
  237. package/src/Marketing/ComparisonScroll.tsx +7 -0
  238. package/src/Marketing/ComparisonTable.tsx +104 -0
  239. package/src/Marketing/FAQ.tsx +26 -0
  240. package/src/Marketing/FeatureGrid.tsx +65 -0
  241. package/src/Marketing/GateLifecycleRail.tsx +52 -0
  242. package/src/Marketing/LogoSlotWall.tsx +53 -0
  243. package/src/Marketing/Marketing.tsx +49 -0
  244. package/src/Marketing/Marketing.types.ts +64 -0
  245. package/src/Marketing/MarketingFooter.tsx +88 -0
  246. package/src/Marketing/MarketingHero.tsx +45 -0
  247. package/src/Marketing/MarketingNav.tsx +76 -0
  248. package/src/Marketing/MarketingPoints.tsx +16 -0
  249. package/src/Marketing/MarketingSection.tsx +52 -0
  250. package/src/Marketing/PersonaSwitcher.tsx +137 -0
  251. package/src/Marketing/PriceDisplay.tsx +56 -0
  252. package/src/Marketing/PricingTable.tsx +122 -0
  253. package/src/Marketing/ProofStatStrip.tsx +35 -0
  254. package/src/Marketing/ScreenshotFrame.tsx +56 -0
  255. package/src/Marketing/SurfaceTriptych.tsx +43 -0
  256. package/src/Marketing/Testimonial.tsx +45 -0
  257. package/src/Marketing/marketing-copy.ts +25 -0
  258. package/src/Marketing/provided.tsx +36 -0
  259. package/src/MarketingFooter/README.md +7 -0
  260. package/src/MarketingHero/README.md +7 -0
  261. package/src/MarketingNav/README.md +7 -0
  262. package/src/MarketingSection/README.md +7 -0
  263. package/src/Media/FilePreviewer.tsx +86 -0
  264. package/src/Media/MapView.tsx +200 -0
  265. package/src/Media/Media.tsx +11 -0
  266. package/src/Media/Media.types.ts +47 -0
  267. package/src/Media/RichTextEditor.tsx +198 -0
  268. package/src/Media/toLngLat.tsx +13 -0
  269. package/src/Menus/ContextMenu.tsx +106 -0
  270. package/src/Menus/DropdownMenu.tsx +51 -0
  271. package/src/Menus/Menus.shared.tsx +67 -0
  272. package/src/Menus/Menus.tsx +13 -0
  273. package/src/Menus/Menus.types.ts +49 -0
  274. package/src/Menus/OrgSwitcher.tsx +69 -0
  275. package/src/Menus/compactTriggerClass.tsx +7 -0
  276. package/src/Menus/menuClass.tsx +4 -0
  277. package/src/Menus/menuItemClass.tsx +7 -0
  278. package/src/Menus/orgSwitcherTriggerClass.tsx +7 -0
  279. package/src/Money/README.md +6 -0
  280. package/src/MultiSelect/README.md +3 -0
  281. package/src/Navigation/Accordion.tsx +59 -0
  282. package/src/Navigation/Breadcrumbs.tsx +59 -0
  283. package/src/Navigation/MoreFields.tsx +42 -0
  284. package/src/Navigation/Navigation.shared.tsx +32 -0
  285. package/src/Navigation/Navigation.tsx +21 -0
  286. package/src/Navigation/Navigation.types.ts +63 -0
  287. package/src/Navigation/Pagination.tsx +30 -0
  288. package/src/Navigation/SegmentedControl.tsx +87 -0
  289. package/src/Navigation/Sidebar.tsx +106 -0
  290. package/src/Navigation/TabBar.tsx +61 -0
  291. package/src/Navigation/Tabs.tsx +79 -0
  292. package/src/Navigation/TopNav.tsx +86 -0
  293. package/src/Navigation/sidebar-classes.ts +18 -0
  294. package/src/NotificationCenter/README.md +3 -0
  295. package/src/NumberInput/README.md +3 -0
  296. package/src/OfflineBanner/README.md +3 -0
  297. package/src/OnboardingChecklist/README.md +3 -0
  298. package/src/OnboardingPacket/README.md +3 -0
  299. package/src/OpportunityCard/README.md +5 -0
  300. package/src/OpportunityFilters/README.md +3 -0
  301. package/src/OrgChart/README.md +5 -0
  302. package/src/OrgSwitcher/README.md +3 -0
  303. package/src/Overlays/Dialog.tsx +54 -0
  304. package/src/Overlays/Drawer.tsx +62 -0
  305. package/src/Overlays/HoverCard.tsx +59 -0
  306. package/src/Overlays/Overlays.shared.tsx +12 -0
  307. package/src/Overlays/Overlays.tsx +20 -0
  308. package/src/Overlays/Overlays.types.ts +66 -0
  309. package/src/Overlays/Popover.tsx +70 -0
  310. package/src/Overlays/PropertyList.tsx +19 -0
  311. package/src/Overlays/ShortcutSheet.tsx +62 -0
  312. package/src/Overlays/SidePeek.tsx +117 -0
  313. package/src/Overlays/Tooltip.tsx +73 -0
  314. package/src/Overlays/panelClass.tsx +6 -0
  315. package/src/Overlays/popoverPanelClass.tsx +6 -0
  316. package/src/PageHeader/README.md +3 -0
  317. package/src/Pagination/README.md +3 -0
  318. package/src/PaymentTracker/README.md +3 -0
  319. package/src/PayoutDetailsForm/README.md +3 -0
  320. package/src/PersonaSwitcher/README.md +7 -0
  321. package/src/PhaseChip/README.md +5 -0
  322. package/src/PhaseGlyph/README.md +17 -0
  323. package/src/PhotoCapture/README.md +3 -0
  324. package/src/Pickers/Combobox.tsx +91 -0
  325. package/src/Pickers/DatePicker.tsx +60 -0
  326. package/src/Pickers/DateRangePicker.tsx +78 -0
  327. package/src/Pickers/FileDrop.tsx +84 -0
  328. package/src/Pickers/MultiSelect.tsx +118 -0
  329. package/src/Pickers/Pickers.shared.tsx +337 -0
  330. package/src/Pickers/Pickers.tsx +17 -0
  331. package/src/Pickers/Pickers.types.ts +47 -0
  332. package/src/Pickers/Slider.tsx +59 -0
  333. package/src/Pickers/TimePicker.tsx +68 -0
  334. package/src/Pickers/rankOptions.tsx +12 -0
  335. package/src/Popover/README.md +3 -0
  336. package/src/PresenceIndicator/README.md +3 -0
  337. package/src/PriceDisplay/README.md +7 -0
  338. package/src/PricingTable/README.md +9 -0
  339. package/src/ProfileEditor/README.md +3 -0
  340. package/src/ProgressBar/README.md +3 -0
  341. package/src/ProofStatStrip/README.md +7 -0
  342. package/src/PropertyChip/README.md +3 -0
  343. package/src/ProvenanceBadge/README.md +3 -0
  344. package/src/QRCode/README.md +6 -0
  345. package/src/QuickAdd/README.md +5 -0
  346. package/src/QuickIncidentSheet/README.md +3 -0
  347. package/src/Radio/README.md +3 -0
  348. package/src/RadioChannelTable/README.md +7 -0
  349. package/src/RatingDialog/README.md +3 -0
  350. package/src/ReconciliationTable/README.md +3 -0
  351. package/src/RecordKindChip/README.md +3 -0
  352. package/src/RecordKindGlyph/README.md +34 -0
  353. package/src/RecordRow/README.md +7 -0
  354. package/src/RefusalNotice/README.md +5 -0
  355. package/src/ResizablePanel/README.md +3 -0
  356. package/src/ResourceSchedule/README.md +6 -0
  357. package/src/RichTextEditor/README.md +3 -0
  358. package/src/RichTextEditor/extensions.ts +217 -0
  359. package/src/RunOfShowLive/README.md +5 -0
  360. package/src/ScanSheet/README.md +3 -0
  361. package/src/ScreenshotFrame/README.md +7 -0
  362. package/src/ScrollRegion/README.md +9 -0
  363. package/src/SegmentedControl/README.md +5 -0
  364. package/src/Select/README.md +5 -0
  365. package/src/ShiftCard/README.md +3 -0
  366. package/src/ShortcutEditor/README.md +3 -0
  367. package/src/ShortcutSheet/README.md +3 -0
  368. package/src/SidePeek/README.md +6 -0
  369. package/src/Sidebar/README.md +5 -0
  370. package/src/SignaturePad/README.md +3 -0
  371. package/src/Skeleton/README.md +3 -0
  372. package/src/Slider/README.md +3 -0
  373. package/src/SplitView/README.md +3 -0
  374. package/src/SpreadsheetGrid/README.md +9 -0
  375. package/src/StalenessIndicator/README.md +3 -0
  376. package/src/StatTile/README.md +6 -0
  377. package/src/StateChip/README.md +6 -0
  378. package/src/StateIcon/README.md +6 -0
  379. package/src/StateIcon/StateIcon.tsx +127 -0
  380. package/src/StatusHeatmap/README.md +15 -0
  381. package/src/Stepper/README.md +3 -0
  382. package/src/Surface/Skeleton.tsx +21 -0
  383. package/src/Surface/Surface.tsx +63 -0
  384. package/src/SurfaceTriptych/README.md +7 -0
  385. package/src/Switch/README.md +5 -0
  386. package/src/SyncConflictSheet/README.md +3 -0
  387. package/src/TabBar/README.md +7 -0
  388. package/src/Tabs/README.md +6 -0
  389. package/src/Tag/README.md +3 -0
  390. package/src/TemplateAuth/README.md +6 -0
  391. package/src/TemplateCollection/README.md +6 -0
  392. package/src/TemplateDashboard/README.md +8 -0
  393. package/src/TemplateDocument/README.md +5 -0
  394. package/src/TemplateGatewayShell/README.md +5 -0
  395. package/src/TemplateGridEditor/README.md +3 -0
  396. package/src/TemplateRecord/README.md +3 -0
  397. package/src/TemplateSettings/README.md +5 -0
  398. package/src/TemplateSplitView/README.md +3 -0
  399. package/src/TemplateSystem/README.md +5 -0
  400. package/src/TemplateWizard/README.md +3 -0
  401. package/src/Templates/TemplateAuth.tsx +16 -0
  402. package/src/Templates/TemplateCollection.tsx +35 -0
  403. package/src/Templates/TemplateDashboard.tsx +200 -0
  404. package/src/Templates/TemplateDocument.tsx +24 -0
  405. package/src/Templates/TemplateGatewayShell.tsx +36 -0
  406. package/src/Templates/TemplateGridEditor.tsx +17 -0
  407. package/src/Templates/TemplateRecord.tsx +31 -0
  408. package/src/Templates/TemplateSettings.tsx +26 -0
  409. package/src/Templates/TemplateSplitView.tsx +27 -0
  410. package/src/Templates/TemplateSystem.tsx +19 -0
  411. package/src/Templates/TemplateSystemLink.tsx +55 -0
  412. package/src/Templates/TemplateWizard.tsx +43 -0
  413. package/src/Templates/Templates.tsx +29 -0
  414. package/src/Templates/Templates.types.ts +153 -0
  415. package/src/Testimonial/README.md +7 -0
  416. package/src/Textarea/README.md +3 -0
  417. package/src/TimePicker/README.md +5 -0
  418. package/src/Timeline/README.md +6 -0
  419. package/src/Toast/README.md +3 -0
  420. package/src/Tooltip/README.md +3 -0
  421. package/src/TopNav/README.md +5 -0
  422. package/src/TreeView/README.md +3 -0
  423. package/src/URIDChip/README.md +5 -0
  424. package/src/UndoToast/README.md +3 -0
  425. package/src/UpNextCard/README.md +6 -0
  426. package/src/Views/ActivityFeedItem.tsx +52 -0
  427. package/src/Views/Board.tsx +229 -0
  428. package/src/Views/BulkActionBar.tsx +76 -0
  429. package/src/Views/RecordList.tsx +146 -0
  430. package/src/Views/RecordRow.tsx +132 -0
  431. package/src/Views/StatTile.tsx +88 -0
  432. package/src/Views/TreeView.tsx +161 -0
  433. package/src/Views/Views.tsx +18 -0
  434. package/src/Views/Views.types.ts +99 -0
  435. package/src/WhatsNew/README.md +3 -0
  436. package/src/WhiteLabelPreview/README.md +8 -0
  437. package/src/XOS/AccessGridMatrix.tsx +111 -0
  438. package/src/XOS/AssertionRankBadge.tsx +27 -0
  439. package/src/XOS/CoordinateMatrix.tsx +123 -0
  440. package/src/XOS/EmergencyCodeCard.tsx +109 -0
  441. package/src/XOS/GateReadinessPanel.tsx +100 -0
  442. package/src/XOS/ProvenanceBadge.tsx +19 -0
  443. package/src/XOS/RadioChannelTable.tsx +50 -0
  444. package/src/XOS/ReconciliationTable.tsx +64 -0
  445. package/src/XOS/RunOfShowLive.tsx +97 -0
  446. package/src/XOS/StalenessIndicator.tsx +31 -0
  447. package/src/XOS/StatusHeatmap.tsx +200 -0
  448. package/src/XOS/XOS.shared.tsx +90 -0
  449. package/src/XOS/XOS.tsx +29 -0
  450. package/src/XOS/XOS.types.ts +99 -0
  451. package/src/XOS/resolveStep.tsx +16 -0
  452. package/src/brand/defaults/README.md +13 -0
  453. package/src/brand/defaults/app-icon.svg +6 -0
  454. package/src/brand/defaults/favicon.svg +6 -0
  455. package/src/brand/defaults/logo-dark.svg +6 -0
  456. package/src/brand/defaults/logo-light.svg +6 -0
  457. package/src/brand/defaults/splash.svg +6 -0
  458. package/src/index.ts +606 -0
  459. package/src/lib/context.tsx +156 -0
  460. package/src/lib/cx.ts +4 -0
  461. package/src/lib/format.ts +200 -0
  462. package/src/lib/hooks.ts +187 -0
  463. package/src/lib/i18n.ts +97 -0
  464. package/src/lib/icons.ts +45 -0
  465. package/src/lib/live.ts +92 -0
  466. package/src/lib/preferences.ts +242 -0
  467. package/src/lib/route-transition.tsx +22 -0
  468. package/src/lib/selection.ts +92 -0
  469. package/src/lib/shortcut-targets.ts +125 -0
  470. package/src/lib/theme.ts +50 -0
  471. package/src/lib/undo-stack.ts +125 -0
  472. package/src/marketing.ts +33 -0
  473. package/src/pwa/action-outcome.tsx +81 -0
  474. package/src/pwa/analytics.ts +236 -0
  475. package/src/pwa/browser-push.tsx +132 -0
  476. package/src/pwa/client.ts +126 -0
  477. package/src/pwa/index.ts +40 -0
  478. package/src/pwa/offline-key.ts +18 -0
  479. package/src/pwa/service-worker.ts +229 -0
  480. package/src/static.ts +10 -0
  481. package/src/styles/base.css +208 -0
  482. package/src/styles/components.css +453 -0
  483. package/src/styles/index.css +16 -0
  484. package/src/styles/marketing.css +1260 -0
  485. package/src/styles/reference.css +5792 -0
  486. package/src/types/reference.d.ts +1608 -0
  487. package/tailwind.config.cjs +3 -0
@@ -0,0 +1,581 @@
1
+ /**
2
+ * The Markdown subset the legal documents are written in (legal/index.yaml), parsed to a tree the
3
+ * page renders as plain elements, so no HTML string ever reaches the page. Pure: no Node and no
4
+ * React, so a generator, a server component and a test all share it.
5
+ */
6
+
7
+ export type LegalInline =
8
+ | { readonly t: "text"; readonly v: string }
9
+ | { readonly t: "strong" | "em"; readonly c: readonly LegalInline[] }
10
+ | { readonly t: "code"; readonly v: string }
11
+ /** A written link: http(s) or mailto only. */
12
+ | { readonly t: "link"; readonly href: string; readonly c: readonly LegalInline[] }
13
+ /** A path to another document as the text writes it, such as /legal/contact. */
14
+ | { readonly t: "document"; readonly slug: string; readonly v: string }
15
+ | { readonly t: "mail"; readonly v: string }
16
+ /** "Section 3.6" where this document has that section. */
17
+ | { readonly t: "section"; readonly id: string; readonly v: string };
18
+
19
+ export interface LegalListItem {
20
+ readonly c: readonly LegalInline[];
21
+ readonly children: readonly LegalBlock[];
22
+ }
23
+
24
+ export type LegalBlock =
25
+ | {
26
+ readonly t: "heading";
27
+ readonly level: 1 | 2 | 3 | 4 | 5 | 6;
28
+ readonly id: string;
29
+ readonly v: string;
30
+ }
31
+ | { readonly t: "p"; readonly id?: string; readonly c: readonly LegalInline[] }
32
+ | { readonly t: "quote"; readonly c: readonly LegalBlock[] }
33
+ | {
34
+ readonly t: "list";
35
+ readonly ordered: boolean;
36
+ readonly start: number;
37
+ readonly items: readonly LegalListItem[];
38
+ }
39
+ | {
40
+ readonly t: "table";
41
+ /** The accessible name of the table's scrolling region, unique on the page. */
42
+ readonly label: string;
43
+ readonly head: readonly (readonly LegalInline[])[];
44
+ readonly rows: readonly (readonly (readonly LegalInline[])[])[];
45
+ };
46
+
47
+ const FRONTMATTER = /^---\r?\n[\s\S]*?\r?\n---\r?\n+/;
48
+
49
+ /** The document without its YAML frontmatter, and nothing else removed. */
50
+ export function stripFrontmatter(markdown: string): string {
51
+ return markdown.replace(FRONTMATTER, "");
52
+ }
53
+
54
+ /** A version still in review: `1.0.0-draft` (legal/index.yaml). */
55
+ export function isDraftVersion(version: string): boolean {
56
+ return version.endsWith("-draft");
57
+ }
58
+
59
+ const SECTION_NUMBER = /^(\d+(?:\.\d+)*)\.?\s/;
60
+ const CLAUSE = /^(\d+(?:\.\d+)+)\s/;
61
+ const LEADING_SPACE = /^\s*/;
62
+ const HEADING_HASHES = /^#{1,6}/;
63
+ const LIST_MARKER = /^(?:[-*+]|\d+\.)/;
64
+ const RULE_CELL = /^:?-+:?$/;
65
+
66
+ /*
67
+ * Headings, list items and table rules are read in steps rather than by one pattern each: a single
68
+ * pattern with neighbouring optional spaces (`\s+(.+?)\s*$`) backtracks over every way to split a
69
+ * long run of spaces, so the time grows with the square of the line.
70
+ */
71
+
72
+ /** `## Title`: [line, the hashes, the title trimmed], or null for any other line. */
73
+ function heading(line: string): [string, string, string] | null {
74
+ const hashes = HEADING_HASHES.exec(line)?.[0];
75
+ if (hashes === undefined) return null;
76
+ const rest = line.slice(hashes.length);
77
+ if (!LEADING_SPACE.exec(rest)?.[0]) return null;
78
+ const title = rest.trim();
79
+ return title === "" ? null : [line, hashes, title];
80
+ }
81
+
82
+ /** `- item` or `1. item`: [line, its indent, the marker, the text after it], or null. */
83
+ function listItem(line: string): [string, string, string, string] | null {
84
+ const indent = LEADING_SPACE.exec(line)?.[0] ?? "";
85
+ const marker = LIST_MARKER.exec(line.slice(indent.length))?.[0];
86
+ if (marker === undefined) return null;
87
+ const rest = line.slice(indent.length + marker.length);
88
+ if (!LEADING_SPACE.exec(rest)?.[0]) return null;
89
+ return [line, indent, marker, rest.trimStart()];
90
+ }
91
+
92
+ /** A table's rule line, `| --- | :-: |`: every cell between the pipes is dashes with optional colons. */
93
+ function isTableRule(line: string): boolean {
94
+ let row = line.trim();
95
+ if (row.startsWith("|")) row = row.slice(1);
96
+ if (row.endsWith("|")) row = row.slice(0, -1);
97
+ return row.split("|").every((cell) => RULE_CELL.test(cell.trim()));
98
+ }
99
+ const QUOTE = /^>\s?/;
100
+
101
+ /**
102
+ * Bold, code, a written link, or underscore emphasis that opens and closes on a word edge. A link's label
103
+ * and address stop at the next bracket or parenthesis, so a run of unclosed links reads in one pass.
104
+ */
105
+ const INLINE =
106
+ /\*\*(.+?)\*\*|`([^`]+)`|\[([^\][]+)\]\(([^()[\]\s]+)\)|(?<![\p{L}\p{N}_])_([^_]+?)_(?![\p{L}\p{N}_])/gu;
107
+
108
+ /**
109
+ * A path to a document, an email address, or a section of this document. A section named with
110
+ * another document ("Section 5 of the Security Overview", "Security Overview, Section 3") stays text.
111
+ */
112
+ const AUTOLINK =
113
+ /(?<![\w./-])(?:atlas\.ghxstship\.tech)?\/legal\/([a-z0-9-]+)|(?<![\w.+-])[\w.+-]+@[a-z0-9-]+(?:\.[a-z0-9-]+)*\.[a-z]{2,}|(?<![A-Z][a-z]+, )\bSection (\d+(?:\.\d+)*)\b(?!\.\d| of\b)/g;
114
+
115
+ const SAFE_HREF = /^(https?:\/\/|mailto:)/;
116
+
117
+ function sectionId(number: string): string {
118
+ return `section-${number.replaceAll(".", "-")}`;
119
+ }
120
+
121
+ function autolinks(text: string, sections: ReadonlySet<string>): LegalInline[] {
122
+ const out: LegalInline[] = [];
123
+ let last = 0;
124
+ for (const m of text.matchAll(AUTOLINK)) {
125
+ const [v, slug, section] = m;
126
+ let node: LegalInline | null = null;
127
+ if (slug !== undefined) node = { t: "document", slug, v };
128
+ else if (section === undefined) node = { t: "mail", v };
129
+ else if (sections.has(section)) node = { t: "section", id: sectionId(section), v };
130
+ if (!node) continue;
131
+ if (m.index > last) out.push({ t: "text", v: text.slice(last, m.index) });
132
+ out.push(node);
133
+ last = m.index + v.length;
134
+ }
135
+ if (last < text.length) out.push({ t: "text", v: text.slice(last) });
136
+ return out;
137
+ }
138
+
139
+ function parseInline(text: string, sections: ReadonlySet<string>): LegalInline[] {
140
+ const out: LegalInline[] = [];
141
+ let last = 0;
142
+ for (const m of text.matchAll(INLINE)) {
143
+ if (m.index > last) out.push(...autolinks(text.slice(last, m.index), sections));
144
+ const [whole, strong, code, label, href, em] = m;
145
+ if (strong !== undefined) out.push({ t: "strong", c: parseInline(strong, sections) });
146
+ else if (code !== undefined) out.push({ t: "code", v: code });
147
+ else if (em !== undefined) out.push({ t: "em", c: parseInline(em, sections) });
148
+ else if (label !== undefined && href !== undefined && SAFE_HREF.test(href)) {
149
+ out.push({ t: "link", href, c: parseInline(label, new Set()) });
150
+ } else out.push(...parseInline(label ?? whole, sections));
151
+ last = m.index + whole.length;
152
+ }
153
+ if (last < text.length) out.push(...autolinks(text.slice(last), sections));
154
+ return out;
155
+ }
156
+
157
+ /** The text of an inline run, without its markup. */
158
+ export function inlineText(parts: readonly LegalInline[]): string {
159
+ return parts.map((part) => ("c" in part ? inlineText(part.c) : part.v)).join("");
160
+ }
161
+
162
+ function slugify(text: string): string {
163
+ return text
164
+ .toLowerCase()
165
+ .replace(/[^a-z0-9]+/g, "-")
166
+ .replace(/^-+|-+$/g, "");
167
+ }
168
+
169
+ /** Splits a table row on its pipes; a pipe inside a code span or written as \| stays in the cell. */
170
+ function tableCells(line: string): string[] {
171
+ const row = line.trim().replace(/^\|/, "");
172
+ const cells: string[] = [];
173
+ let cell = "";
174
+ let inCode = false;
175
+ for (let i = 0; i < row.length; i += 1) {
176
+ const ch = row.charAt(i);
177
+ if (ch === "\\" && row.charAt(i + 1) === "|") {
178
+ cell += "|";
179
+ i += 1;
180
+ } else if (ch === "|" && !inCode) {
181
+ cells.push(cell.trim());
182
+ cell = "";
183
+ } else {
184
+ if (ch === "`") inCode = !inCode;
185
+ cell += ch;
186
+ }
187
+ }
188
+ if (cell.trim() !== "") cells.push(cell.trim());
189
+ return cells;
190
+ }
191
+
192
+ interface Scope {
193
+ readonly sections: ReadonlySet<string>;
194
+ readonly ids: Set<string>;
195
+ readonly labels: Set<string>;
196
+ /** The nearest heading so far, which names a table that has no caption of its own. */
197
+ heading: string;
198
+ }
199
+
200
+ function uniqueIn(used: Set<string>, wanted: string, join: (n: number) => string): string {
201
+ let value = wanted;
202
+ for (let n = 2; used.has(value); n += 1) value = join(n);
203
+ used.add(value);
204
+ return value;
205
+ }
206
+
207
+ function headingBlock(hashes: string, raw: string, scope: Scope): LegalBlock {
208
+ const v = inlineText(parseInline(raw, new Set()));
209
+ const number = SECTION_NUMBER.exec(v)?.[1];
210
+ const wanted = (number ? sectionId(number) : slugify(v)) || "section";
211
+ const id = uniqueIn(scope.ids, wanted, (n) => `${wanted}-${n}`);
212
+ scope.heading = v;
213
+ return { t: "heading", level: hashes.length as 1 | 2 | 3 | 4 | 5 | 6, id, v };
214
+ }
215
+
216
+ function paragraphBlock(text: string, scope: Scope): LegalBlock {
217
+ const c = parseInline(text, scope.sections);
218
+ const number = CLAUSE.exec(text)?.[1];
219
+ const wanted = number ? sectionId(number) : undefined;
220
+ if (wanted === undefined || scope.ids.has(wanted)) return { t: "p", c };
221
+ scope.ids.add(wanted);
222
+ return { t: "p", id: wanted, c };
223
+ }
224
+
225
+ function tableBlock(lines: readonly string[], scope: Scope): LegalBlock {
226
+ const [head = "", , ...rows] = lines;
227
+ const cells = (line: string) => tableCells(line).map((c) => parseInline(c, scope.sections));
228
+ const label = uniqueIn(scope.labels, scope.heading, (n) => `${scope.heading} (${n})`);
229
+ return { t: "table", label, head: cells(head), rows: rows.map(cells) };
230
+ }
231
+
232
+ /** A list and the lists nested in it, from `from`; returns the block and the next unread line. */
233
+ function listBlock(
234
+ lines: readonly string[],
235
+ from: number,
236
+ scope: Scope,
237
+ ): { block: LegalBlock; next: number } {
238
+ const first = listItem(lines[from] ?? "");
239
+ const indent = first?.[1]?.length ?? 0;
240
+ const ordered = /\d/.test(first?.[2] ?? "");
241
+ const items: { c: LegalInline[]; children: LegalBlock[] }[] = [];
242
+ let i = from;
243
+ let text: string | null = null;
244
+ let nested: LegalBlock[] = [];
245
+ const close = () => {
246
+ if (text !== null) items.push({ c: parseInline(text, scope.sections), children: nested });
247
+ text = null;
248
+ nested = [];
249
+ };
250
+ while (i < lines.length) {
251
+ const line = lines[i] ?? "";
252
+ const item = listItem(line);
253
+ if (line.trim() === "") {
254
+ // A blank line ends the list unless the next line carries on with it.
255
+ let peek = i + 1;
256
+ while (peek < lines.length && (lines[peek] ?? "").trim() === "") peek += 1;
257
+ const after = listItem(lines[peek] ?? "");
258
+ const continues =
259
+ after !== null &&
260
+ ((after[1]?.length ?? 0) > indent ||
261
+ ((after[1]?.length ?? 0) === indent && /\d/.test(after[2] ?? "") === ordered));
262
+ if (!continues) break;
263
+ i = peek;
264
+ } else if (item) {
265
+ const at = item[1]?.length ?? 0;
266
+ if (at < indent) break;
267
+ if (at > indent) {
268
+ const inner = listBlock(lines, i, scope);
269
+ nested.push(inner.block);
270
+ i = inner.next;
271
+ } else if (/\d/.test(item[2] ?? "") !== ordered) {
272
+ break;
273
+ } else {
274
+ close();
275
+ text = item[3] ?? "";
276
+ i += 1;
277
+ }
278
+ } else if (startsBlock(lines, i)) {
279
+ break;
280
+ } else {
281
+ // A line directly under an item continues that item.
282
+ text = `${text ?? ""} ${line.trim()}`.trim();
283
+ i += 1;
284
+ }
285
+ }
286
+ close();
287
+ const start = ordered ? Number.parseInt(first?.[2] ?? "1", 10) : 1;
288
+ return { block: { t: "list", ordered, start, items }, next: i };
289
+ }
290
+
291
+ function startsTable(lines: readonly string[], i: number): boolean {
292
+ return (lines[i] ?? "").includes("|") && isTableRule(lines[i + 1] ?? "");
293
+ }
294
+
295
+ function startsBlock(lines: readonly string[], i: number): boolean {
296
+ const line = lines[i] ?? "";
297
+ return (
298
+ line.trim() === "" ||
299
+ heading(line) !== null ||
300
+ QUOTE.test(line) ||
301
+ listItem(line) !== null ||
302
+ startsTable(lines, i)
303
+ );
304
+ }
305
+
306
+ function parseBlocks(lines: readonly string[], scope: Scope): LegalBlock[] {
307
+ const blocks: LegalBlock[] = [];
308
+ let i = 0;
309
+ while (i < lines.length) {
310
+ const line = lines[i] ?? "";
311
+ const title = heading(line);
312
+ if (line.trim() === "") {
313
+ i += 1;
314
+ } else if (title) {
315
+ blocks.push(headingBlock(title[1], title[2], scope));
316
+ i += 1;
317
+ } else if (QUOTE.test(line)) {
318
+ const inner: string[] = [];
319
+ for (; QUOTE.test(lines[i] ?? ""); i += 1) inner.push((lines[i] ?? "").replace(QUOTE, ""));
320
+ blocks.push({ t: "quote", c: parseBlocks(inner, scope) });
321
+ } else if (listItem(line) !== null) {
322
+ const { block, next } = listBlock(lines, i, scope);
323
+ blocks.push(block);
324
+ i = next;
325
+ } else if (startsTable(lines, i)) {
326
+ const table: string[] = [];
327
+ for (; (lines[i] ?? "").includes("|") && (lines[i] ?? "").trim() !== ""; i += 1) {
328
+ table.push(lines[i] ?? "");
329
+ }
330
+ blocks.push(tableBlock(table, scope));
331
+ } else {
332
+ const text: string[] = [line.trim()];
333
+ for (i += 1; i < lines.length && !startsBlock(lines, i); i += 1) {
334
+ text.push((lines[i] ?? "").trim());
335
+ }
336
+ blocks.push(paragraphBlock(text.join(" "), scope));
337
+ }
338
+ }
339
+ return blocks;
340
+ }
341
+
342
+ /** The section numbers a document's headings and numbered clauses give. */
343
+ function sectionNumbers(lines: readonly string[]): Set<string> {
344
+ const found = new Set<string>();
345
+ for (const line of lines) {
346
+ const title = heading(line)?.[2];
347
+ const number = title ? SECTION_NUMBER.exec(`${title} `)?.[1] : CLAUSE.exec(line)?.[1];
348
+ if (number) found.add(number);
349
+ }
350
+ return found;
351
+ }
352
+
353
+ /**
354
+ * The id of the version history that ends each page (LegalHistory). The parser keeps it out of a
355
+ * document's own ids, so a heading of the document named Version History gets another one.
356
+ */
357
+ export const LEGAL_HISTORY_ID = "version-history";
358
+
359
+ /** Parses a document's Markdown, with its frontmatter already stripped, into blocks. */
360
+ export function parseLegalMarkdown(markdown: string): LegalBlock[] {
361
+ const lines = markdown.split(/\r?\n/);
362
+ return parseBlocks(lines, {
363
+ sections: sectionNumbers(lines),
364
+ ids: new Set([LEGAL_HISTORY_ID]),
365
+ labels: new Set(),
366
+ heading: "",
367
+ });
368
+ }
369
+
370
+ /** The plain text of parsed blocks, in reading order. */
371
+ export function legalPlainText(blocks: readonly LegalBlock[]): string {
372
+ return blocks
373
+ .map((block) => {
374
+ switch (block.t) {
375
+ case "heading":
376
+ return block.v;
377
+ case "p":
378
+ return inlineText(block.c);
379
+ case "quote":
380
+ return legalPlainText(block.c);
381
+ case "list":
382
+ return block.items
383
+ .map((item) => `${inlineText(item.c)} ${legalPlainText(item.children)}`)
384
+ .join(" ");
385
+ case "table":
386
+ return [block.head, ...block.rows]
387
+ .map((row) => row.map((cell) => inlineText(cell)).join(" "))
388
+ .join(" ");
389
+ }
390
+ })
391
+ .join(" ");
392
+ }
393
+
394
+ /** Every /legal/{slug} a document's text points to. */
395
+ export function legalDocumentLinks(blocks: readonly LegalBlock[]): string[] {
396
+ const inlines = (parts: readonly LegalInline[]): string[] =>
397
+ parts.flatMap((part) =>
398
+ part.t === "document" ? [part.slug] : "c" in part ? inlines(part.c) : [],
399
+ );
400
+ return blocks.flatMap((block) => {
401
+ switch (block.t) {
402
+ case "heading":
403
+ return [];
404
+ case "p":
405
+ return inlines(block.c);
406
+ case "quote":
407
+ return legalDocumentLinks(block.c);
408
+ case "list":
409
+ return block.items.flatMap((i) => [...inlines(i.c), ...legalDocumentLinks(i.children)]);
410
+ case "table":
411
+ return [block.head, ...block.rows].flatMap((row) => row.flatMap((cell) => inlines(cell)));
412
+ }
413
+ });
414
+ }
415
+
416
+ /** One version of a document as legal/index.yaml records it under `history`, newest first. */
417
+ export interface LegalHistoryEntry {
418
+ readonly version: string;
419
+ /** The day the version took effect, YYYY-MM-DD. A draft has none. */
420
+ readonly effective_date?: string;
421
+ /** What changed in this version, in a sentence or two. */
422
+ readonly summary: string;
423
+ }
424
+
425
+ /** One version of a document as its page lists it. */
426
+ export interface LegalVersion {
427
+ readonly version: string;
428
+ /** YYYY-MM-DD, or null while the version is a draft and so not in effect. */
429
+ readonly effectiveDate: string | null;
430
+ readonly summary: string;
431
+ }
432
+
433
+ /**
434
+ * Where a version stands on a given day: a draft is never in effect, a final version is upcoming
435
+ * until its effective date (the notice period of Terms of Service 15) and in effect from that day.
436
+ */
437
+ export type LegalVersionState = "draft" | "upcoming" | "inEffect";
438
+
439
+ /** The UTC calendar day of `now`, YYYY-MM-DD, the clock the pages compare effective dates with. */
440
+ export function legalDay(now: Date): string {
441
+ return now.toISOString().slice(0, 10);
442
+ }
443
+
444
+ /** Where `version` stands on `today`, a YYYY-MM-DD day from legalDay. */
445
+ export function legalVersionState(version: LegalVersion, today: string): LegalVersionState {
446
+ if (version.effectiveDate === null) return "draft";
447
+ return version.effectiveDate > today ? "upcoming" : "inEffect";
448
+ }
449
+
450
+ /**
451
+ * The newest version of `history` in effect on `today`, or undefined when none is yet. While the
452
+ * current version is upcoming, this is the one that still applies until its date.
453
+ */
454
+ export function legalVersionInEffect(
455
+ history: readonly LegalVersion[],
456
+ today: string,
457
+ ): LegalVersion | undefined {
458
+ return history.find((v) => legalVersionState(v, today) === "inEffect");
459
+ }
460
+
461
+ /** A document of the registry, as the text generator reads it. */
462
+ export interface LegalTextEntry {
463
+ readonly slug: string;
464
+ readonly file: string;
465
+ readonly publish: string;
466
+ /** The current version; the document's frontmatter states the same one. */
467
+ readonly version: string;
468
+ /** Every version, newest first; the first is the current one. */
469
+ readonly history: readonly LegalHistoryEntry[];
470
+ }
471
+
472
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
473
+
474
+ /** True for a real calendar day written YYYY-MM-DD. */
475
+ function isIsoDate(value: string | undefined): value is string {
476
+ if (value === undefined || !ISO_DATE.test(value)) return false;
477
+ const day = new Date(`${value}T00:00:00Z`);
478
+ return !Number.isNaN(day.getTime()) && day.toISOString().startsWith(value);
479
+ }
480
+
481
+ /** A field of the document's YAML frontmatter, unquoted, or undefined when it has none. */
482
+ export function frontmatterField(markdown: string, key: string): string | undefined {
483
+ const front = FRONTMATTER.exec(markdown)?.[0] ?? "";
484
+ const line = front.split(/\r?\n/).find((l) => l.startsWith(`${key}:`));
485
+ const value = line?.slice(key.length + 1).trim();
486
+ return value?.replace(/^(["'])(.*)\1$/, "$2");
487
+ }
488
+
489
+ /**
490
+ * The versions a document's page lists, newest first, checked against its text. The current version
491
+ * comes first and matches the frontmatter's; only it may be a draft, and only while no version is
492
+ * final, since the page serves one text and a draft would hide a final one. A new version of a
493
+ * final document is final from the start, with an effective date far enough ahead for its notice;
494
+ * the page calls it upcoming until then. A final version carries its effective date, and the
495
+ * current one carries the day its frontmatter states. Throws on any mismatch, so the generator
496
+ * cannot publish a page whose dates disagree with the document.
497
+ */
498
+ export function legalVersions(entry: LegalTextEntry, markdown: string): LegalVersion[] {
499
+ const fail = (problem: string): never => {
500
+ throw new Error(`legal/index.yaml, ${entry.slug}: ${problem}`);
501
+ };
502
+ if (frontmatterField(markdown, "version") !== entry.version) {
503
+ fail(`${entry.file} does not state version ${entry.version}`);
504
+ }
505
+ const { history } = entry;
506
+ if (history[0]?.version !== entry.version) {
507
+ fail(`history must open with the current version, ${entry.version}`);
508
+ }
509
+ if (new Set(history.map((h) => h.version)).size !== history.length) {
510
+ fail("history lists a version twice");
511
+ }
512
+ const versions = history.map((h, i): LegalVersion => {
513
+ if (h.summary.trim() === "") fail(`version ${h.version} has no summary of changes`);
514
+ if (isDraftVersion(h.version)) {
515
+ if (i > 0) fail(`only the current version may be a draft, not ${h.version}`);
516
+ const final = history[1];
517
+ if (final !== undefined && !isDraftVersion(final.version)) {
518
+ fail(
519
+ `draft ${h.version} would hide final ${final.version}; give the new version its effective_date instead`,
520
+ );
521
+ }
522
+ if (h.effective_date !== undefined) fail(`draft ${h.version} cannot have an effective date`);
523
+ return { version: h.version, effectiveDate: null, summary: h.summary.trim() };
524
+ }
525
+ if (!isIsoDate(h.effective_date))
526
+ fail(`version ${h.version} needs an effective_date, YYYY-MM-DD`);
527
+ return {
528
+ version: h.version,
529
+ effectiveDate: h.effective_date ?? null,
530
+ summary: h.summary.trim(),
531
+ };
532
+ });
533
+ const current = versions[0];
534
+ if (current && current.effectiveDate !== null) {
535
+ if (frontmatterField(markdown, "effective_date") !== current.effectiveDate) {
536
+ fail(`${entry.file} must state effective_date: ${current.effectiveDate}`);
537
+ }
538
+ }
539
+ const dated = versions.flatMap((v) => (v.effectiveDate === null ? [] : [v.effectiveDate]));
540
+ if (dated.some((day, i) => i > 0 && day > (dated[i - 1] ?? day))) {
541
+ fail("history must list versions newest first");
542
+ }
543
+ return versions;
544
+ }
545
+
546
+ /**
547
+ * The source of an app's generated legal text module: the body of each document the app serves,
548
+ * frontmatter removed, so the server bundle carries the text and no page reads a file at runtime,
549
+ * with each document's versions for the page to list. Internal documents never enter it.
550
+ * LEGAL_SLUGS is every public document, served here or not.
551
+ */
552
+ export function legalTextSource(
553
+ stamp: string,
554
+ documents: readonly LegalTextEntry[],
555
+ read: (file: string) => string,
556
+ serves: (slug: string) => boolean = () => true,
557
+ ): string {
558
+ const published = documents.filter((d) => d.publish === "public");
559
+ const served = published.filter((d) => serves(d.slug)).map((d) => ({ d, source: read(d.file) }));
560
+ const text = Object.fromEntries(
561
+ served.map(({ d, source }) => [d.slug, stripFrontmatter(source)]),
562
+ );
563
+ const history = Object.fromEntries(
564
+ served.map(({ d, source }) => [d.slug, legalVersions(d, source)]),
565
+ );
566
+ return `${stamp}
567
+ import type { LegalVersion } from "@xos/ui/legal-text";
568
+
569
+ /** The text of each legal document this app serves, without its frontmatter (Section 14.4). */
570
+ export const LEGAL_TEXT: Readonly<Record<string, string>> = ${JSON.stringify(text)};
571
+
572
+ /**
573
+ * Each served document's versions, newest first, from legal/index.yaml: the first is the current
574
+ * one, and its effective date is the one its frontmatter states (null while it is a draft).
575
+ */
576
+ export const LEGAL_HISTORY: Readonly<Record<string, readonly LegalVersion[]>> = ${JSON.stringify(history)};
577
+
578
+ /** Every public legal document, whichever app serves it. */
579
+ export const LEGAL_SLUGS: readonly string[] = ${JSON.stringify(published.map((d) => d.slug))};
580
+ `;
581
+ }
@@ -0,0 +1,25 @@
1
+ import type { ReactElement } from "react";
2
+ import type { LinkProps as ReferenceLinkProps } from "../types/reference";
3
+ import { cx } from "../lib/cx.ts";
4
+ import { useT } from "../lib/context.tsx";
5
+ import { Icon } from "../Icon/Icon.tsx";
6
+ import { linkClass } from "./link-class.ts";
7
+
8
+ export type LinkProps = ReferenceLinkProps;
9
+
10
+ export { linkClass };
11
+
12
+ /** Navigates; Button acts. External links open in a new tab and say so. */
13
+ export function Link({ external = false, className, children, ...rest }: LinkProps): ReactElement {
14
+ const t = useT();
15
+ return (
16
+ <a
17
+ {...rest}
18
+ {...(external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
19
+ className={cx(linkClass, className)}
20
+ >
21
+ {children}
22
+ {external ? <Icon name="ExternalLink" size={12} label={t("link.newTab")} /> : null}
23
+ </a>
24
+ );
25
+ }
@@ -0,0 +1,5 @@
1
+ # Link
2
+
3
+ Link navigates; Button acts. External links show an arrow and open in a new tab.
4
+
5
+ - Dark theme links use `focus-ring` for contrast; light theme links use `accent-default`.
@@ -0,0 +1,10 @@
1
+ import { cx } from "../lib/cx.ts";
2
+
3
+ /**
4
+ * Link classes without the component: no hooks and no context, so a server component can draw a
5
+ * link the way Link does (ADR 0027).
6
+ */
7
+ export const linkClass = cx(
8
+ "xos-hit inline-flex items-center gap-2 font-medium text-accent-text no-underline",
9
+ "hover:underline hover:underline-offset-(length:--space-2)",
10
+ );
@@ -0,0 +1,3 @@
1
+ # LiveActivity
2
+
3
+ LiveActivity shows the running shift on the lock screen with Break and Clock Out, plus a home screen widget for the next shift.
@@ -0,0 +1,7 @@
1
+ # LogoSlotWall
2
+
3
+ LogoSlotWall shows customer marks, or the empty slots that reserve their place in a design.
4
+
5
+ With no `logos` and no `slots` it renders nothing, so a page ships without it until customers have consented to the use of their marks. `slots` draws empty BrandSlot frames at 160 by 40 for design review only. Marks render through BrandMark with a light and a dark file per theme.
6
+
7
+ The consumer provides each mark with its owner's written consent. Never draw or approximate a mark.
@@ -0,0 +1,5 @@
1
+ # MapView
2
+
3
+ MapView plots venues, zones, geofences and incident pins; production renders on map tiles with the same pin and zone styles.
4
+
5
+ - Every map has a numbered legend as its text alternative.
@@ -0,0 +1,31 @@
1
+ import { type ReactElement } from "react";
2
+ import { Icon } from "../Icon/Icon.tsx";
3
+ import { linkClass } from "../Link/link-class.ts";
4
+ import { marketingCopy } from "./marketing-copy.ts";
5
+ import { type AIDisclosureBadgeProps } from "./Marketing.types.ts";
6
+
7
+ /** Marks a feature as AI, names the provider and links the notice. */
8
+ export function AIDisclosureBadge({
9
+ label,
10
+ provider,
11
+ href,
12
+ linkLabel,
13
+ locale,
14
+ messages,
15
+ }: AIDisclosureBadgeProps): ReactElement {
16
+ const { t } = marketingCopy({ locale, messages });
17
+ return (
18
+ <span className="xos-mkt-ai">
19
+ <span className="xos-mkt-tag">
20
+ <Icon name="Sparkles" size={12} />
21
+ {label ?? t("mk.ai.badge")}
22
+ </span>
23
+ {provider ? <span className="xos-mkt-ai-provider">{provider}</span> : null}
24
+ {href ? (
25
+ <a href={href} className={linkClass}>
26
+ {linkLabel}
27
+ </a>
28
+ ) : null}
29
+ </span>
30
+ );
31
+ }