portal-mojo 0.1.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 (369) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +55 -0
  3. package/docs/DetailView.md +173 -0
  4. package/docs/ModelTable.md +128 -0
  5. package/docs/README.md +98 -0
  6. package/docs/admin-assistant.md +72 -0
  7. package/docs/admin-bouncer.md +120 -0
  8. package/docs/admin-cloudwatch.md +38 -0
  9. package/docs/admin-credentials.md +116 -0
  10. package/docs/admin-dashboard.md +15 -0
  11. package/docs/admin-devices-geoip.md +284 -0
  12. package/docs/admin-dns-certificates.md +122 -0
  13. package/docs/admin-dns-records.md +65 -0
  14. package/docs/admin-dns-registrar.md +33 -0
  15. package/docs/admin-dns.md +80 -0
  16. package/docs/admin-identity-users.md +103 -0
  17. package/docs/admin-incidents.md +71 -0
  18. package/docs/admin-jobs.md +426 -0
  19. package/docs/admin-members.md +112 -0
  20. package/docs/admin-messaging.md +56 -0
  21. package/docs/admin-metrics-explorer.md +63 -0
  22. package/docs/admin-monitoring.md +99 -0
  23. package/docs/admin-network-security.md +393 -0
  24. package/docs/admin-phonehub.md +27 -0
  25. package/docs/admin-push.md +36 -0
  26. package/docs/admin-rules.md +87 -0
  27. package/docs/admin-security-tickets.md +40 -0
  28. package/docs/admin-settings.md +78 -0
  29. package/docs/admin-shortlinks.md +32 -0
  30. package/docs/admin-storage.md +141 -0
  31. package/docs/auth-pages.md +212 -0
  32. package/docs/auth.md +58 -0
  33. package/docs/calendar.md +122 -0
  34. package/docs/charts.md +303 -0
  35. package/docs/client.md +206 -0
  36. package/docs/collection-multiselect.md +117 -0
  37. package/docs/collection-select.md +132 -0
  38. package/docs/combobox.md +105 -0
  39. package/docs/dataview.md +226 -0
  40. package/docs/datepicker.md +151 -0
  41. package/docs/daterange.md +170 -0
  42. package/docs/datetimepicker.md +249 -0
  43. package/docs/defineModel.md +72 -0
  44. package/docs/detail-primitives.md +174 -0
  45. package/docs/feedback.md +85 -0
  46. package/docs/file-upload-ux.md +146 -0
  47. package/docs/forms.md +435 -0
  48. package/docs/grouping-and-fmt.md +98 -0
  49. package/docs/idioms.md +112 -0
  50. package/docs/image-editor.md +101 -0
  51. package/docs/loading.md +242 -0
  52. package/docs/location-address.md +76 -0
  53. package/docs/markdown.md +203 -0
  54. package/docs/menus-and-access.md +89 -0
  55. package/docs/multiselect-dropdown.md +153 -0
  56. package/docs/params.md +73 -0
  57. package/docs/popover.md +130 -0
  58. package/docs/realtime.md +61 -0
  59. package/docs/record-feed.md +238 -0
  60. package/docs/right-panel.md +42 -0
  61. package/docs/tabs-and-form-wizard.md +60 -0
  62. package/docs/taginput.md +120 -0
  63. package/docs/timepicker.md +173 -0
  64. package/docs/timezone-select.md +147 -0
  65. package/docs/worldmap.md +302 -0
  66. package/package.json +55 -0
  67. package/src/admin/assistant/AssistantFeed.tsx +29 -0
  68. package/src/admin/assistant/AssistantPanel.tsx +65 -0
  69. package/src/admin/assistant/api.ts +17 -0
  70. package/src/admin/assistant/data.ts +63 -0
  71. package/src/admin/assistant/index.ts +21 -0
  72. package/src/admin/assistant/launchers.tsx +33 -0
  73. package/src/admin/assistant/pages.tsx +78 -0
  74. package/src/admin/assistant/streaming.ts +238 -0
  75. package/src/admin/assistant/types.ts +30 -0
  76. package/src/admin/bouncer/devices.tsx +230 -0
  77. package/src/admin/bouncer/index.ts +34 -0
  78. package/src/admin/bouncer/models.ts +199 -0
  79. package/src/admin/bouncer/signals.tsx +182 -0
  80. package/src/admin/bouncer/signatures.tsx +220 -0
  81. package/src/admin/cloudwatch/CloudWatchChart.tsx +61 -0
  82. package/src/admin/cloudwatch/CloudWatchDashboardPage.tsx +52 -0
  83. package/src/admin/cloudwatch/CloudWatchResourceDetail.tsx +33 -0
  84. package/src/admin/cloudwatch/data.ts +163 -0
  85. package/src/admin/cloudwatch/index.ts +16 -0
  86. package/src/admin/credentials/group-api-keys.tsx +433 -0
  87. package/src/admin/credentials/index.ts +4 -0
  88. package/src/admin/credentials/models.ts +244 -0
  89. package/src/admin/credentials/secret-dialog.tsx +77 -0
  90. package/src/admin/credentials/webhook-subscriptions.tsx +423 -0
  91. package/src/admin/dashboard/AdminDashboardPage.tsx +101 -0
  92. package/src/admin/dashboard/data.ts +60 -0
  93. package/src/admin/dashboard/index.ts +2 -0
  94. package/src/admin/dns/CertificateDetail.tsx +115 -0
  95. package/src/admin/dns/CertificateLifecyclePoller.tsx +141 -0
  96. package/src/admin/dns/CertificateRequestDialog.tsx +103 -0
  97. package/src/admin/dns/CertificatesPage.tsx +85 -0
  98. package/src/admin/dns/DnsRecordEditor.tsx +110 -0
  99. package/src/admin/dns/DnsRecordsPage.tsx +36 -0
  100. package/src/admin/dns/DnsRecordsPanel.tsx +71 -0
  101. package/src/admin/dns/DomainCertificatesSection.tsx +40 -0
  102. package/src/admin/dns/DomainDetail.tsx +41 -0
  103. package/src/admin/dns/DomainOnboardingDialog.tsx +27 -0
  104. package/src/admin/dns/DomainPurchaseDetail.tsx +11 -0
  105. package/src/admin/dns/DomainPurchaseWizard.tsx +112 -0
  106. package/src/admin/dns/DomainPurchasesPage.tsx +16 -0
  107. package/src/admin/dns/DomainsPage.tsx +27 -0
  108. package/src/admin/dns/ProviderCredentialsPage.tsx +304 -0
  109. package/src/admin/dns/RegistrantContactPage.tsx +42 -0
  110. package/src/admin/dns/api.ts +515 -0
  111. package/src/admin/dns/certificate-data.ts +221 -0
  112. package/src/admin/dns/data.ts +32 -0
  113. package/src/admin/dns/dns-data.ts +266 -0
  114. package/src/admin/dns/dns-integration.ts +24 -0
  115. package/src/admin/dns/domain-links.ts +37 -0
  116. package/src/admin/dns/index.ts +86 -0
  117. package/src/admin/dns/models.ts +418 -0
  118. package/src/admin/dns/purchase-data.ts +67 -0
  119. package/src/admin/dns/registrant-data.ts +52 -0
  120. package/src/admin/identity/index.ts +5 -0
  121. package/src/admin/identity/members/GroupMembersPanel.tsx +146 -0
  122. package/src/admin/identity/members/MemberDetail.tsx +226 -0
  123. package/src/admin/identity/members/MembersPage.tsx +144 -0
  124. package/src/admin/identity/members/index.ts +24 -0
  125. package/src/admin/identity/members/member-flows.tsx +132 -0
  126. package/src/admin/identity/members/models.ts +204 -0
  127. package/src/admin/identity/users/UserDetail.tsx +257 -0
  128. package/src/admin/identity/users/UsersPage.tsx +163 -0
  129. package/src/admin/identity/users/index.ts +27 -0
  130. package/src/admin/identity/users/models.ts +232 -0
  131. package/src/admin/identity/users/sections/ApiKeysSection.tsx +119 -0
  132. package/src/admin/identity/users/sections/AuditSection.tsx +190 -0
  133. package/src/admin/identity/users/sections/DevicesSection.tsx +140 -0
  134. package/src/admin/identity/users/sections/GroupsSection.tsx +64 -0
  135. package/src/admin/identity/users/sections/LoginsSection.tsx +106 -0
  136. package/src/admin/identity/users/sections/MetadataPanel.tsx +36 -0
  137. package/src/admin/identity/users/sections/NotificationsSection.tsx +117 -0
  138. package/src/admin/identity/users/sections/OAuthSection.tsx +72 -0
  139. package/src/admin/identity/users/sections/OverviewSection.tsx +109 -0
  140. package/src/admin/identity/users/sections/PermissionsSection.tsx +35 -0
  141. package/src/admin/identity/users/sections/PersonalSection.tsx +73 -0
  142. package/src/admin/identity/users/sections/ProfileSection.tsx +175 -0
  143. package/src/admin/identity/users/sections/SecuritySection.tsx +123 -0
  144. package/src/admin/identity/users/sections/actions.tsx +752 -0
  145. package/src/admin/identity/users/sections/permission-catalog.ts +138 -0
  146. package/src/admin/identity/users/sections/queries.ts +74 -0
  147. package/src/admin/identity/users/sections/shared.tsx +299 -0
  148. package/src/admin/incidents/EventDetail.tsx +43 -0
  149. package/src/admin/incidents/EventsPage.tsx +52 -0
  150. package/src/admin/incidents/IncidentDetail.tsx +68 -0
  151. package/src/admin/incidents/IncidentsPage.tsx +102 -0
  152. package/src/admin/incidents/forensics.tsx +73 -0
  153. package/src/admin/incidents/index.ts +16 -0
  154. package/src/admin/incidents/models.ts +113 -0
  155. package/src/admin/incidents/sanitize.ts +71 -0
  156. package/src/admin/index.ts +383 -0
  157. package/src/admin/jobs/JobDashboardPage.tsx +111 -0
  158. package/src/admin/jobs/JobDetail.tsx +721 -0
  159. package/src/admin/jobs/JobRunnersPage.tsx +216 -0
  160. package/src/admin/jobs/JobsTablePage.tsx +122 -0
  161. package/src/admin/jobs/RunnerDetail.tsx +576 -0
  162. package/src/admin/jobs/ScheduledTaskDetail.tsx +421 -0
  163. package/src/admin/jobs/ScheduledTasksPage.tsx +167 -0
  164. package/src/admin/jobs/columns.tsx +297 -0
  165. package/src/admin/jobs/control.ts +488 -0
  166. package/src/admin/jobs/index.ts +85 -0
  167. package/src/admin/jobs/models.ts +599 -0
  168. package/src/admin/jobs/queries.ts +272 -0
  169. package/src/admin/jobs/scheduled-task-form.tsx +493 -0
  170. package/src/admin/jobs/sections/JobChannelsPanel.tsx +142 -0
  171. package/src/admin/jobs/sections/JobOperationsSection.tsx +423 -0
  172. package/src/admin/jobs/sections/JobRunnersStrip.tsx +82 -0
  173. package/src/admin/jobs/sections/JobThroughputSection.tsx +118 -0
  174. package/src/admin/messaging/EmailDomainWizard.tsx +24 -0
  175. package/src/admin/messaging/EmailDomainsPage.tsx +17 -0
  176. package/src/admin/messaging/EmailTemplatesPage.tsx +13 -0
  177. package/src/admin/messaging/MailboxesPage.tsx +15 -0
  178. package/src/admin/messaging/PublicMessagesPage.tsx +13 -0
  179. package/src/admin/messaging/SandboxedEmailPreview.tsx +23 -0
  180. package/src/admin/messaging/SentMessagesPage.tsx +11 -0
  181. package/src/admin/messaging/api.ts +17 -0
  182. package/src/admin/messaging/data.ts +10 -0
  183. package/src/admin/messaging/index.ts +23 -0
  184. package/src/admin/messaging/models.ts +76 -0
  185. package/src/admin/messaging/push/PushPage.tsx +44 -0
  186. package/src/admin/messaging/push/api.ts +17 -0
  187. package/src/admin/messaging/push/index.ts +9 -0
  188. package/src/admin/messaging/push/models.ts +43 -0
  189. package/src/admin/messaging/sanitize.ts +44 -0
  190. package/src/admin/monitoring/LogInspector.tsx +160 -0
  191. package/src/admin/monitoring/LogsPage.tsx +135 -0
  192. package/src/admin/monitoring/MetricsExplorerPage.tsx +360 -0
  193. package/src/admin/monitoring/MetricsPermissionsPage.tsx +166 -0
  194. package/src/admin/monitoring/MetricsSourcePicker.tsx +136 -0
  195. package/src/admin/monitoring/index.ts +55 -0
  196. package/src/admin/monitoring/metrics-explorer-client.ts +229 -0
  197. package/src/admin/monitoring/metrics-explorer-data.ts +158 -0
  198. package/src/admin/monitoring/models.ts +155 -0
  199. package/src/admin/network/BlockedIPsPage.tsx +194 -0
  200. package/src/admin/network/BlockedIpDetail.tsx +262 -0
  201. package/src/admin/network/FirewallLogPage.tsx +115 -0
  202. package/src/admin/network/IPSetDetail.tsx +280 -0
  203. package/src/admin/network/IPSetEditor.tsx +191 -0
  204. package/src/admin/network/IPSetsPage.tsx +177 -0
  205. package/src/admin/network/geofence/BlocksTab.tsx +289 -0
  206. package/src/admin/network/geofence/ExemptionsTab.tsx +349 -0
  207. package/src/admin/network/geofence/GeofencingPage.tsx +119 -0
  208. package/src/admin/network/geofence/PostureHeader.tsx +184 -0
  209. package/src/admin/network/geofence/RuleEditor.tsx +176 -0
  210. package/src/admin/network/geofence/RulesTab.tsx +254 -0
  211. package/src/admin/network/geofence/SimulatorTab.tsx +281 -0
  212. package/src/admin/network/geofence/geofence-data.ts +674 -0
  213. package/src/admin/network/index.ts +68 -0
  214. package/src/admin/network/models.ts +607 -0
  215. package/src/admin/phonehub/PhoneHubPage.tsx +57 -0
  216. package/src/admin/phonehub/api.ts +47 -0
  217. package/src/admin/phonehub/data.ts +6 -0
  218. package/src/admin/phonehub/index.ts +10 -0
  219. package/src/admin/phonehub/models.ts +68 -0
  220. package/src/admin/rules/HandlerChainBuilder.tsx +51 -0
  221. package/src/admin/rules/RuleSetDetailPage.tsx +33 -0
  222. package/src/admin/rules/RuleSetsPage.tsx +33 -0
  223. package/src/admin/rules/editors.tsx +54 -0
  224. package/src/admin/rules/handler-dsl.ts +213 -0
  225. package/src/admin/rules/index.ts +13 -0
  226. package/src/admin/rules/models.ts +112 -0
  227. package/src/admin/security/devices/LoginEventDetail.tsx +123 -0
  228. package/src/admin/security/devices/LoginLocationMap.tsx +321 -0
  229. package/src/admin/security/devices/LoginLocationsPage.tsx +162 -0
  230. package/src/admin/security/devices/UserDeviceDetail.tsx +546 -0
  231. package/src/admin/security/devices/UserDeviceLocationDetail.tsx +272 -0
  232. package/src/admin/security/devices/UserDevicesPage.tsx +84 -0
  233. package/src/admin/security/devices/index.ts +40 -0
  234. package/src/admin/security/devices/models.ts +470 -0
  235. package/src/admin/security/geoip/GeoIpCachePage.tsx +135 -0
  236. package/src/admin/security/geoip/GeoIpDossier.tsx +804 -0
  237. package/src/admin/security/geoip/geoip-forms.ts +102 -0
  238. package/src/admin/security/geoip/index.ts +27 -0
  239. package/src/admin/security/geoip/models.ts +397 -0
  240. package/src/admin/security/index.ts +31 -0
  241. package/src/admin/security/models.ts +137 -0
  242. package/src/admin/security/tickets.tsx +562 -0
  243. package/src/admin/security-permissions.ts +5 -0
  244. package/src/admin/settings/SettingDetail.tsx +57 -0
  245. package/src/admin/settings/SettingEditor.tsx +157 -0
  246. package/src/admin/settings/SettingsPage.tsx +72 -0
  247. package/src/admin/settings/index.ts +19 -0
  248. package/src/admin/settings/model.ts +143 -0
  249. package/src/admin/shortlinks/ShortlinkDetail.tsx +55 -0
  250. package/src/admin/shortlinks/ShortlinkHistoryPage.tsx +18 -0
  251. package/src/admin/shortlinks/ShortlinksPage.tsx +59 -0
  252. package/src/admin/shortlinks/index.ts +18 -0
  253. package/src/admin/shortlinks/models.ts +246 -0
  254. package/src/admin/storage/BackendsPage.tsx +43 -0
  255. package/src/admin/storage/BucketDetail.tsx +77 -0
  256. package/src/admin/storage/BucketsPage.tsx +64 -0
  257. package/src/admin/storage/FileManagerDetail.tsx +36 -0
  258. package/src/admin/storage/FilePreview.tsx +54 -0
  259. package/src/admin/storage/FileUploadSurface.tsx +250 -0
  260. package/src/admin/storage/FileView.tsx +109 -0
  261. package/src/admin/storage/FilesPage.tsx +53 -0
  262. package/src/admin/storage/file-renditions.ts +99 -0
  263. package/src/admin/storage/index.ts +31 -0
  264. package/src/admin/storage/models.ts +585 -0
  265. package/src/admin/storage/storage-dialogs.tsx +80 -0
  266. package/src/charts/CircularProgress.tsx +281 -0
  267. package/src/charts/KPIStrip.tsx +162 -0
  268. package/src/charts/KPITile.tsx +125 -0
  269. package/src/charts/MetricsChart.tsx +459 -0
  270. package/src/charts/MetricsMiniWidget.tsx +588 -0
  271. package/src/charts/MiniChart.tsx +283 -0
  272. package/src/charts/PieChart.tsx +354 -0
  273. package/src/charts/SeriesChart.tsx +378 -0
  274. package/src/charts/chart-dialogs.tsx +149 -0
  275. package/src/charts/exportChart.ts +144 -0
  276. package/src/charts/index.ts +28 -0
  277. package/src/charts/pie-math.ts +127 -0
  278. package/src/charts/stats.ts +265 -0
  279. package/src/charts/worldmap/WorldMap.tsx +649 -0
  280. package/src/charts/worldmap/countryCentroids.ts +304 -0
  281. package/src/charts/worldmap/geo.ts +293 -0
  282. package/src/charts/worldmap/use-world-land.ts +47 -0
  283. package/src/charts/worldmap/world-land.ts +18 -0
  284. package/src/charts/worldmap/worldmap-data.ts +285 -0
  285. package/src/client/auth.ts +737 -0
  286. package/src/client/client.ts +268 -0
  287. package/src/client/duid.ts +31 -0
  288. package/src/client/errors.ts +33 -0
  289. package/src/client/group-context.ts +28 -0
  290. package/src/client/group.tsx +126 -0
  291. package/src/client/hooks.ts +50 -0
  292. package/src/client/index.ts +37 -0
  293. package/src/client/jwt.ts +75 -0
  294. package/src/client/location.ts +285 -0
  295. package/src/client/lookups.ts +41 -0
  296. package/src/client/markdown-fallback.tsx +115 -0
  297. package/src/client/markdown-parse.ts +386 -0
  298. package/src/client/markdown-sanitize.ts +217 -0
  299. package/src/client/markdown.ts +122 -0
  300. package/src/client/me.ts +188 -0
  301. package/src/client/mock.ts +9984 -0
  302. package/src/client/model.ts +227 -0
  303. package/src/client/params.ts +292 -0
  304. package/src/client/realtime-mock.ts +221 -0
  305. package/src/client/realtime.ts +561 -0
  306. package/src/client/record-feed.ts +329 -0
  307. package/src/client/safe-export.ts +71 -0
  308. package/src/client/types.ts +278 -0
  309. package/src/client/upload.ts +421 -0
  310. package/src/ui/AddressField.tsx +273 -0
  311. package/src/ui/AttachmentQueue.tsx +92 -0
  312. package/src/ui/CollectionMultiSelect.tsx +622 -0
  313. package/src/ui/CollectionSelect.tsx +517 -0
  314. package/src/ui/ComboBox.tsx +336 -0
  315. package/src/ui/DetailView.tsx +438 -0
  316. package/src/ui/FileDrop.tsx +194 -0
  317. package/src/ui/FileField.tsx +369 -0
  318. package/src/ui/FilterBar.tsx +269 -0
  319. package/src/ui/FormFields.tsx +77 -0
  320. package/src/ui/FormView.tsx +360 -0
  321. package/src/ui/FormWizard.tsx +336 -0
  322. package/src/ui/GroupSwitcher.tsx +202 -0
  323. package/src/ui/Guarded.tsx +16 -0
  324. package/src/ui/MarkdownView.tsx +89 -0
  325. package/src/ui/ModelTable.tsx +848 -0
  326. package/src/ui/MultiSelectDropdown.tsx +390 -0
  327. package/src/ui/Popover.tsx +204 -0
  328. package/src/ui/RecordFeed.tsx +627 -0
  329. package/src/ui/RequiresGroup.tsx +32 -0
  330. package/src/ui/RightPanel.tsx +107 -0
  331. package/src/ui/SidebarNav.tsx +330 -0
  332. package/src/ui/Tabs.tsx +161 -0
  333. package/src/ui/TagInput.tsx +381 -0
  334. package/src/ui/ThemeProvider.tsx +52 -0
  335. package/src/ui/UploadQueue.tsx +603 -0
  336. package/src/ui/armed-button.tsx +91 -0
  337. package/src/ui/date/Calendar.tsx +498 -0
  338. package/src/ui/date/DatePicker.tsx +289 -0
  339. package/src/ui/date/DateRangePicker.tsx +280 -0
  340. package/src/ui/date/DateTimePicker.tsx +587 -0
  341. package/src/ui/date/PresetRail.tsx +165 -0
  342. package/src/ui/date/TimePicker.tsx +624 -0
  343. package/src/ui/date/TimezoneSelect.tsx +267 -0
  344. package/src/ui/date/fns.ts +679 -0
  345. package/src/ui/detail/DataView.tsx +692 -0
  346. package/src/ui/detail/FlowStrip.tsx +80 -0
  347. package/src/ui/detail/KnownFieldsCard.tsx +121 -0
  348. package/src/ui/detail/MetadataSection.tsx +219 -0
  349. package/src/ui/detail/StackTraceView.tsx +150 -0
  350. package/src/ui/detail/StatusPanel.tsx +73 -0
  351. package/src/ui/detail/Timeline.tsx +52 -0
  352. package/src/ui/detail/index.ts +12 -0
  353. package/src/ui/field-registry.tsx +428 -0
  354. package/src/ui/field-wire.ts +322 -0
  355. package/src/ui/form-autosave.ts +583 -0
  356. package/src/ui/format.ts +444 -0
  357. package/src/ui/grouping.ts +150 -0
  358. package/src/ui/image-editor/ImageEditor.tsx +484 -0
  359. package/src/ui/image-editor/canvas.ts +254 -0
  360. package/src/ui/image-editor/index.ts +3 -0
  361. package/src/ui/image-editor/math.ts +416 -0
  362. package/src/ui/index.ts +58 -0
  363. package/src/ui/loading.tsx +604 -0
  364. package/src/ui/menu-registry.ts +213 -0
  365. package/src/ui/modal.tsx +297 -0
  366. package/src/ui/password.tsx +302 -0
  367. package/src/ui/schema-form-core.tsx +315 -0
  368. package/src/ui/toast.tsx +219 -0
  369. package/src/ui/ui.tsx +45 -0
@@ -0,0 +1,219 @@
1
+ // Toast manager — the ToastService API shape (success/error/info/warning) on
2
+ // a small external store, plus the two maestro idioms ported from
3
+ // workspaces/js/dom.js:
4
+ // · toast.undo / undoToast (dom.js:84-103) — act IMMEDIATELY, offer Undo
5
+ // for a grace window (replaces confirm() for reversible actions). Undo
6
+ // dismisses at once and runs the handler; otherwise auto-dismisses.
7
+ // · toast.progress / progressToast (dom.js:41-79) — persistent card with a
8
+ // bar. Handle: update(0..100) / finalizing(msg?) / done(msg?) /
9
+ // fail(msg?) / remove();
10
+ // optional onCancel renders a ✕ that ABORTS the caller's operation (the
11
+ // caller then calls fail/remove — cancel itself never dismisses).
12
+ //
13
+ // Basic toasts keep the original behavior byte-for-byte: 3.5s life, newest
14
+ // last, capped at 5. The cap deliberately does NOT count undo/progress cards
15
+ // — evicting a live progress bar because five saves toasted would orphan an
16
+ // in-flight operation's only indicator.
17
+ //
18
+ // Visual spec from web-mojo's toast.css (left accent bar, compact card,
19
+ // bottom-right); idiom styles live in the app's theme/idioms.css — tokens
20
+ // only, both themes.
21
+ import { useSyncExternalStore } from 'react';
22
+
23
+ type Level = 'success' | 'error' | 'info' | 'warning';
24
+
25
+ interface BasicToast { id: number; kind: 'basic'; level: Level; message: string }
26
+ interface UndoToastItem { id: number; kind: 'undo'; message: string; onUndo?: () => void }
27
+ interface ProgressToastItem {
28
+ id: number;
29
+ kind: 'progress';
30
+ label: string;
31
+ pct: number;
32
+ state: 'active' | 'finalizing' | 'done' | 'error';
33
+ onCancel?: () => void;
34
+ }
35
+ type ToastItem = BasicToast | UndoToastItem | ProgressToastItem;
36
+
37
+ let nextId = 1;
38
+ let toasts: ToastItem[] = [];
39
+ const listeners = new Set<() => void>();
40
+ const emit = () => listeners.forEach((fn) => fn());
41
+
42
+ function remove(id: number) {
43
+ if (!toasts.some((t) => t.id === id)) return;
44
+ toasts = toasts.filter((t) => t.id !== id);
45
+ emit();
46
+ }
47
+
48
+ /** Immutable in-place update — the store array is replaced so React sees it. */
49
+ function patch(id: number, changes: Partial<ProgressToastItem>) {
50
+ toasts = toasts.map((t) => (t.id === id ? ({ ...t, ...changes } as ToastItem) : t));
51
+ emit();
52
+ }
53
+
54
+ function push(level: Level, message: string) {
55
+ const id = nextId++;
56
+ const basics = toasts.filter((t) => t.kind === 'basic');
57
+ // Cap BASIC toasts at 5 (original behavior); persistent cards are exempt.
58
+ const evict = basics.length >= 5 ? basics[0]!.id : null;
59
+ toasts = [...toasts.filter((t) => t.id !== evict), { id, kind: 'basic', level, message }];
60
+ emit();
61
+ setTimeout(() => remove(id), 3500);
62
+ }
63
+
64
+ export interface UndoToastHandle { dismiss: () => void }
65
+
66
+ /**
67
+ * Act-then-offer-Undo (dom.js undoToast port). The ACTION HAS ALREADY
68
+ * HAPPENED when this shows — clicking Undo dismisses and calls `onUndo`;
69
+ * otherwise the card auto-dismisses after `timeout` ms and the action
70
+ * stands. Returns { dismiss } for callers that resolve the ambiguity early.
71
+ */
72
+ export function undoToast(message: string, onUndo?: () => void, opts: { timeout?: number } = {}): UndoToastHandle {
73
+ const id = nextId++;
74
+ toasts = [...toasts, { id, kind: 'undo', message, onUndo }];
75
+ emit();
76
+ const timer = setTimeout(() => remove(id), opts.timeout ?? 6000);
77
+ return {
78
+ dismiss: () => { clearTimeout(timer); remove(id); },
79
+ };
80
+ }
81
+
82
+ export interface ProgressToastHandle {
83
+ /** Set the bar, clamped+rounded to 0–100. */
84
+ update: (pct: number) => void;
85
+ /** 100% + non-cancellable finalization state; the operation is not settled yet. */
86
+ finalizing: (message?: string) => void;
87
+ /** 100% + ✓, optional label swap, auto-removes after 1.4s. */
88
+ done: (message?: string) => void;
89
+ /** Error styling + ✕, optional label swap, auto-removes after 5s. */
90
+ fail: (message?: string) => void;
91
+ /** Immediate removal (e.g. after the caller aborted on cancel). */
92
+ remove: () => void;
93
+ }
94
+
95
+ /**
96
+ * Persistent progress card (dom.js progressToast port). Never auto-dismisses
97
+ * while active — the caller drives it to done()/fail()/remove(). Pass
98
+ * `onCancel` to render a ✕ that calls it; cancel ABORTS the operation on the
99
+ * caller's side and the caller then settles the toast.
100
+ */
101
+ export function progressToast(label: string, opts: { onCancel?: () => void } = {}): ProgressToastHandle {
102
+ const id = nextId++;
103
+ toasts = [...toasts, { id, kind: 'progress', label, pct: 0, state: 'active', onCancel: opts.onCancel }];
104
+ emit();
105
+ let settled = false;
106
+ return {
107
+ update: (pct: number) => {
108
+ if (settled) return;
109
+ patch(id, { pct: Math.max(0, Math.min(100, Math.round(pct || 0))) });
110
+ },
111
+ finalizing: (message?: string) => {
112
+ if (settled) return;
113
+ patch(id, { pct: 100, state: 'finalizing', ...(message ? { label: message } : {}), onCancel: undefined });
114
+ },
115
+ done: (message?: string) => {
116
+ if (settled) return;
117
+ settled = true;
118
+ patch(id, { pct: 100, state: 'done', ...(message ? { label: message } : {}), onCancel: undefined });
119
+ setTimeout(() => remove(id), 1400);
120
+ },
121
+ fail: (message?: string) => {
122
+ if (settled) return;
123
+ settled = true;
124
+ patch(id, { state: 'error', ...(message ? { label: message } : {}), onCancel: undefined });
125
+ setTimeout(() => remove(id), 5000);
126
+ },
127
+ remove: () => { settled = true; remove(id); },
128
+ };
129
+ }
130
+
131
+ export const toast = {
132
+ success: (m: string) => push('success', m),
133
+ error: (m: string) => push('error', m),
134
+ info: (m: string) => push('info', m),
135
+ /** Partial outcomes (batch "N succeeded, M failed"). */
136
+ warning: (m: string) => push('warning', m),
137
+ /** Act immediately, offer a grace-window Undo. See undoToast(). */
138
+ undo: undoToast,
139
+ /** Persistent progress card with a bar. See progressToast(). */
140
+ progress: progressToast,
141
+ };
142
+
143
+ const ICONS: Record<Level, string> = {
144
+ success: 'bi-check-circle-fill',
145
+ error: 'bi-x-circle-fill',
146
+ info: 'bi-info-circle-fill',
147
+ warning: 'bi-exclamation-triangle-fill',
148
+ };
149
+
150
+ function ProgressCard({ t }: { t: ProgressToastItem }) {
151
+ return (
152
+ <div className={`toast-card toast-progress${t.state === 'finalizing' ? ' is-finalizing' : ''}${t.state === 'done' ? ' is-done' : ''}${t.state === 'error' ? ' toast-error is-failed' : ''}`}>
153
+ <div className="progress-head">
154
+ <div className="progress-name" title={t.label}>{t.label}</div>
155
+ {t.onCancel && t.state === 'active' && (
156
+ <button
157
+ type="button"
158
+ className="progress-cancel"
159
+ title="Cancel"
160
+ aria-label="Cancel"
161
+ onClick={() => t.onCancel?.()}
162
+ >
163
+ <i className="bi bi-x-lg" />
164
+ </button>
165
+ )}
166
+ </div>
167
+ <div className="progress-row">
168
+ <div
169
+ className="progress-bar"
170
+ role="progressbar"
171
+ aria-valuenow={t.pct}
172
+ aria-valuemin={0}
173
+ aria-valuemax={100}
174
+ >
175
+ <div className="progress-fill" style={{ width: `${t.pct}%` }} />
176
+ </div>
177
+ <span className="progress-pct">
178
+ {t.state === 'done' ? <i className="bi bi-check-lg" /> : t.state === 'error' ? <i className="bi bi-x-lg" /> : t.state === 'finalizing' ? <i className="bi bi-arrow-repeat spin" /> : `${t.pct}%`}
179
+ </span>
180
+ </div>
181
+ </div>
182
+ );
183
+ }
184
+
185
+ export function ToastHost() {
186
+ const items = useSyncExternalStore(
187
+ (cb) => { listeners.add(cb); return () => listeners.delete(cb); },
188
+ () => toasts,
189
+ );
190
+ return (
191
+ <div className="toast-host" role="status" aria-live="polite">
192
+ {items.map((t) => {
193
+ if (t.kind === 'basic') {
194
+ return (
195
+ <div key={t.id} className={`toast-card toast-${t.level}`}>
196
+ <i className={`bi ${ICONS[t.level]}`} />
197
+ <span>{t.message}</span>
198
+ </div>
199
+ );
200
+ }
201
+ if (t.kind === 'undo') {
202
+ return (
203
+ <div key={t.id} className="toast-card toast-undo">
204
+ <span className="undo-msg">{t.message}</span>
205
+ <button
206
+ type="button"
207
+ className="undo-btn"
208
+ onClick={() => { remove(t.id); t.onUndo?.(); }}
209
+ >
210
+ Undo
211
+ </button>
212
+ </div>
213
+ );
214
+ }
215
+ return <ProgressCard key={t.id} t={t} />;
216
+ })}
217
+ </div>
218
+ );
219
+ }
package/src/ui/ui.tsx ADDED
@@ -0,0 +1,45 @@
1
+ // Small display primitives: Badge (the HTML-returning formatters become
2
+ // components), MetricCard (flat monochrome KPI), Spark (dependency-free SVG
3
+ // mini chart — the chartlib porting pattern in miniature).
4
+ import type { ReactNode } from 'react';
5
+ import { inferTone, type Tone } from './format';
6
+
7
+ export function Badge({ tone, children }: { tone?: Tone; children: ReactNode }) {
8
+ const t = tone ?? (typeof children === 'string' || typeof children === 'boolean' ? inferTone(children) : 'muted');
9
+ return <span className={`chip chip-${t}`}>{children === true ? 'Yes' : children === false ? 'No' : children}</span>;
10
+ }
11
+
12
+ export function MetricCard({ label, value, hint, icon }: { label: string; value: ReactNode; hint?: string; icon?: string }) {
13
+ return (
14
+ <div className="metric">
15
+ <div className="metric-top">
16
+ <span className="metric-label">{label}</span>
17
+ {icon && <i className={`bi ${icon} metric-icon`} />}
18
+ </div>
19
+ <div className="metric-value">{value}</div>
20
+ {hint && <div className="metric-hint">{hint}</div>}
21
+ </div>
22
+ );
23
+ }
24
+
25
+ /** Inline SVG sparkline with area fill and emphasized endpoint. */
26
+ export function Spark({ data, height = 48 }: { data: number[]; height?: number }) {
27
+ if (data.length < 2) return null;
28
+ const w = 100;
29
+ const max = Math.max(...data, 1);
30
+ const min = Math.min(...data, 0);
31
+ const span = max - min || 1;
32
+ const pts = data.map((v, i) => [
33
+ (i / (data.length - 1)) * w,
34
+ height - 4 - ((v - min) / span) * (height - 10),
35
+ ]);
36
+ const line = pts.map(([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`).join(' ');
37
+ // No endpoint circle: preserveAspectRatio="none" would stretch it into a
38
+ // blob. The stroke stays crisp via vector-effect: non-scaling-stroke.
39
+ return (
40
+ <svg viewBox={`0 0 ${w} ${height}`} className="spark" preserveAspectRatio="none" role="img" aria-label="Trend">
41
+ <polyline points={`0,${height} ${line} ${w},${height}`} className="spark-fill" />
42
+ <polyline points={line} className="spark-line" />
43
+ </svg>
44
+ );
45
+ }