@antelopejs/dms 0.9.8 → 0.11.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 (451) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +1 -1
  3. package/data/module-store/catalog.json +2 -2
  4. package/dist/attachments/table-view.js +2 -2
  5. package/dist/attachments/table-view.js.map +1 -1
  6. package/dist/automation/actions.d.ts +2 -0
  7. package/dist/automation/actions.js +116 -71
  8. package/dist/automation/actions.js.map +1 -1
  9. package/dist/automation/i18n.d.ts +4 -0
  10. package/dist/automation/i18n.js +19 -0
  11. package/dist/automation/i18n.js.map +1 -0
  12. package/dist/automation/send-notification.d.ts +21 -0
  13. package/dist/automation/send-notification.js +163 -0
  14. package/dist/automation/send-notification.js.map +1 -0
  15. package/dist/automation/triggers.js +19 -25
  16. package/dist/automation/triggers.js.map +1 -1
  17. package/dist/client-origin-cors.d.ts +1 -0
  18. package/dist/client-origin-cors.js +19 -0
  19. package/dist/client-origin-cors.js.map +1 -0
  20. package/dist/config.d.ts +3 -2
  21. package/dist/config.js +5 -7
  22. package/dist/config.js.map +1 -1
  23. package/dist/frontend-module.d.ts +4 -0
  24. package/dist/frontend-module.js +37 -0
  25. package/dist/frontend-module.js.map +1 -0
  26. package/dist/implementations/dms/client-ip.js +1 -1
  27. package/dist/implementations/dms/client-ip.js.map +1 -1
  28. package/dist/implementations/dms/dev-reload.d.ts +5 -0
  29. package/dist/implementations/dms/dev-reload.js +23 -3
  30. package/dist/implementations/dms/dev-reload.js.map +1 -1
  31. package/dist/implementations/dms/frontend-modules.d.ts +2 -4
  32. package/dist/implementations/dms/frontend-modules.js +38 -94
  33. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  34. package/dist/implementations/dms/frontend-ready.d.ts +12 -2
  35. package/dist/implementations/dms/frontend-ready.js +34 -4
  36. package/dist/implementations/dms/frontend-ready.js.map +1 -1
  37. package/dist/implementations/dms/layout-banners.d.ts +2 -1
  38. package/dist/implementations/dms/layout-banners.js +9 -0
  39. package/dist/implementations/dms/layout-banners.js.map +1 -1
  40. package/dist/implementations/dms/manifest.d.ts +7 -2
  41. package/dist/implementations/dms/manifest.js +12 -7
  42. package/dist/implementations/dms/manifest.js.map +1 -1
  43. package/dist/implementations/dms/page.d.ts +4 -3
  44. package/dist/implementations/dms/page.js +49 -18
  45. package/dist/implementations/dms/page.js.map +1 -1
  46. package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
  47. package/dist/implementations/dms/permissions-resolver.js +2 -2
  48. package/dist/implementations/dms/permissions-resolver.js.map +1 -1
  49. package/dist/implementations/dms/realtime.js +1 -0
  50. package/dist/implementations/dms/realtime.js.map +1 -1
  51. package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
  52. package/dist/implementations/dms-base/bulk-selection.js +23 -1
  53. package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
  54. package/dist/implementations/dms-base/filter-tokens.js +5 -3
  55. package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
  56. package/dist/implementations/dms-base/search-route.d.ts +1 -0
  57. package/dist/implementations/dms-base/search-route.js +35 -13
  58. package/dist/implementations/dms-base/search-route.js.map +1 -1
  59. package/dist/implementations/dms-base/table-view.js +11 -6
  60. package/dist/implementations/dms-base/table-view.js.map +1 -1
  61. package/dist/implementations/dms-notifications/index.d.ts +2 -2
  62. package/dist/implementations/dms-notifications/index.js +5 -5
  63. package/dist/implementations/dms-notifications/index.js.map +1 -1
  64. package/dist/index.js +32 -55
  65. package/dist/index.js.map +1 -1
  66. package/dist/pages/modules/index.d.ts +1 -0
  67. package/dist/pages/modules/index.js +1 -0
  68. package/dist/pages/modules/index.js.map +1 -1
  69. package/dist/pages/modules/modules-store.d.ts +9 -0
  70. package/dist/pages/modules/modules-store.js +30 -0
  71. package/dist/pages/modules/modules-store.js.map +1 -0
  72. package/dist/pages/settings/users/account-credentials.js +2 -2
  73. package/dist/pages/settings/users/account-credentials.js.map +1 -1
  74. package/dist/pages/settings/users/account-data-store.js +25 -8
  75. package/dist/pages/settings/users/account-data-store.js.map +1 -1
  76. package/dist/pages/settings/users/invite-extension-routes.js +9 -3
  77. package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
  78. package/dist/pages/settings/users/member-management.js +13 -14
  79. package/dist/pages/settings/users/member-management.js.map +1 -1
  80. package/dist/pages/settings/users/profile.d.ts +1 -4
  81. package/dist/pages/settings/users/profile.js +0 -35
  82. package/dist/pages/settings/users/profile.js.map +1 -1
  83. package/dist/pages/settings/users/role-editor-store.js +26 -18
  84. package/dist/pages/settings/users/role-editor-store.js.map +1 -1
  85. package/dist/pages/settings/users/role-editor.d.ts +0 -1
  86. package/dist/pages/settings/users/role-editor.js +1 -2
  87. package/dist/pages/settings/users/role-editor.js.map +1 -1
  88. package/dist/pages/settings/users/role-preview.js +1 -2
  89. package/dist/pages/settings/users/role-preview.js.map +1 -1
  90. package/dist/pages/settings/users/roles.js +2 -2
  91. package/dist/pages/settings/users/roles.js.map +1 -1
  92. package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
  93. package/dist/realtime/broker.d.ts +6 -0
  94. package/dist/realtime/broker.js +8 -1
  95. package/dist/realtime/broker.js.map +1 -1
  96. package/dist/realtime/presence.d.ts +8 -3
  97. package/dist/realtime/presence.js +8 -4
  98. package/dist/realtime/presence.js.map +1 -1
  99. package/dist/realtime/sessions.d.ts +11 -1
  100. package/dist/realtime/sessions.js +21 -3
  101. package/dist/realtime/sessions.js.map +1 -1
  102. package/dist/realtime/table-view.d.ts +2 -2
  103. package/dist/realtime/table-view.js +4 -3
  104. package/dist/realtime/table-view.js.map +1 -1
  105. package/dist/routes/auth/client-ip.d.ts +1 -0
  106. package/dist/routes/auth/client-ip.js +18 -0
  107. package/dist/routes/auth/client-ip.js.map +1 -1
  108. package/dist/routes/auth/oauth/config.d.ts +2 -1
  109. package/dist/routes/auth/oauth/config.js +5 -1
  110. package/dist/routes/auth/oauth/config.js.map +1 -1
  111. package/dist/routes/auth/session-handoff.d.ts +0 -1
  112. package/dist/routes/auth/session-handoff.js +0 -2
  113. package/dist/routes/auth/session-handoff.js.map +1 -1
  114. package/dist/routes/auth.js +3 -2
  115. package/dist/routes/auth.js.map +1 -1
  116. package/dist/routes/index.d.ts +0 -1
  117. package/dist/routes/index.js +0 -1
  118. package/dist/routes/index.js.map +1 -1
  119. package/dist/routes/realtime.d.ts +13 -1
  120. package/dist/routes/realtime.js +114 -29
  121. package/dist/routes/realtime.js.map +1 -1
  122. package/dist/startup-config.d.ts +2 -0
  123. package/dist/startup-config.js +62 -0
  124. package/dist/startup-config.js.map +1 -0
  125. package/dist/store/interfaces.d.ts +3 -2
  126. package/dist/store/interfaces.js +16 -6
  127. package/dist/store/interfaces.js.map +1 -1
  128. package/dist/store/listing.js +25 -15
  129. package/dist/store/listing.js.map +1 -1
  130. package/dist/store/operations.js +5 -2
  131. package/dist/store/operations.js.map +1 -1
  132. package/dist/store/requirements.js +3 -2
  133. package/dist/store/requirements.js.map +1 -1
  134. package/dist/store/runtime.d.ts +10 -1
  135. package/dist/store/runtime.js +33 -6
  136. package/dist/store/runtime.js.map +1 -1
  137. package/dist/store/types.d.ts +10 -0
  138. package/dist/utils/ensure-default-tenant.d.ts +2 -1
  139. package/dist/utils/ensure-default-tenant.js +13 -1
  140. package/dist/utils/ensure-default-tenant.js.map +1 -1
  141. package/dist/utils/export-jobs.js +2 -1
  142. package/dist/utils/export-jobs.js.map +1 -1
  143. package/dist/validation/auth.schema.d.ts +2 -2
  144. package/dist/validation/member-invite.schema.d.ts +2 -10
  145. package/dist/validation/member-invite.schema.js +2 -15
  146. package/dist/validation/member-invite.schema.js.map +1 -1
  147. package/frontend-vue/dms.frontend.ts +24 -5
  148. package/frontend-vue/dms.server.ts +17 -0
  149. package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
  150. package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +33 -0
  151. package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
  152. package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
  153. package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
  154. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
  155. package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
  156. package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
  157. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
  158. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
  159. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
  160. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
  161. package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
  162. package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +25 -5
  163. package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +3 -5
  164. package/frontend-vue/layers/dms-core/app/composables/useFieldErrors.ts +61 -26
  165. package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
  166. package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
  167. package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
  168. package/frontend-vue/layers/dms-core/app/utils/formatter.ts +134 -35
  169. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
  170. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +136 -2
  171. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +136 -2
  172. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +0 -2
  173. package/frontend-vue/layers/dms-layout/app/app.config.ts +0 -1
  174. package/frontend-vue/layers/dms-layout/app/assets/css/main.css +11 -1
  175. package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Italic-Variable.woff2 +0 -0
  176. package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Variable.woff2 +0 -0
  177. package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Italic-Variable.woff2 +0 -0
  178. package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Variable.woff2 +0 -0
  179. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
  180. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
  181. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +6 -2
  182. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
  183. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -107
  184. package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
  185. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +4 -0
  186. package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
  187. package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
  188. package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
  189. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
  190. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
  191. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
  192. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
  193. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
  194. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
  195. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
  196. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
  197. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
  198. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
  199. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
  200. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
  201. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
  202. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
  203. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
  204. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
  205. package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
  206. package/frontend-vue/layers/dms-layout/app/build/utils/github-repository.ts +2 -2
  207. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
  208. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
  209. package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
  210. package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +49 -11
  211. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
  212. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +59 -4
  213. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
  214. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
  215. package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
  216. package/frontend-vue/layers/dms-layout/app/composables/page/usePageRecordDeletion.ts +162 -0
  217. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
  218. package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +6 -2
  219. package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +6 -2
  220. package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
  221. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
  222. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
  223. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +13 -22
  224. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +11 -10
  225. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +22 -52
  226. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-quick-actions.client.ts +7 -7
  227. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +11 -10
  228. package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
  229. package/frontend-vue/layers/dms-layout/app/theme/actions.ts +5 -4
  230. package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
  231. package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
  232. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
  233. package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
  234. package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
  235. package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
  236. package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
  237. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +45 -18
  238. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +44 -17
  239. package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
  240. package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
  241. package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
  242. package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
  243. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
  244. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
  245. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
  246. package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
  247. package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
  248. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
  249. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
  250. package/frontend-vue/layers/dms-ui/app/build/components/form/FormLoadError.vue +5 -1
  251. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
  252. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
  253. package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
  254. package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
  255. package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +48 -1
  256. package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
  257. package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
  258. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
  259. package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
  260. package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
  261. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
  262. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
  263. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
  264. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
  265. package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
  266. package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
  267. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
  268. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
  269. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
  270. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/actionError.ts +6 -1
  271. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
  272. package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
  273. package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
  274. package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
  275. package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
  276. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
  277. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +44 -0
  278. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +123 -27
  279. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
  280. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
  281. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +92 -0
  282. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
  283. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
  284. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
  285. package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
  286. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
  287. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
  288. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
  289. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
  290. package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
  291. package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +11 -5
  292. package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
  293. package/frontend-vue/layers/dms-ui/app/build/composables/form/formValidation.ts +36 -4
  294. package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
  295. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
  296. package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +78 -0
  297. package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
  298. package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
  299. package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
  300. package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
  301. package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
  302. package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
  303. package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
  304. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
  305. package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
  306. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
  307. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
  308. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
  309. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
  310. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
  311. package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
  312. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +97 -0
  313. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
  314. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
  315. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
  316. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
  317. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
  318. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
  319. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
  320. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
  321. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
  322. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
  323. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
  324. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
  325. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
  326. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
  327. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +90 -35
  328. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
  329. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
  330. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
  331. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
  332. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
  333. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
  334. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
  335. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
  336. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
  337. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
  338. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
  339. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
  340. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
  341. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
  342. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
  343. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/writeError.ts +21 -0
  344. package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
  345. package/frontend-vue/layers/dms-ui/app/build/utils/codeHighlight.ts +67 -29
  346. package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
  347. package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
  348. package/frontend-vue/layers/dms-ui/app/build/utils/htmlGrammar.ts +79 -0
  349. package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
  350. package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
  351. package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
  352. package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
  353. package/frontend-vue/layers/dms-ui/app/build/utils/shellParser.ts +1 -1
  354. package/frontend-vue/layers/dms-ui/app/build/utils/streamHighlight.ts +90 -0
  355. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
  356. package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
  357. package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
  358. package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
  359. package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
  360. package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
  361. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
  362. package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
  363. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
  364. package/frontend-vue/layers/dms-ui/app/components/code-snippet/CodeSnippet.vue +23 -12
  365. package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
  366. package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
  367. package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
  368. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
  369. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +119 -36
  370. package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +53 -22
  371. package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
  372. package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
  373. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
  374. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
  375. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
  376. package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
  377. package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
  378. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
  379. package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
  380. package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
  381. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +226 -65
  382. package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
  383. package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +210 -0
  384. package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
  385. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
  386. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
  387. package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
  388. package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
  389. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
  390. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
  391. package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
  392. package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
  393. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
  394. package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
  395. package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +74 -25
  396. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +48 -44
  397. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
  398. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +426 -97
  399. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
  400. package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
  401. package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
  402. package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
  403. package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
  404. package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
  405. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
  406. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
  407. package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
  408. package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
  409. package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
  410. package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +16 -0
  411. package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
  412. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +189 -28
  413. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +29 -12
  414. package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
  415. package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
  416. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
  417. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
  418. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
  419. package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
  420. package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
  421. package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
  422. package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
  423. package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
  424. package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
  425. package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
  426. package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
  427. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
  428. package/frontend-vue/layers/dms-ui/app/utils/cascader.ts +94 -3
  429. package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
  430. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +20 -5
  431. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +20 -5
  432. package/frontend-vue/package.json +6 -2
  433. package/package.json +11 -10
  434. package/skills/dms/SKILL.md +1 -1
  435. package/skills/dms-auth/SKILL.md +5 -3
  436. package/skills/dms-best-practices/REFERENCE.md +2 -0
  437. package/skills/dms-best-practices/SKILL.md +4 -0
  438. package/skills/dms-module/SKILL.md +3 -2
  439. package/skills/dms-pages/REFERENCE.md +129 -16
  440. package/skills/dms-pages/SKILL.md +1 -1
  441. package/skills/dms-project/DEV-SERVERS.md +23 -1
  442. package/skills/dms-project/REFERENCE.md +1 -1
  443. package/skills/dms-project/SKILL.md +4 -1
  444. package/skills/dms-project/scripts/dms-dev.sh +205 -17
  445. package/dist/hooks/tenant-instances.d.ts +0 -1
  446. package/dist/hooks/tenant-instances.js +0 -13
  447. package/dist/hooks/tenant-instances.js.map +0 -1
  448. package/dist/routes/startup-gate.d.ts +0 -7
  449. package/dist/routes/startup-gate.js +0 -39
  450. package/dist/routes/startup-gate.js.map +0 -1
  451. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
@@ -19,15 +19,29 @@ export function formatRelativeDate(
19
19
  );
20
20
  }
21
21
 
22
- /** "Sep 27": day and short month. */
22
+ const DAY_MONTH_FORMAT: Intl.DateTimeFormatOptions = {
23
+ month: "short",
24
+ day: "numeric",
25
+ };
26
+ const YEAR_FORMAT: Intl.DateTimeFormatOptions = { year: "numeric" };
27
+
28
+ /**
29
+ * "Sep 27": day and short month, read against `now`; a date of another year
30
+ * ("Apr 2, 2018") names its year as well.
31
+ */
23
32
  export function formatDayMonth(
24
33
  date: string | number | Date,
25
34
  locale: string,
35
+ now: number = Date.now(),
26
36
  ): string {
27
- return regionalDateTimeFormat(locale, {
28
- month: "short",
29
- day: "numeric",
30
- }).format(new Date(date));
37
+ const day = new Date(date);
38
+ // Years read in the reader's time zone, as the date is written.
39
+ const year = regionalDateTimeFormat(locale, YEAR_FORMAT);
40
+ const isOtherYear = year.format(day) !== year.format(now);
41
+ return regionalDateTimeFormat(
42
+ locale,
43
+ isOtherYear ? { ...DAY_MONTH_FORMAT, ...YEAR_FORMAT } : DAY_MONTH_FORMAT,
44
+ ).format(day);
31
45
  }
32
46
 
33
47
  /** First word of a name, for "Sep 27 · by Camille". */
@@ -0,0 +1,99 @@
1
+ // `scrollIntoView` scrolls every ancestor that can scroll, including the
2
+ // layout's `overflow: hidden` root: the header and breadcrumb slide out of
3
+ // view and nothing brings them back. These helpers scroll the one container
4
+ // that actually scrolls the element instead.
5
+
6
+ const SCROLLABLE_OVERFLOW = /(auto|scroll)/;
7
+
8
+ /** Where an element lands in its scroll container. */
9
+ export type ScrollWithinBlock = "start" | "center" | "nearest";
10
+
11
+ /** How `scrollWithin` brings an element into view. */
12
+ export interface ScrollWithinOptions {
13
+ block?: ScrollWithinBlock;
14
+ behavior?: ScrollBehavior;
15
+ }
16
+
17
+ /**
18
+ * The closest ancestor of `element` the user can scroll (`overflow-y: auto`
19
+ * or `scroll`, with content taller than itself), or `null` when only the
20
+ * window scrolls it.
21
+ *
22
+ * @internal
23
+ */
24
+ export function scrollParent(element: HTMLElement): HTMLElement | null {
25
+ for (let node = element.parentElement; node; node = node.parentElement) {
26
+ const { overflowY } = getComputedStyle(node);
27
+ if (
28
+ SCROLLABLE_OVERFLOW.test(overflowY) &&
29
+ node.scrollHeight > node.clientHeight
30
+ ) {
31
+ return node;
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+
37
+ interface ScrollViewport {
38
+ top: number;
39
+ height: number;
40
+ scrollTop: number;
41
+ }
42
+
43
+ function viewportOf(scroller: HTMLElement | null): ScrollViewport {
44
+ if (scroller) {
45
+ return {
46
+ top: scroller.getBoundingClientRect().top,
47
+ height: scroller.clientHeight,
48
+ scrollTop: scroller.scrollTop,
49
+ };
50
+ }
51
+ return { top: 0, height: window.innerHeight, scrollTop: window.scrollY };
52
+ }
53
+
54
+ function scrollMarginTop(element: HTMLElement): number {
55
+ return Number.parseFloat(getComputedStyle(element).scrollMarginTop) || 0;
56
+ }
57
+
58
+ /** The scroll offset that puts `rect` at `block` in `viewport`. */
59
+ function targetOffset(
60
+ rect: DOMRect,
61
+ margin: number,
62
+ viewport: ScrollViewport,
63
+ block: ScrollWithinBlock,
64
+ ): number | null {
65
+ const top = rect.top - viewport.top - margin;
66
+ if (block === "start") return viewport.scrollTop + top;
67
+ if (block === "center") {
68
+ return viewport.scrollTop + top - (viewport.height - rect.height) / 2;
69
+ }
70
+ const bottom = rect.bottom - viewport.top;
71
+ if (top >= 0 && bottom <= viewport.height) return null;
72
+ return top < 0
73
+ ? viewport.scrollTop + top
74
+ : viewport.scrollTop + bottom - viewport.height;
75
+ }
76
+
77
+ /**
78
+ * Brings `element` into view by scrolling its own scroll container only
79
+ * (the window when nothing closer scrolls it), honouring its
80
+ * `scroll-margin-top`. Unlike `scrollIntoView`, no other ancestor moves.
81
+ *
82
+ * @internal
83
+ */
84
+ export function scrollWithin(
85
+ element: HTMLElement,
86
+ { block = "start", behavior = "auto" }: ScrollWithinOptions = {},
87
+ ): void {
88
+ const scroller = scrollParent(element);
89
+ const offset = targetOffset(
90
+ element.getBoundingClientRect(),
91
+ block === "start" ? scrollMarginTop(element) : 0,
92
+ viewportOf(scroller),
93
+ block,
94
+ );
95
+ if (offset === null) return;
96
+ const top = Math.max(0, offset);
97
+ if (scroller) scroller.scrollTo({ top, behavior });
98
+ else window.scrollTo({ top, behavior });
99
+ }
@@ -1,4 +1,4 @@
1
- import type { StreamParser, StringStream } from "@codemirror/language";
1
+ import type { StreamParser, StringStream } from "@codemirror/streamparser";
2
2
 
3
3
  // A shell snippet as read for highlighting only: the command of each
4
4
  // pipeline step, its flags, quoted strings, variables and comments.
@@ -0,0 +1,90 @@
1
+ // A CodeMirror stream parser (a language written as a line tokenizer, like
2
+ // the shell's) run for read-only highlighting. `StreamLanguage` would do it
3
+ // inside an editor state, and so pull CodeMirror's view and state into every
4
+ // page showing a snippet: this drives the parser the same way, line by line
5
+ // over `StringStream`, and names its tokens' tags the same way.
6
+ import { type StreamParser, StringStream } from "@codemirror/streamparser";
7
+ import { type Highlighter, type Tag, tags } from "@lezer/highlight";
8
+
9
+ /** Receives one styled range of the code: its offsets and its classes. */
10
+ export type HighlightedRange = (
11
+ from: number,
12
+ to: number,
13
+ classes: string,
14
+ ) => void;
15
+
16
+ // CodeMirror's defaults, which `StreamLanguage` hands every stream.
17
+ const TAB_SIZE = 4;
18
+ const INDENT_UNIT = 2;
19
+ // Zero-length tokens a parser may return in a row (each one changing its
20
+ // state) before the stream moves on by itself, as `StreamLanguage` allows.
21
+ const MAX_EMPTY_TOKENS = 10;
22
+
23
+ type TagValue = Tag | readonly Tag[] | ((tag: Tag) => Tag);
24
+ const TAG_TABLE = tags as unknown as Record<string, TagValue | undefined>;
25
+ const tagsByName = new Map<string, Tag[]>();
26
+
27
+ // A token type names tags the way `StreamLanguage` reads them: space
28
+ // separated, each a tag followed by its modifiers (`variableName.special`).
29
+ function tagsOf(type: string): Tag[] {
30
+ const cached = tagsByName.get(type);
31
+ if (cached) return cached;
32
+ const resolved = type.split(" ").flatMap((name) => {
33
+ let found: Tag[] = [];
34
+ for (const part of name.split(".")) {
35
+ const value = TAG_TABLE[part];
36
+ if (typeof value === "function") found = found.map(value);
37
+ else if (value && !found.length)
38
+ found = Array.isArray(value) ? [...value] : [value as Tag];
39
+ }
40
+ return found;
41
+ });
42
+ tagsByName.set(type, resolved);
43
+ return resolved;
44
+ }
45
+
46
+ /**
47
+ * Tokenizes `code` with `parser` and calls `put` for each range its
48
+ * highlighter styles, adjacent ranges of one style merged, as
49
+ * `highlightTree` reports a parsed tree.
50
+ */
51
+ export function highlightStream<State>(
52
+ parser: StreamParser<State>,
53
+ code: string,
54
+ highlighter: Highlighter,
55
+ put: HighlightedRange,
56
+ ): void {
57
+ const state = parser.startState?.(INDENT_UNIT) ?? (true as State);
58
+ let pending: { from: number; to: number; classes: string } | null = null;
59
+ const flush = () => {
60
+ if (pending) put(pending.from, pending.to, pending.classes);
61
+ pending = null;
62
+ };
63
+ const add = (from: number, to: number, classes: string | null) => {
64
+ if (pending && (pending.to !== from || pending.classes !== classes))
65
+ flush();
66
+ if (!classes) return;
67
+ if (pending) pending.to = to;
68
+ else pending = { from, to, classes };
69
+ };
70
+
71
+ let lineStart = 0;
72
+ for (const line of code.split("\n")) {
73
+ if (!line) parser.blankLine?.(state, INDENT_UNIT);
74
+ const stream = new StringStream(line, TAB_SIZE, INDENT_UNIT);
75
+ let emptyTokens = 0;
76
+ while (!stream.eol()) {
77
+ stream.start = stream.pos;
78
+ const type = parser.token(stream, state);
79
+ if (stream.pos === stream.start) {
80
+ if (++emptyTokens < MAX_EMPTY_TOKENS) continue;
81
+ stream.next();
82
+ }
83
+ emptyTokens = 0;
84
+ const classes = type ? highlighter.style(tagsOf(type)) : null;
85
+ add(lineStart + stream.start, lineStart + stream.pos, classes);
86
+ }
87
+ lineStart += line.length + 1;
88
+ }
89
+ flush();
90
+ }
@@ -154,19 +154,32 @@ const FRAGMENT_SEPARATOR = "#";
154
154
  const PAIR_SEPARATOR = "&";
155
155
  const VALUE_SEPARATOR = "=";
156
156
 
157
- /** The query of a URL without the pairs whose whole value is a token left empty. */
158
- function dropUnresolvedPairs(search: string): string {
157
+ /** The query of a URL without the pairs whose whole value matches `value`. */
158
+ function dropPairs(search: string, value: RegExp): string {
159
159
  return search
160
160
  .split(PAIR_SEPARATOR)
161
161
  .filter((pair) => {
162
- const value = pair.slice(pair.indexOf(VALUE_SEPARATOR) + 1);
163
- return (
164
- !pair.includes(VALUE_SEPARATOR) || !UNRESOLVED_ROUTE_VALUE.test(value)
165
- );
162
+ const pairValue = pair.slice(pair.indexOf(VALUE_SEPARATOR) + 1);
163
+ return !pair.includes(VALUE_SEPARATOR) || !value.test(pairValue);
166
164
  })
167
165
  .join(PAIR_SEPARATOR);
168
166
  }
169
167
 
168
+ /**
169
+ * A URL without its `token`s: a query pair whose whole value is one (`value`)
170
+ * is left out, any other is left empty.
171
+ */
172
+ function stripTokens(url: string, token: RegExp, value: RegExp): string {
173
+ const [beforeFragment = "", ...fragment] = url.split(FRAGMENT_SEPARATOR);
174
+ const [path = "", ...search] = beforeFragment.split(QUERY_SEPARATOR);
175
+ const query = dropPairs(search.join(QUERY_SEPARATOR), value);
176
+ const rebuilt = [
177
+ query ? `${path}${QUERY_SEPARATOR}${query}` : path,
178
+ ...fragment,
179
+ ].join(FRAGMENT_SEPARATOR);
180
+ return rebuilt.replace(token, "");
181
+ }
182
+
170
183
  /**
171
184
  * A link's URL with the `{{params.X}}` / `{{query.X}}` tokens of the page it
172
185
  * sits on filled, for a button that always leads somewhere: a query pair
@@ -180,12 +193,21 @@ export function resolveLinkVariables(
180
193
  ): string {
181
194
  const filled = replaceUrlVariables(url, context);
182
195
  if (!hasUrlVariables(filled)) return filled;
183
- const [beforeFragment = "", ...fragment] = filled.split(FRAGMENT_SEPARATOR);
184
- const [path = "", ...search] = beforeFragment.split(QUERY_SEPARATOR);
185
- const query = dropUnresolvedPairs(search.join(QUERY_SEPARATOR));
186
- const rebuilt = [
187
- query ? `${path}${QUERY_SEPARATOR}${query}` : path,
188
- ...fragment,
189
- ].join(FRAGMENT_SEPARATOR);
190
- return rebuilt.replace(UNRESOLVED_ROUTE_TOKEN, "");
196
+ return stripTokens(filled, UNRESOLVED_ROUTE_TOKEN, UNRESOLVED_ROUTE_VALUE);
197
+ }
198
+
199
+ // A row field, `{{row.<path>}}`, as `interpolateUrl` fills it.
200
+ const ROW_KEY = String.raw`row\.[^{}\s]+`;
201
+ const ROW_TOKEN = tokenPattern(ROW_KEY);
202
+ const ROW_VALUE = new RegExp(`^${tokenPattern(ROW_KEY, "").source}$`);
203
+
204
+ /**
205
+ * A URL sent without a row (a bulk action on the selection bar) with its
206
+ * `{{row.X}}` tokens taken out as a link's unresolved page tokens are: a query
207
+ * pair whose whole value is one is left out (`?ids={{row._id}}` gives way to
208
+ * the selection's `ids`), any other is left empty.
209
+ */
210
+ export function dropRowTokens(url: string): string {
211
+ if (!new RegExp(ROW_TOKEN.source).test(url)) return url;
212
+ return stripTokens(url, ROW_TOKEN, ROW_VALUE);
191
213
  }
@@ -16,7 +16,7 @@
16
16
  {{ $t("form.password_strength.contains") }}
17
17
  </p>
18
18
 
19
- <ul class="space-y-1 pl-3" aria-label="Password requirements">
19
+ <ul class="space-y-1 pl-3" aria-labelledby="password-strength">
20
20
  <li
21
21
  v-for="(req, index) in props.strength"
22
22
  :key="index"
@@ -20,6 +20,11 @@ interface Props {
20
20
  * a role preview veil this block; without it the block is never veiled.
21
21
  */
22
22
  layoutPath?: string;
23
+ /**
24
+ * Props handed to this block only, as they are (functions included), over
25
+ * its options: what an action target's container gives its block.
26
+ */
27
+ rootProps?: Record<string, unknown>;
23
28
  }
24
29
 
25
30
  const props = defineProps<Props>();
@@ -108,7 +113,10 @@ provide<GridDeclaredColumns>(GRID_DECLARED_COLUMNS, {
108
113
  <Component
109
114
  :is="component.component"
110
115
  :ref="anchorBlock"
111
- v-bind="JSON.parse(JSON.stringify(component.options || {})) || {}"
116
+ v-bind="{
117
+ ...JSON.parse(JSON.stringify(component.options || {})),
118
+ ...rootProps,
119
+ }"
112
120
  :page-id="pageId"
113
121
  :component-id="componentId"
114
122
  :child-count="columnCount"
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { computed, onBeforeUnmount, onMounted, ref } from "vue";
2
+ import { computed, onMounted, ref } from "vue";
3
3
  import DmsSegmented from "../segmented/Segmented.vue";
4
4
  import { usePageLive } from "../../composables/live/usePageLive";
5
- import { formatElapsed } from "../../build/composables/live/formatElapsed";
5
+ import { useUpdatedLabel } from "../../build/composables/live/useUpdatedLabel";
6
6
  import { useLiveToggleShortcut } from "../../build/composables/live/useLiveToggleShortcut";
7
7
  import { HIDDEN_TAB_NOTE } from "../../build/composables/live/pageLiveController";
8
8
  import type { ConfirmTranslate } from "../../build/composables/confirm/confirmDialogTexts";
@@ -10,14 +10,24 @@ import {
10
10
  missedRowsText,
11
11
  showLiveNote,
12
12
  } from "../../build/composables/live/liveNotes";
13
+ import {
14
+ type PageToolbarSize,
15
+ usePageToolbar,
16
+ useToolbarLiveToggle,
17
+ } from "../../build/composables/page-toolbar/pageToolbarContext";
18
+ import { PAGE_TOOLBAR_CONTROL_HEIGHT_CLASSES } from "../../build/composables/page-toolbar/toolbarControlHeight";
13
19
  import type { DefaultComponentProps } from "../../../../dms-core/app/types/component";
14
20
 
15
- interface LiveToggleBlockProps extends DefaultComponentProps {
21
+ // Its ids are optional: it reads the page's live state, not its own, and a
22
+ // component may draw it by hand inside its own `PageToolbar`.
23
+ interface LiveToggleBlockProps extends Partial<DefaultComponentProps> {
16
24
  align?: "left" | "center" | "right";
17
25
  /** Shows "updated N s ago". */
18
26
  showUpdatedAt?: boolean;
19
27
  /** `segmented` (Live / Pause segments) or `pill` (a pulsing Live pill). */
20
28
  variant?: "segmented" | "pill";
29
+ /** How large it is drawn; the holding `PageToolbar`'s size, else `sm`. */
30
+ size?: PageToolbarSize;
21
31
  /** What the paused toggle calls the missed events, given `{count}`. */
22
32
  missedLabel?: string;
23
33
  }
@@ -26,12 +36,13 @@ const props = withDefaults(defineProps<LiveToggleBlockProps>(), {
26
36
  align: "right",
27
37
  showUpdatedAt: true,
28
38
  variant: "segmented",
39
+ size: undefined,
29
40
  missedLabel: undefined,
30
41
  });
31
42
 
32
43
  const DIRECT = "direct";
33
44
  const PAUSE = "pause";
34
- const TICK_MS = 1000;
45
+ const DEFAULT_SIZE: PageToolbarSize = "sm";
35
46
  const ALIGN_CLASSES: Record<
36
47
  NonNullable<LiveToggleBlockProps["align"]>,
37
48
  string
@@ -41,26 +52,53 @@ const ALIGN_CLASSES: Record<
41
52
  right: "justify-end",
42
53
  };
43
54
  const PILL_BUTTON_CLASS =
44
- "focus-visible:outline-primary flex items-center gap-1.5 rounded-[5px] px-2 py-1 text-[12px] font-medium transition-colors focus-visible:outline-2";
55
+ "focus-visible:outline-primary flex h-full items-center rounded-[5px] font-medium transition-colors focus-visible:outline-2";
45
56
  const PILL_IDLE_CLASS = "text-muted hover:text-default hover:bg-elevated";
46
57
 
58
+ /** What each size changes: the texts and the pill's buttons. */
59
+ interface LiveToggleSizeClasses {
60
+ text: string;
61
+ pillButton: string;
62
+ }
63
+
64
+ // The pill takes the toolbar's one height (its buttons fill it), as the
65
+ // period selector's segments beside it and the Resume button do.
66
+ const SIZE_CLASSES: Record<PageToolbarSize, LiveToggleSizeClasses> = {
67
+ xs: {
68
+ text: "text-[11.5px]",
69
+ pillButton: "gap-1 px-1.5 text-[11px]",
70
+ },
71
+ sm: {
72
+ text: "text-[12.5px]",
73
+ pillButton: "gap-1.5 px-2 text-[12px]",
74
+ },
75
+ md: {
76
+ text: "text-[13px]",
77
+ pillButton: "gap-1.5 px-2.5 text-[13px]",
78
+ },
79
+ };
80
+
47
81
  const { t } = useI18n();
48
82
  const translate = t as ConfirmTranslate;
49
83
  const live = usePageLive();
84
+ const toolbar = usePageToolbar();
85
+ useToolbarLiveToggle(toolbar);
86
+ const size = computed(() => props.size ?? toolbar?.size.value ?? DEFAULT_SIZE);
87
+ const sizeClasses = computed(() => SIZE_CLASSES[size.value]);
88
+ const heightClass = computed(
89
+ () => PAGE_TOOLBAR_CONTROL_HEIGHT_CLASSES[size.value],
90
+ );
50
91
 
51
- // The state lives in the browser (its storage, its clock): the server and the
52
- // hydrating render draw nothing, the mounted block draws the real state.
92
+ // The toggle is drawn from the server on, live (the remembered pause applies
93
+ // once mounted), so it takes its place before the page paints. Only the
94
+ // "updated N s ago" text waits for the browser's clock: the server's would
95
+ // not match the hydrating render's.
53
96
  const isMounted = ref(false);
54
- const now = ref(Date.now());
55
- let ticker: ReturnType<typeof setInterval> | undefined;
56
-
57
- onMounted(() => {
58
- isMounted.value = true;
59
- if (props.showUpdatedAt) {
60
- ticker = setInterval(() => (now.value = Date.now()), TICK_MS);
61
- }
62
- });
63
- onBeforeUnmount(() => clearInterval(ticker));
97
+ onMounted(() => (isMounted.value = true));
98
+ const updatedText = useUpdatedLabel(
99
+ () => live.lastUpdatedAt.value,
100
+ props.showUpdatedAt,
101
+ );
64
102
 
65
103
  const mode = computed({
66
104
  get: () => (live.isPaused.value ? PAUSE : DIRECT),
@@ -75,15 +113,9 @@ const items = computed(() => [
75
113
  { value: PAUSE, label: t("dms.live.pause"), icon: "i-ph-pause" },
76
114
  ]);
77
115
 
78
- const updatedLabel = computed(() => {
79
- const at = live.lastUpdatedAt.value;
80
- if (at === null) return "";
81
- const { unit, count } = formatElapsed(now.value - at);
82
- const updated = t(`dms.live.updated_${unit}`, { count }, count);
83
- return live.isPolling.value
84
- ? `${updated} · ${t("dms.live.polling")}`
85
- : updated;
86
- });
116
+ // The browser's clock only: the server's text would not match the hydrating
117
+ // render's.
118
+ const updatedLabel = computed(() => (isMounted.value ? updatedText.value : ""));
87
119
 
88
120
  const missedText = computed(() => {
89
121
  const count = live.missedCount.value;
@@ -122,13 +154,14 @@ useLiveToggleShortcut(() => live.toggle());
122
154
 
123
155
  <template>
124
156
  <div
125
- v-if="isMounted && live.isEnabled.value"
157
+ v-if="live.isEnabled.value"
126
158
  class="dms-live-toggle flex flex-wrap items-center gap-x-2.5 gap-y-1"
127
159
  :class="[ALIGN_CLASSES[align], `dms-live-toggle--${variant}`]"
128
160
  >
129
161
  <span
130
162
  v-if="live.isPaused.value"
131
- class="dms-live-toggle__status text-muted min-w-0 text-[12.5px] tabular-nums"
163
+ class="dms-live-toggle__status text-muted min-w-0 tabular-nums"
164
+ :class="sizeClasses.text"
132
165
  aria-live="polite"
133
166
  >
134
167
  {{ pausedText }}
@@ -136,16 +169,28 @@ useLiveToggleShortcut(() => live.toggle());
136
169
  </span>
137
170
  <span
138
171
  v-else-if="showUpdatedAt"
139
- class="text-muted text-[12.5px] tabular-nums"
172
+ class="text-muted inline-flex items-center gap-1.5 tabular-nums"
173
+ :class="sizeClasses.text"
140
174
  aria-live="off"
141
175
  >
142
176
  {{ updatedLabel }}
177
+ <!-- The polling hint, its box always kept: showing it never widens the
178
+ toggle, which would wrap the toolbar and move the page. -->
179
+ <UIcon
180
+ name="i-ph-wifi-slash"
181
+ class="dms-live-toggle__polling size-3.5 shrink-0"
182
+ :class="!live.isPolling.value && 'invisible'"
183
+ :title="live.isPolling.value ? t('dms.live.polling') : undefined"
184
+ :aria-label="live.isPolling.value ? t('dms.live.polling') : undefined"
185
+ :aria-hidden="!live.isPolling.value || undefined"
186
+ />
143
187
  </span>
144
188
  <div
145
189
  v-if="variant === 'pill'"
146
190
  role="group"
147
191
  :aria-label="t('dms.live.aria_label')"
148
- class="border-default bg-default inline-flex shrink-0 items-center gap-0.5 rounded-md border p-0.5"
192
+ class="border-default bg-default inline-flex shrink-0 items-stretch gap-0.5 rounded-md border p-0.5"
193
+ :class="heightClass"
149
194
  >
150
195
  <button
151
196
  type="button"
@@ -153,6 +198,7 @@ useLiveToggleShortcut(() => live.toggle());
153
198
  :aria-pressed="live.isLive.value"
154
199
  :class="[
155
200
  PILL_BUTTON_CLASS,
201
+ sizeClasses.pillButton,
156
202
  live.isLive.value ? 'bg-success/10 text-success' : PILL_IDLE_CLASS,
157
203
  ]"
158
204
  @click="live.resume()"
@@ -173,6 +219,7 @@ useLiveToggleShortcut(() => live.toggle());
173
219
  :aria-pressed="live.isPaused.value"
174
220
  :class="[
175
221
  PILL_BUTTON_CLASS,
222
+ sizeClasses.pillButton,
176
223
  live.isPaused.value
177
224
  ? 'bg-elevated text-highlighted'
178
225
  : PILL_IDLE_CLASS,
@@ -188,12 +235,13 @@ useLiveToggleShortcut(() => live.toggle());
188
235
  v-model="mode"
189
236
  :items="items"
190
237
  :aria-label="t('dms.live.aria_label')"
191
- size="sm"
238
+ :size="size"
192
239
  />
193
240
  <UButton
194
241
  v-if="variant === 'pill' && canResume"
195
242
  data-action="resume"
196
- size="xs"
243
+ :size="size"
244
+ :class="heightClass"
197
245
  color="neutral"
198
246
  variant="soft"
199
247
  icon="i-ph-play-fill"