@prenta/admin 2.6.1 → 2.7.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 (794) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/design-system/tokens.css +179 -85
  3. package/design-system/tokens.json +276 -85
  4. package/design-system/tokens.scss +148 -44
  5. package/design-system/tokens.tailwind.cjs +50 -1
  6. package/dist/AdminRoot.d.ts.map +1 -1
  7. package/dist/AdminRoot.js +42 -20
  8. package/dist/AdminRoot.js.map +1 -1
  9. package/dist/LoginRoot.d.ts.map +1 -1
  10. package/dist/LoginRoot.js +2 -1
  11. package/dist/LoginRoot.js.map +1 -1
  12. package/dist/__tests__/components/author-avatar.test.js +3 -17
  13. package/dist/__tests__/components/author-avatar.test.js.map +1 -1
  14. package/dist/__tests__/components/notification-bell.test.js +86 -15
  15. package/dist/__tests__/components/notification-bell.test.js.map +1 -1
  16. package/dist/__tests__/components/responsive-tiers.render.test.d.ts +2 -0
  17. package/dist/__tests__/components/responsive-tiers.render.test.d.ts.map +1 -0
  18. package/dist/__tests__/components/responsive-tiers.render.test.js +313 -0
  19. package/dist/__tests__/components/responsive-tiers.render.test.js.map +1 -0
  20. package/dist/__tests__/components/serp-preview.render.test.js +6 -3
  21. package/dist/__tests__/components/serp-preview.render.test.js.map +1 -1
  22. package/dist/__tests__/components/shell-primitives.render.test.d.ts +2 -0
  23. package/dist/__tests__/components/shell-primitives.render.test.d.ts.map +1 -0
  24. package/dist/__tests__/components/shell-primitives.render.test.js +716 -0
  25. package/dist/__tests__/components/shell-primitives.render.test.js.map +1 -0
  26. package/dist/__tests__/components/theme-provider-accent.test.js +49 -7
  27. package/dist/__tests__/components/theme-provider-accent.test.js.map +1 -1
  28. package/dist/__tests__/components/toggle.test.js +10 -3
  29. package/dist/__tests__/components/toggle.test.js.map +1 -1
  30. package/dist/__tests__/components/ui-primitives.test.js +3 -7
  31. package/dist/__tests__/components/ui-primitives.test.js.map +1 -1
  32. package/dist/__tests__/layout/build-nav-items.test.js +1 -1
  33. package/dist/__tests__/layout/build-nav-items.test.js.map +1 -1
  34. package/dist/__tests__/layout/shell-nav.render.test.d.ts +2 -0
  35. package/dist/__tests__/layout/shell-nav.render.test.d.ts.map +1 -0
  36. package/dist/__tests__/layout/shell-nav.render.test.js +736 -0
  37. package/dist/__tests__/layout/shell-nav.render.test.js.map +1 -0
  38. package/dist/__tests__/layout/sidebar-forms-submenu.render.test.js +8 -4
  39. package/dist/__tests__/layout/sidebar-forms-submenu.render.test.js.map +1 -1
  40. package/dist/__tests__/layout/sidebar-groups.render.test.d.ts +2 -0
  41. package/dist/__tests__/layout/sidebar-groups.render.test.d.ts.map +1 -0
  42. package/dist/__tests__/layout/sidebar-groups.render.test.js +56 -0
  43. package/dist/__tests__/layout/sidebar-groups.render.test.js.map +1 -0
  44. package/dist/__tests__/layout/sidebar-menus.render.test.d.ts +2 -0
  45. package/dist/__tests__/layout/sidebar-menus.render.test.d.ts.map +1 -0
  46. package/dist/__tests__/layout/sidebar-menus.render.test.js +82 -0
  47. package/dist/__tests__/layout/sidebar-menus.render.test.js.map +1 -0
  48. package/dist/__tests__/layout/sidebar-plugin-nav.test.js +1 -1
  49. package/dist/__tests__/layout/sidebar-plugin-nav.test.js.map +1 -1
  50. package/dist/__tests__/layout/sidebar-posts.test.js +48 -38
  51. package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
  52. package/dist/__tests__/layout/sidebar-submenu.render.test.js +26 -10
  53. package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
  54. package/dist/__tests__/layout/topbar.render.test.d.ts +2 -0
  55. package/dist/__tests__/layout/topbar.render.test.d.ts.map +1 -0
  56. package/dist/__tests__/layout/topbar.render.test.js +406 -0
  57. package/dist/__tests__/layout/topbar.render.test.js.map +1 -0
  58. package/dist/__tests__/lib/api.test.js +2 -0
  59. package/dist/__tests__/lib/api.test.js.map +1 -1
  60. package/dist/__tests__/lib/job-drawer.test.d.ts +2 -0
  61. package/dist/__tests__/lib/job-drawer.test.d.ts.map +1 -0
  62. package/dist/__tests__/lib/job-drawer.test.js +393 -0
  63. package/dist/__tests__/lib/job-drawer.test.js.map +1 -0
  64. package/dist/__tests__/lib/job-view.test.d.ts +2 -0
  65. package/dist/__tests__/lib/job-view.test.d.ts.map +1 -0
  66. package/dist/__tests__/lib/job-view.test.js +387 -0
  67. package/dist/__tests__/lib/job-view.test.js.map +1 -0
  68. package/dist/__tests__/lib/jobs-service.test.d.ts +2 -0
  69. package/dist/__tests__/lib/jobs-service.test.d.ts.map +1 -0
  70. package/dist/__tests__/lib/jobs-service.test.js +88 -0
  71. package/dist/__tests__/lib/jobs-service.test.js.map +1 -0
  72. package/dist/__tests__/router/admin-route-order.test.d.ts +2 -0
  73. package/dist/__tests__/router/admin-route-order.test.d.ts.map +1 -0
  74. package/dist/__tests__/router/admin-route-order.test.js +36 -0
  75. package/dist/__tests__/router/admin-route-order.test.js.map +1 -0
  76. package/dist/__tests__/styles/ai-gradient-contrast.test.d.ts +2 -0
  77. package/dist/__tests__/styles/ai-gradient-contrast.test.d.ts.map +1 -0
  78. package/dist/__tests__/styles/ai-gradient-contrast.test.js +110 -0
  79. package/dist/__tests__/styles/ai-gradient-contrast.test.js.map +1 -0
  80. package/dist/__tests__/styles/breakpoint-order.test.d.ts +2 -0
  81. package/dist/__tests__/styles/breakpoint-order.test.d.ts.map +1 -0
  82. package/dist/__tests__/styles/breakpoint-order.test.js +52 -0
  83. package/dist/__tests__/styles/breakpoint-order.test.js.map +1 -0
  84. package/dist/__tests__/styles/responsive-layer.test.js +303 -12
  85. package/dist/__tests__/styles/responsive-layer.test.js.map +1 -1
  86. package/dist/__tests__/styles/tailwind-preset.test.d.ts +2 -0
  87. package/dist/__tests__/styles/tailwind-preset.test.d.ts.map +1 -0
  88. package/dist/__tests__/styles/tailwind-preset.test.js +147 -0
  89. package/dist/__tests__/styles/tailwind-preset.test.js.map +1 -0
  90. package/dist/__tests__/styles/theme-token-scope.test.js +9 -3
  91. package/dist/__tests__/styles/theme-token-scope.test.js.map +1 -1
  92. package/dist/__tests__/styles/tiers.test.d.ts +2 -0
  93. package/dist/__tests__/styles/tiers.test.d.ts.map +1 -0
  94. package/dist/__tests__/styles/tiers.test.js +121 -0
  95. package/dist/__tests__/styles/tiers.test.js.map +1 -0
  96. package/dist/__tests__/styles/tokens-drift.test.js +57 -6
  97. package/dist/__tests__/styles/tokens-drift.test.js.map +1 -1
  98. package/dist/__tests__/views/admin-lazy-route.render.test.js +27 -1
  99. package/dist/__tests__/views/admin-lazy-route.render.test.js.map +1 -1
  100. package/dist/__tests__/views/ai-settings.render.test.js +4 -4
  101. package/dist/__tests__/views/appearance-menus-route.render.test.d.ts +2 -0
  102. package/dist/__tests__/views/appearance-menus-route.render.test.d.ts.map +1 -0
  103. package/dist/__tests__/views/appearance-menus-route.render.test.js +50 -0
  104. package/dist/__tests__/views/appearance-menus-route.render.test.js.map +1 -0
  105. package/dist/__tests__/views/appearance-settings.render.test.js +8 -6
  106. package/dist/__tests__/views/appearance-settings.render.test.js.map +1 -1
  107. package/dist/__tests__/views/appearance-theme-provider.render.test.d.ts +2 -0
  108. package/dist/__tests__/views/appearance-theme-provider.render.test.d.ts.map +1 -0
  109. package/dist/__tests__/views/appearance-theme-provider.render.test.js +286 -0
  110. package/dist/__tests__/views/appearance-theme-provider.render.test.js.map +1 -0
  111. package/dist/__tests__/views/dashboard.test.js +37 -0
  112. package/dist/__tests__/views/dashboard.test.js.map +1 -1
  113. package/dist/__tests__/views/document-edit-navigation.render.test.d.ts +2 -0
  114. package/dist/__tests__/views/document-edit-navigation.render.test.d.ts.map +1 -0
  115. package/dist/__tests__/views/document-edit-navigation.render.test.js +101 -0
  116. package/dist/__tests__/views/document-edit-navigation.render.test.js.map +1 -0
  117. package/dist/__tests__/views/document-edit-routes.render.test.d.ts +2 -0
  118. package/dist/__tests__/views/document-edit-routes.render.test.d.ts.map +1 -0
  119. package/dist/__tests__/views/document-edit-routes.render.test.js +113 -0
  120. package/dist/__tests__/views/document-edit-routes.render.test.js.map +1 -0
  121. package/dist/__tests__/views/email-settings.render.test.js +2 -2
  122. package/dist/__tests__/views/form-entries.render.test.js +15 -0
  123. package/dist/__tests__/views/form-entries.render.test.js.map +1 -1
  124. package/dist/__tests__/views/form-submissions.render.test.js +18 -0
  125. package/dist/__tests__/views/form-submissions.render.test.js.map +1 -1
  126. package/dist/__tests__/views/forms-list.render.test.js +12 -0
  127. package/dist/__tests__/views/forms-list.render.test.js.map +1 -1
  128. package/dist/__tests__/views/general-settings.render.test.js +26 -10
  129. package/dist/__tests__/views/general-settings.render.test.js.map +1 -1
  130. package/dist/__tests__/views/jobs-drawer.render.test.d.ts +2 -0
  131. package/dist/__tests__/views/jobs-drawer.render.test.d.ts.map +1 -0
  132. package/dist/__tests__/views/jobs-drawer.render.test.js +908 -0
  133. package/dist/__tests__/views/jobs-drawer.render.test.js.map +1 -0
  134. package/dist/__tests__/views/jobs-list.render.test.d.ts +2 -0
  135. package/dist/__tests__/views/jobs-list.render.test.d.ts.map +1 -0
  136. package/dist/__tests__/views/jobs-list.render.test.js +695 -0
  137. package/dist/__tests__/views/jobs-list.render.test.js.map +1 -0
  138. package/dist/__tests__/views/kpi-grid.test.d.ts +2 -0
  139. package/dist/__tests__/views/kpi-grid.test.d.ts.map +1 -0
  140. package/dist/__tests__/views/kpi-grid.test.js +26 -0
  141. package/dist/__tests__/views/kpi-grid.test.js.map +1 -0
  142. package/dist/__tests__/views/post-section-editor.test.js +3 -1
  143. package/dist/__tests__/views/post-section-editor.test.js.map +1 -1
  144. package/dist/__tests__/views/script-tags.render.test.js +25 -0
  145. package/dist/__tests__/views/script-tags.render.test.js.map +1 -1
  146. package/dist/__tests__/views/security-settings.render.test.js +9 -8
  147. package/dist/__tests__/views/security-settings.render.test.js.map +1 -1
  148. package/dist/__tests__/views/seo-settings.render.test.js +7 -7
  149. package/dist/__tests__/views/seo-settings.render.test.js.map +1 -1
  150. package/dist/__tests__/views/settings-save-bar.render.test.d.ts +2 -0
  151. package/dist/__tests__/views/settings-save-bar.render.test.d.ts.map +1 -0
  152. package/dist/__tests__/views/settings-save-bar.render.test.js +68 -0
  153. package/dist/__tests__/views/settings-save-bar.render.test.js.map +1 -0
  154. package/dist/__tests__/views/settings-save-footer.render.test.js +23 -15
  155. package/dist/__tests__/views/settings-save-footer.render.test.js.map +1 -1
  156. package/dist/__tests__/views/settings-tabs.render.test.d.ts +2 -0
  157. package/dist/__tests__/views/settings-tabs.render.test.d.ts.map +1 -0
  158. package/dist/__tests__/views/settings-tabs.render.test.js +319 -0
  159. package/dist/__tests__/views/settings-tabs.render.test.js.map +1 -0
  160. package/dist/__tests__/views/updates-tab.render.test.js +4 -3
  161. package/dist/__tests__/views/updates-tab.render.test.js.map +1 -1
  162. package/dist/components/Breadcrumbs.d.ts +8 -1
  163. package/dist/components/Breadcrumbs.d.ts.map +1 -1
  164. package/dist/components/Breadcrumbs.js +6 -0
  165. package/dist/components/Breadcrumbs.js.map +1 -1
  166. package/dist/components/CommandPalette.d.ts.map +1 -1
  167. package/dist/components/CommandPalette.js +40 -4
  168. package/dist/components/CommandPalette.js.map +1 -1
  169. package/dist/components/LocaleSwitcher.d.ts.map +1 -1
  170. package/dist/components/LocaleSwitcher.js +1 -1
  171. package/dist/components/LocaleSwitcher.js.map +1 -1
  172. package/dist/components/NotificationBell.d.ts.map +1 -1
  173. package/dist/components/NotificationBell.js +44 -3
  174. package/dist/components/NotificationBell.js.map +1 -1
  175. package/dist/components/OfflineStatus.js +1 -1
  176. package/dist/components/OfflineStatus.js.map +1 -1
  177. package/dist/components/PresenceIndicator.d.ts.map +1 -1
  178. package/dist/components/PresenceIndicator.js +2 -1
  179. package/dist/components/PresenceIndicator.js.map +1 -1
  180. package/dist/components/ReflowMeter.js +2 -2
  181. package/dist/components/ReflowMeter.js.map +1 -1
  182. package/dist/components/SEOPanel.js +2 -2
  183. package/dist/components/SEOPanel.js.map +1 -1
  184. package/dist/components/ThemeProvider.d.ts +24 -4
  185. package/dist/components/ThemeProvider.d.ts.map +1 -1
  186. package/dist/components/ThemeProvider.js +80 -22
  187. package/dist/components/ThemeProvider.js.map +1 -1
  188. package/dist/components/TipTapEditor.d.ts.map +1 -1
  189. package/dist/components/TipTapEditor.js +3 -4
  190. package/dist/components/TipTapEditor.js.map +1 -1
  191. package/dist/components/forms/EmbedPanel.js +1 -1
  192. package/dist/components/forms/EmbedPanel.js.map +1 -1
  193. package/dist/components/forms/FormEntryDetailPane.js +6 -1
  194. package/dist/components/forms/FormEntryDetailPane.js.map +1 -1
  195. package/dist/components/forms/ShortcodeGlossary.d.ts.map +1 -1
  196. package/dist/components/forms/ShortcodeGlossary.js +6 -1
  197. package/dist/components/forms/ShortcodeGlossary.js.map +1 -1
  198. package/dist/components/forms/primitives.d.ts +15 -5
  199. package/dist/components/forms/primitives.d.ts.map +1 -1
  200. package/dist/components/forms/primitives.js +11 -11
  201. package/dist/components/forms/primitives.js.map +1 -1
  202. package/dist/components/pages/tag-badges.d.ts +2 -1
  203. package/dist/components/pages/tag-badges.d.ts.map +1 -1
  204. package/dist/components/pages/tag-badges.js +4 -3
  205. package/dist/components/pages/tag-badges.js.map +1 -1
  206. package/dist/components/seo/PageAuditPanel.js +2 -2
  207. package/dist/components/seo/PageAuditPanel.js.map +1 -1
  208. package/dist/components/seo/SerpPreview.d.ts +4 -4
  209. package/dist/components/seo/SerpPreview.js +1 -1
  210. package/dist/components/seo/SerpPreview.js.map +1 -1
  211. package/dist/components/seo/primitives.js +2 -2
  212. package/dist/components/seo/primitives.js.map +1 -1
  213. package/dist/components/ui/AuthorAvatar.d.ts +4 -4
  214. package/dist/components/ui/AuthorAvatar.d.ts.map +1 -1
  215. package/dist/components/ui/AuthorAvatar.js +11 -10
  216. package/dist/components/ui/AuthorAvatar.js.map +1 -1
  217. package/dist/components/ui/Badge.d.ts +1 -1
  218. package/dist/components/ui/Badge.js +1 -1
  219. package/dist/components/ui/Badge.js.map +1 -1
  220. package/dist/components/ui/Breadcrumb.d.ts +27 -0
  221. package/dist/components/ui/Breadcrumb.d.ts.map +1 -0
  222. package/dist/components/ui/Breadcrumb.js +41 -0
  223. package/dist/components/ui/Breadcrumb.js.map +1 -0
  224. package/dist/components/ui/Button.d.ts +2 -2
  225. package/dist/components/ui/Button.d.ts.map +1 -1
  226. package/dist/components/ui/Button.js +31 -16
  227. package/dist/components/ui/Button.js.map +1 -1
  228. package/dist/components/ui/Card.d.ts +8 -4
  229. package/dist/components/ui/Card.d.ts.map +1 -1
  230. package/dist/components/ui/Card.js +21 -13
  231. package/dist/components/ui/Card.js.map +1 -1
  232. package/dist/components/ui/CardListControls.d.ts +34 -0
  233. package/dist/components/ui/CardListControls.d.ts.map +1 -0
  234. package/dist/components/ui/CardListControls.js +25 -0
  235. package/dist/components/ui/CardListControls.js.map +1 -0
  236. package/dist/components/ui/CountChip.d.ts +20 -0
  237. package/dist/components/ui/CountChip.d.ts.map +1 -0
  238. package/dist/components/ui/CountChip.js +48 -0
  239. package/dist/components/ui/CountChip.js.map +1 -0
  240. package/dist/components/ui/FilterPill.d.ts +2 -1
  241. package/dist/components/ui/FilterPill.d.ts.map +1 -1
  242. package/dist/components/ui/FilterPill.js +3 -2
  243. package/dist/components/ui/FilterPill.js.map +1 -1
  244. package/dist/components/ui/Input.d.ts +1 -1
  245. package/dist/components/ui/InspectorPane.d.ts +1 -1
  246. package/dist/components/ui/InspectorPane.d.ts.map +1 -1
  247. package/dist/components/ui/InspectorPane.js +15 -4
  248. package/dist/components/ui/InspectorPane.js.map +1 -1
  249. package/dist/components/ui/Modal.d.ts.map +1 -1
  250. package/dist/components/ui/Modal.js +10 -2
  251. package/dist/components/ui/Modal.js.map +1 -1
  252. package/dist/components/ui/PageHeader.d.ts +28 -0
  253. package/dist/components/ui/PageHeader.d.ts.map +1 -0
  254. package/dist/components/ui/PageHeader.js +18 -0
  255. package/dist/components/ui/PageHeader.js.map +1 -0
  256. package/dist/components/ui/RowActions.d.ts +5 -0
  257. package/dist/components/ui/RowActions.d.ts.map +1 -1
  258. package/dist/components/ui/RowActions.js +7 -2
  259. package/dist/components/ui/RowActions.js.map +1 -1
  260. package/dist/components/ui/Segmented.d.ts +21 -0
  261. package/dist/components/ui/Segmented.d.ts.map +1 -0
  262. package/dist/components/ui/Segmented.js +41 -0
  263. package/dist/components/ui/Segmented.js.map +1 -0
  264. package/dist/components/ui/Toaster.d.ts +12 -0
  265. package/dist/components/ui/Toaster.d.ts.map +1 -0
  266. package/dist/components/ui/Toaster.js +103 -0
  267. package/dist/components/ui/Toaster.js.map +1 -0
  268. package/dist/components/ui/Toggle.d.ts +4 -3
  269. package/dist/components/ui/Toggle.d.ts.map +1 -1
  270. package/dist/components/ui/Toggle.js +16 -12
  271. package/dist/components/ui/Toggle.js.map +1 -1
  272. package/dist/components/ui/UnderlineTabs.d.ts +56 -0
  273. package/dist/components/ui/UnderlineTabs.d.ts.map +1 -0
  274. package/dist/components/ui/UnderlineTabs.js +205 -0
  275. package/dist/components/ui/UnderlineTabs.js.map +1 -0
  276. package/dist/components/ui/UnsavedBar.d.ts +25 -0
  277. package/dist/components/ui/UnsavedBar.d.ts.map +1 -0
  278. package/dist/components/ui/UnsavedBar.js +22 -0
  279. package/dist/components/ui/UnsavedBar.js.map +1 -0
  280. package/dist/components/ui/admin-links.d.ts +11 -0
  281. package/dist/components/ui/admin-links.d.ts.map +1 -0
  282. package/dist/components/ui/admin-links.js +10 -0
  283. package/dist/components/ui/admin-links.js.map +1 -0
  284. package/dist/components/ui/card-table.d.ts +43 -0
  285. package/dist/components/ui/card-table.d.ts.map +1 -0
  286. package/dist/components/ui/card-table.js +31 -0
  287. package/dist/components/ui/card-table.js.map +1 -0
  288. package/dist/components/virtual-window.d.ts +7 -0
  289. package/dist/components/virtual-window.d.ts.map +1 -1
  290. package/dist/components/virtual-window.js +5 -2
  291. package/dist/components/virtual-window.js.map +1 -1
  292. package/dist/fields/RelationshipField.js +1 -1
  293. package/dist/fields/RelationshipField.js.map +1 -1
  294. package/dist/files/dm-mono-latin-400-normal.woff +0 -0
  295. package/dist/files/dm-mono-latin-400-normal.woff2 +0 -0
  296. package/dist/files/dm-mono-latin-500-normal.woff +0 -0
  297. package/dist/files/dm-mono-latin-500-normal.woff2 +0 -0
  298. package/dist/files/dm-mono-latin-ext-400-normal.woff +0 -0
  299. package/dist/files/dm-mono-latin-ext-400-normal.woff2 +0 -0
  300. package/dist/files/dm-mono-latin-ext-500-normal.woff +0 -0
  301. package/dist/files/dm-mono-latin-ext-500-normal.woff2 +0 -0
  302. package/dist/files/dm-sans-latin-400-normal.woff +0 -0
  303. package/dist/files/dm-sans-latin-400-normal.woff2 +0 -0
  304. package/dist/files/dm-sans-latin-500-normal.woff +0 -0
  305. package/dist/files/dm-sans-latin-500-normal.woff2 +0 -0
  306. package/dist/files/dm-sans-latin-600-normal.woff +0 -0
  307. package/dist/files/dm-sans-latin-600-normal.woff2 +0 -0
  308. package/dist/files/dm-sans-latin-700-normal.woff +0 -0
  309. package/dist/files/dm-sans-latin-700-normal.woff2 +0 -0
  310. package/dist/files/dm-sans-latin-ext-400-normal.woff +0 -0
  311. package/dist/files/dm-sans-latin-ext-400-normal.woff2 +0 -0
  312. package/dist/files/dm-sans-latin-ext-500-normal.woff +0 -0
  313. package/dist/files/dm-sans-latin-ext-500-normal.woff2 +0 -0
  314. package/dist/files/dm-sans-latin-ext-600-normal.woff +0 -0
  315. package/dist/files/dm-sans-latin-ext-600-normal.woff2 +0 -0
  316. package/dist/files/dm-sans-latin-ext-700-normal.woff +0 -0
  317. package/dist/files/dm-sans-latin-ext-700-normal.woff2 +0 -0
  318. package/dist/hooks/useAppearanceBootstrap.d.ts.map +1 -1
  319. package/dist/hooks/useAppearanceBootstrap.js +9 -3
  320. package/dist/hooks/useAppearanceBootstrap.js.map +1 -1
  321. package/dist/index.d.ts +15 -0
  322. package/dist/index.d.ts.map +1 -1
  323. package/dist/index.js +8 -0
  324. package/dist/index.js.map +1 -1
  325. package/dist/layout/Header.d.ts +14 -5
  326. package/dist/layout/Header.d.ts.map +1 -1
  327. package/dist/layout/Header.js +78 -13
  328. package/dist/layout/Header.js.map +1 -1
  329. package/dist/layout/Layout.d.ts +5 -3
  330. package/dist/layout/Layout.d.ts.map +1 -1
  331. package/dist/layout/Layout.js +45 -54
  332. package/dist/layout/Layout.js.map +1 -1
  333. package/dist/layout/MobileTabBar.d.ts +20 -0
  334. package/dist/layout/MobileTabBar.d.ts.map +1 -0
  335. package/dist/layout/MobileTabBar.js +29 -0
  336. package/dist/layout/MobileTabBar.js.map +1 -0
  337. package/dist/layout/Sidebar.d.ts +8 -26
  338. package/dist/layout/Sidebar.d.ts.map +1 -1
  339. package/dist/layout/Sidebar.js +309 -345
  340. package/dist/layout/Sidebar.js.map +1 -1
  341. package/dist/layout/nav-config.d.ts +101 -0
  342. package/dist/layout/nav-config.d.ts.map +1 -0
  343. package/dist/layout/nav-config.js +297 -0
  344. package/dist/layout/nav-config.js.map +1 -0
  345. package/dist/layout/primitives/AdminShell.d.ts +10 -2
  346. package/dist/layout/primitives/AdminShell.d.ts.map +1 -1
  347. package/dist/layout/primitives/AdminShell.js +114 -6
  348. package/dist/layout/primitives/AdminShell.js.map +1 -1
  349. package/dist/layout/topbar-classes.d.ts +8 -0
  350. package/dist/layout/topbar-classes.d.ts.map +1 -0
  351. package/dist/layout/topbar-classes.js +8 -0
  352. package/dist/layout/topbar-classes.js.map +1 -0
  353. package/dist/layout/use-nav-items.d.ts +10 -0
  354. package/dist/layout/use-nav-items.d.ts.map +1 -0
  355. package/dist/layout/use-nav-items.js +39 -0
  356. package/dist/layout/use-nav-items.js.map +1 -0
  357. package/dist/lib/api.d.ts +6 -0
  358. package/dist/lib/api.d.ts.map +1 -1
  359. package/dist/lib/api.js +1 -1
  360. package/dist/lib/api.js.map +1 -1
  361. package/dist/lib/appearance-api.d.ts +3 -1
  362. package/dist/lib/appearance-api.d.ts.map +1 -1
  363. package/dist/lib/appearance-api.js +1 -0
  364. package/dist/lib/appearance-api.js.map +1 -1
  365. package/dist/lib/job-drawer.d.ts +128 -0
  366. package/dist/lib/job-drawer.d.ts.map +1 -0
  367. package/dist/lib/job-drawer.js +351 -0
  368. package/dist/lib/job-drawer.js.map +1 -0
  369. package/dist/lib/job-view.d.ts +187 -0
  370. package/dist/lib/job-view.d.ts.map +1 -0
  371. package/dist/lib/job-view.js +363 -0
  372. package/dist/lib/job-view.js.map +1 -0
  373. package/dist/lib/jobs-service.d.ts +134 -0
  374. package/dist/lib/jobs-service.d.ts.map +1 -0
  375. package/dist/lib/jobs-service.js +179 -0
  376. package/dist/lib/jobs-service.js.map +1 -0
  377. package/dist/lib/sheet-focus.d.ts +24 -0
  378. package/dist/lib/sheet-focus.d.ts.map +1 -0
  379. package/dist/lib/sheet-focus.js +61 -0
  380. package/dist/lib/sheet-focus.js.map +1 -0
  381. package/dist/lib/tiers.d.ts +30 -0
  382. package/dist/lib/tiers.d.ts.map +1 -0
  383. package/dist/lib/tiers.js +43 -0
  384. package/dist/lib/tiers.js.map +1 -0
  385. package/dist/prenta-admin.css +1 -1
  386. package/dist/router/admin-routes.d.ts.map +1 -1
  387. package/dist/router/admin-routes.js +8 -0
  388. package/dist/router/admin-routes.js.map +1 -1
  389. package/dist/router/index.d.ts +5 -1
  390. package/dist/router/index.d.ts.map +1 -1
  391. package/dist/router/index.js +7 -0
  392. package/dist/router/index.js.map +1 -1
  393. package/dist/views/ApiKeys.js +2 -2
  394. package/dist/views/ApiKeys.js.map +1 -1
  395. package/dist/views/AppearanceChrome.d.ts +6 -0
  396. package/dist/views/AppearanceChrome.d.ts.map +1 -0
  397. package/dist/views/AppearanceChrome.js +25 -0
  398. package/dist/views/AppearanceChrome.js.map +1 -0
  399. package/dist/views/CollectionList.d.ts.map +1 -1
  400. package/dist/views/CollectionList.js +73 -53
  401. package/dist/views/CollectionList.js.map +1 -1
  402. package/dist/views/Dashboard.d.ts.map +1 -1
  403. package/dist/views/Dashboard.js +14 -8
  404. package/dist/views/Dashboard.js.map +1 -1
  405. package/dist/views/DocumentEdit.d.ts +6 -1
  406. package/dist/views/DocumentEdit.d.ts.map +1 -1
  407. package/dist/views/DocumentEdit.js +44 -8
  408. package/dist/views/DocumentEdit.js.map +1 -1
  409. package/dist/views/FormDeliveries.d.ts.map +1 -1
  410. package/dist/views/FormDeliveries.js +7 -1
  411. package/dist/views/FormDeliveries.js.map +1 -1
  412. package/dist/views/FormEntries.d.ts.map +1 -1
  413. package/dist/views/FormEntries.js +11 -2
  414. package/dist/views/FormEntries.js.map +1 -1
  415. package/dist/views/FormSubmissions.d.ts +1 -1
  416. package/dist/views/FormSubmissions.d.ts.map +1 -1
  417. package/dist/views/FormSubmissions.js +15 -4
  418. package/dist/views/FormSubmissions.js.map +1 -1
  419. package/dist/views/Forms.d.ts.map +1 -1
  420. package/dist/views/Forms.js +6 -4
  421. package/dist/views/Forms.js.map +1 -1
  422. package/dist/views/FormsSettings.d.ts +2 -0
  423. package/dist/views/FormsSettings.d.ts.map +1 -0
  424. package/dist/views/FormsSettings.js +37 -0
  425. package/dist/views/FormsSettings.js.map +1 -0
  426. package/dist/views/Jobs/BusyButton.d.ts +15 -0
  427. package/dist/views/Jobs/BusyButton.d.ts.map +1 -0
  428. package/dist/views/Jobs/BusyButton.js +26 -0
  429. package/dist/views/Jobs/BusyButton.js.map +1 -0
  430. package/dist/views/Jobs/CancelJobDialog.d.ts +21 -0
  431. package/dist/views/Jobs/CancelJobDialog.d.ts.map +1 -0
  432. package/dist/views/Jobs/CancelJobDialog.js +66 -0
  433. package/dist/views/Jobs/CancelJobDialog.js.map +1 -0
  434. package/dist/views/Jobs/DrawerActivity.d.ts +16 -0
  435. package/dist/views/Jobs/DrawerActivity.d.ts.map +1 -0
  436. package/dist/views/Jobs/DrawerActivity.js +59 -0
  437. package/dist/views/Jobs/DrawerActivity.js.map +1 -0
  438. package/dist/views/Jobs/DrawerOverview.d.ts +28 -0
  439. package/dist/views/Jobs/DrawerOverview.d.ts.map +1 -0
  440. package/dist/views/Jobs/DrawerOverview.js +32 -0
  441. package/dist/views/Jobs/DrawerOverview.js.map +1 -0
  442. package/dist/views/Jobs/DrawerResults.d.ts +25 -0
  443. package/dist/views/Jobs/DrawerResults.d.ts.map +1 -0
  444. package/dist/views/Jobs/DrawerResults.js +50 -0
  445. package/dist/views/Jobs/DrawerResults.js.map +1 -0
  446. package/dist/views/Jobs/DrawerReview.d.ts +44 -0
  447. package/dist/views/Jobs/DrawerReview.d.ts.map +1 -0
  448. package/dist/views/Jobs/DrawerReview.js +110 -0
  449. package/dist/views/Jobs/DrawerReview.js.map +1 -0
  450. package/dist/views/Jobs/JobDrawer.d.ts +31 -0
  451. package/dist/views/Jobs/JobDrawer.d.ts.map +1 -0
  452. package/dist/views/Jobs/JobDrawer.js +353 -0
  453. package/dist/views/Jobs/JobDrawer.js.map +1 -0
  454. package/dist/views/Jobs/JobsTable.d.ts +19 -0
  455. package/dist/views/Jobs/JobsTable.d.ts.map +1 -0
  456. package/dist/views/Jobs/JobsTable.js +80 -0
  457. package/dist/views/Jobs/JobsTable.js.map +1 -0
  458. package/dist/views/Jobs/WorkerStrip.d.ts +22 -0
  459. package/dist/views/Jobs/WorkerStrip.d.ts.map +1 -0
  460. package/dist/views/Jobs/WorkerStrip.js +63 -0
  461. package/dist/views/Jobs/WorkerStrip.js.map +1 -0
  462. package/dist/views/Jobs/atoms.d.ts +19 -0
  463. package/dist/views/Jobs/atoms.d.ts.map +1 -0
  464. package/dist/views/Jobs/atoms.js +52 -0
  465. package/dist/views/Jobs/atoms.js.map +1 -0
  466. package/dist/views/Jobs/focus.d.ts +23 -0
  467. package/dist/views/Jobs/focus.d.ts.map +1 -0
  468. package/dist/views/Jobs/focus.js +54 -0
  469. package/dist/views/Jobs/focus.js.map +1 -0
  470. package/dist/views/Jobs/icons.d.ts +22 -0
  471. package/dist/views/Jobs/icons.d.ts.map +1 -0
  472. package/dist/views/Jobs/icons.js +23 -0
  473. package/dist/views/Jobs/icons.js.map +1 -0
  474. package/dist/views/Jobs/placeholders.d.ts +6 -0
  475. package/dist/views/Jobs/placeholders.d.ts.map +1 -0
  476. package/dist/views/Jobs/placeholders.js +20 -0
  477. package/dist/views/Jobs/placeholders.js.map +1 -0
  478. package/dist/views/Jobs/useJobDetail.d.ts +21 -0
  479. package/dist/views/Jobs/useJobDetail.d.ts.map +1 -0
  480. package/dist/views/Jobs/useJobDetail.js +39 -0
  481. package/dist/views/Jobs/useJobDetail.js.map +1 -0
  482. package/dist/views/Jobs/useJobsFeed.d.ts +35 -0
  483. package/dist/views/Jobs/useJobsFeed.d.ts.map +1 -0
  484. package/dist/views/Jobs/useJobsFeed.js +231 -0
  485. package/dist/views/Jobs/useJobsFeed.js.map +1 -0
  486. package/dist/views/Jobs/useReviewItems.d.ts +39 -0
  487. package/dist/views/Jobs/useReviewItems.d.ts.map +1 -0
  488. package/dist/views/Jobs/useReviewItems.js +140 -0
  489. package/dist/views/Jobs/useReviewItems.js.map +1 -0
  490. package/dist/views/Jobs.d.ts +10 -1
  491. package/dist/views/Jobs.d.ts.map +1 -1
  492. package/dist/views/Jobs.js +126 -35
  493. package/dist/views/Jobs.js.map +1 -1
  494. package/dist/views/MediaBrowser.d.ts.map +1 -1
  495. package/dist/views/MediaBrowser.js +37 -9
  496. package/dist/views/MediaBrowser.js.map +1 -1
  497. package/dist/views/Pages/PagesListView.d.ts +1 -1
  498. package/dist/views/Pages/PagesListView.d.ts.map +1 -1
  499. package/dist/views/Pages/PagesListView.js +34 -5
  500. package/dist/views/Pages/PagesListView.js.map +1 -1
  501. package/dist/views/Posts/NewPostTypeInstructions.d.ts.map +1 -1
  502. package/dist/views/Posts/NewPostTypeInstructions.js +12 -2
  503. package/dist/views/Posts/NewPostTypeInstructions.js.map +1 -1
  504. package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
  505. package/dist/views/Posts/PostTypesView.js +10 -1
  506. package/dist/views/Posts/PostTypesView.js.map +1 -1
  507. package/dist/views/Posts/PostsListView.d.ts +1 -2
  508. package/dist/views/Posts/PostsListView.d.ts.map +1 -1
  509. package/dist/views/Posts/PostsListView.js +31 -3
  510. package/dist/views/Posts/PostsListView.js.map +1 -1
  511. package/dist/views/SEO.d.ts.map +1 -1
  512. package/dist/views/SEO.js +3 -3
  513. package/dist/views/SEO.js.map +1 -1
  514. package/dist/views/ScriptTags.d.ts.map +1 -1
  515. package/dist/views/ScriptTags.js +20 -15
  516. package/dist/views/ScriptTags.js.map +1 -1
  517. package/dist/views/Settings.d.ts.map +1 -1
  518. package/dist/views/Settings.js +115 -51
  519. package/dist/views/Settings.js.map +1 -1
  520. package/dist/views/SpamFolder.d.ts +2 -0
  521. package/dist/views/SpamFolder.d.ts.map +1 -0
  522. package/dist/views/SpamFolder.js +14 -0
  523. package/dist/views/SpamFolder.js.map +1 -0
  524. package/dist/views/Trash.d.ts +6 -1
  525. package/dist/views/Trash.d.ts.map +1 -1
  526. package/dist/views/Trash.js +7 -2
  527. package/dist/views/Trash.js.map +1 -1
  528. package/dist/views/Users.js +2 -2
  529. package/dist/views/Users.js.map +1 -1
  530. package/dist/views/WidgetsPlaceholder.d.ts +2 -0
  531. package/dist/views/WidgetsPlaceholder.d.ts.map +1 -0
  532. package/dist/views/WidgetsPlaceholder.js +15 -0
  533. package/dist/views/WidgetsPlaceholder.js.map +1 -0
  534. package/dist/views/dashboard/kpi-grid.d.ts +18 -0
  535. package/dist/views/dashboard/kpi-grid.d.ts.map +1 -0
  536. package/dist/views/dashboard/kpi-grid.js +36 -0
  537. package/dist/views/dashboard/kpi-grid.js.map +1 -0
  538. package/dist/views/lazy-views.d.ts +4 -0
  539. package/dist/views/lazy-views.d.ts.map +1 -1
  540. package/dist/views/lazy-views.js +4 -0
  541. package/dist/views/lazy-views.js.map +1 -1
  542. package/dist/views/page-editor/PageTab.js +1 -1
  543. package/dist/views/page-editor/PageTab.js.map +1 -1
  544. package/dist/views/page-editor/ReviewBar.js +1 -1
  545. package/dist/views/page-editor/ReviewBar.js.map +1 -1
  546. package/dist/views/page-editor/VersionHistoryPanel.js +1 -1
  547. package/dist/views/page-editor/VersionHistoryPanel.js.map +1 -1
  548. package/dist/views/seo/AuditTab.js +1 -1
  549. package/dist/views/seo/AuditTab.js.map +1 -1
  550. package/dist/views/seo/OverviewTab.d.ts.map +1 -1
  551. package/dist/views/seo/OverviewTab.js +2 -2
  552. package/dist/views/seo/OverviewTab.js.map +1 -1
  553. package/dist/views/seo/ProposalsTab.js +1 -1
  554. package/dist/views/seo/ProposalsTab.js.map +1 -1
  555. package/dist/views/settings/AISettingsTab.d.ts.map +1 -1
  556. package/dist/views/settings/AISettingsTab.js +4 -2
  557. package/dist/views/settings/AISettingsTab.js.map +1 -1
  558. package/dist/views/settings/AccessProtectionCard.js +1 -1
  559. package/dist/views/settings/AccessProtectionCard.js.map +1 -1
  560. package/dist/views/settings/AdminThemeCard.d.ts.map +1 -1
  561. package/dist/views/settings/AdminThemeCard.js +63 -15
  562. package/dist/views/settings/AdminThemeCard.js.map +1 -1
  563. package/dist/views/settings/AppearanceTab.d.ts.map +1 -1
  564. package/dist/views/settings/AppearanceTab.js +4 -2
  565. package/dist/views/settings/AppearanceTab.js.map +1 -1
  566. package/dist/views/settings/BrandPreviewCard.js +1 -1
  567. package/dist/views/settings/BrandPreviewCard.js.map +1 -1
  568. package/dist/views/settings/BrandVoiceCard.js +1 -1
  569. package/dist/views/settings/BrandVoiceCard.js.map +1 -1
  570. package/dist/views/settings/EmailSettingsTab.d.ts.map +1 -1
  571. package/dist/views/settings/EmailSettingsTab.js +5 -3
  572. package/dist/views/settings/EmailSettingsTab.js.map +1 -1
  573. package/dist/views/settings/GeneralSettingsTab.d.ts.map +1 -1
  574. package/dist/views/settings/GeneralSettingsTab.js +4 -2
  575. package/dist/views/settings/GeneralSettingsTab.js.map +1 -1
  576. package/dist/views/settings/ProviderConnectionCard.js +1 -1
  577. package/dist/views/settings/ProviderConnectionCard.js.map +1 -1
  578. package/dist/views/settings/SearchDataCard.js +1 -1
  579. package/dist/views/settings/SearchDataCard.js.map +1 -1
  580. package/dist/views/settings/SecurityStatusCard.js +2 -2
  581. package/dist/views/settings/SecurityStatusCard.js.map +1 -1
  582. package/dist/views/settings/SecurityTab.d.ts.map +1 -1
  583. package/dist/views/settings/SecurityTab.js +4 -2
  584. package/dist/views/settings/SecurityTab.js.map +1 -1
  585. package/dist/views/settings/SeoAutopilotCard.js +2 -2
  586. package/dist/views/settings/SeoAutopilotCard.js.map +1 -1
  587. package/dist/views/settings/SeoSettingsTab.d.ts.map +1 -1
  588. package/dist/views/settings/SeoSettingsTab.js +5 -3
  589. package/dist/views/settings/SeoSettingsTab.js.map +1 -1
  590. package/dist/views/settings/SiteIndexStatusCard.js +1 -1
  591. package/dist/views/settings/SiteIndexStatusCard.js.map +1 -1
  592. package/dist/views/settings/SiteThemeCard.js +1 -1
  593. package/dist/views/settings/UpdatesTab.d.ts.map +1 -1
  594. package/dist/views/settings/UpdatesTab.js +6 -5
  595. package/dist/views/settings/UpdatesTab.js.map +1 -1
  596. package/dist/views/settings/components.d.ts +7 -11
  597. package/dist/views/settings/components.d.ts.map +1 -1
  598. package/dist/views/settings/components.js +18 -12
  599. package/dist/views/settings/components.js.map +1 -1
  600. package/dist/views/settings/useAppearanceSettings.d.ts +7 -1
  601. package/dist/views/settings/useAppearanceSettings.d.ts.map +1 -1
  602. package/dist/views/settings/useAppearanceSettings.js +89 -22
  603. package/dist/views/settings/useAppearanceSettings.js.map +1 -1
  604. package/dist/views/users/UserDetailDrawer.js +1 -1
  605. package/dist/views/users/UserDetailDrawer.js.map +1 -1
  606. package/package.json +10 -8
  607. package/src/AdminRoot.tsx +88 -30
  608. package/src/LoginRoot.tsx +2 -1
  609. package/src/__tests__/components/author-avatar.test.tsx +3 -20
  610. package/src/__tests__/components/notification-bell.test.tsx +146 -14
  611. package/src/__tests__/components/responsive-tiers.render.test.tsx +461 -0
  612. package/src/__tests__/components/serp-preview.render.test.tsx +6 -3
  613. package/src/__tests__/components/shell-primitives.render.test.tsx +951 -0
  614. package/src/__tests__/components/theme-provider-accent.test.tsx +83 -8
  615. package/src/__tests__/components/toggle.test.tsx +11 -3
  616. package/src/__tests__/components/ui-primitives.test.tsx +5 -8
  617. package/src/__tests__/layout/build-nav-items.test.ts +1 -1
  618. package/src/__tests__/layout/shell-nav.render.test.tsx +983 -0
  619. package/src/__tests__/layout/sidebar-forms-submenu.render.test.tsx +8 -4
  620. package/src/__tests__/layout/sidebar-groups.render.test.tsx +116 -0
  621. package/src/__tests__/layout/sidebar-menus.render.test.tsx +116 -0
  622. package/src/__tests__/layout/sidebar-plugin-nav.test.ts +1 -1
  623. package/src/__tests__/layout/sidebar-posts.test.ts +48 -40
  624. package/src/__tests__/layout/sidebar-submenu.render.test.tsx +35 -10
  625. package/src/__tests__/layout/topbar.render.test.tsx +464 -0
  626. package/src/__tests__/lib/api.test.ts +2 -0
  627. package/src/__tests__/lib/job-drawer.test.ts +493 -0
  628. package/src/__tests__/lib/job-view.test.ts +437 -0
  629. package/src/__tests__/lib/jobs-service.test.ts +98 -0
  630. package/src/__tests__/router/admin-route-order.test.ts +39 -0
  631. package/src/__tests__/styles/ai-gradient-contrast.test.ts +135 -0
  632. package/src/__tests__/styles/breakpoint-order.test.ts +68 -0
  633. package/src/__tests__/styles/responsive-layer.test.ts +367 -12
  634. package/src/__tests__/styles/tailwind-preset.test.ts +154 -0
  635. package/src/__tests__/styles/theme-token-scope.test.ts +10 -3
  636. package/src/__tests__/styles/tiers.test.ts +136 -0
  637. package/src/__tests__/styles/tokens-drift.test.ts +67 -6
  638. package/src/__tests__/views/admin-lazy-route.render.test.tsx +41 -1
  639. package/src/__tests__/views/ai-settings.render.test.tsx +4 -4
  640. package/src/__tests__/views/appearance-menus-route.render.test.tsx +58 -0
  641. package/src/__tests__/views/appearance-settings.render.test.tsx +10 -8
  642. package/src/__tests__/views/appearance-theme-provider.render.test.tsx +347 -0
  643. package/src/__tests__/views/dashboard.test.tsx +48 -0
  644. package/src/__tests__/views/document-edit-navigation.render.test.tsx +130 -0
  645. package/src/__tests__/views/document-edit-routes.render.test.tsx +130 -0
  646. package/src/__tests__/views/email-settings.render.test.tsx +2 -2
  647. package/src/__tests__/views/form-entries.render.test.tsx +22 -0
  648. package/src/__tests__/views/form-submissions.render.test.tsx +22 -0
  649. package/src/__tests__/views/forms-list.render.test.tsx +13 -0
  650. package/src/__tests__/views/general-settings.render.test.tsx +38 -11
  651. package/src/__tests__/views/jobs-drawer.render.test.tsx +1047 -0
  652. package/src/__tests__/views/jobs-list.render.test.tsx +850 -0
  653. package/src/__tests__/views/kpi-grid.test.ts +29 -0
  654. package/src/__tests__/views/post-section-editor.test.tsx +5 -1
  655. package/src/__tests__/views/script-tags.render.test.tsx +29 -0
  656. package/src/__tests__/views/security-settings.render.test.tsx +9 -10
  657. package/src/__tests__/views/seo-settings.render.test.tsx +7 -9
  658. package/src/__tests__/views/settings-save-bar.render.test.tsx +91 -0
  659. package/src/__tests__/views/settings-save-footer.render.test.tsx +25 -15
  660. package/src/__tests__/views/settings-tabs.render.test.tsx +371 -0
  661. package/src/__tests__/views/updates-tab.render.test.tsx +4 -3
  662. package/src/components/Breadcrumbs.tsx +7 -1
  663. package/src/components/CommandPalette.tsx +51 -3
  664. package/src/components/LocaleSwitcher.tsx +5 -6
  665. package/src/components/NotificationBell.tsx +74 -18
  666. package/src/components/OfflineStatus.tsx +1 -1
  667. package/src/components/PresenceIndicator.tsx +2 -1
  668. package/src/components/ReflowMeter.tsx +3 -3
  669. package/src/components/SEOPanel.tsx +2 -2
  670. package/src/components/ThemeProvider.tsx +122 -42
  671. package/src/components/TipTapEditor.tsx +3 -4
  672. package/src/components/forms/EmbedPanel.tsx +1 -1
  673. package/src/components/forms/FormEntryDetailPane.tsx +7 -1
  674. package/src/components/forms/ShortcodeGlossary.tsx +7 -1
  675. package/src/components/forms/primitives.tsx +37 -21
  676. package/src/components/pages/tag-badges.tsx +4 -3
  677. package/src/components/seo/PageAuditPanel.tsx +2 -2
  678. package/src/components/seo/SerpPreview.tsx +5 -5
  679. package/src/components/seo/primitives.tsx +2 -2
  680. package/src/components/ui/AuthorAvatar.tsx +11 -10
  681. package/src/components/ui/Badge.tsx +1 -1
  682. package/src/components/ui/Breadcrumb.tsx +109 -0
  683. package/src/components/ui/Button.tsx +40 -15
  684. package/src/components/ui/Card.tsx +35 -32
  685. package/src/components/ui/CardListControls.tsx +90 -0
  686. package/src/components/ui/CountChip.tsx +90 -0
  687. package/src/components/ui/FilterPill.tsx +5 -3
  688. package/src/components/ui/InspectorPane.tsx +21 -7
  689. package/src/components/ui/Modal.tsx +20 -6
  690. package/src/components/ui/PageHeader.tsx +63 -0
  691. package/src/components/ui/RowActions.tsx +7 -2
  692. package/src/components/ui/Segmented.tsx +97 -0
  693. package/src/components/ui/Toaster.tsx +139 -0
  694. package/src/components/ui/Toggle.tsx +18 -14
  695. package/src/components/ui/UnderlineTabs.tsx +373 -0
  696. package/src/components/ui/UnsavedBar.tsx +119 -0
  697. package/src/components/ui/admin-links.tsx +20 -0
  698. package/src/components/ui/card-table.ts +34 -0
  699. package/src/components/virtual-window.tsx +13 -2
  700. package/src/fields/RelationshipField.tsx +1 -1
  701. package/src/hooks/useAppearanceBootstrap.ts +9 -3
  702. package/src/index.ts +19 -0
  703. package/src/layout/Header.tsx +210 -135
  704. package/src/layout/Layout.tsx +61 -108
  705. package/src/layout/MobileTabBar.tsx +92 -0
  706. package/src/layout/Sidebar.tsx +555 -593
  707. package/src/layout/nav-config.tsx +425 -0
  708. package/src/layout/primitives/AdminShell.tsx +154 -6
  709. package/src/layout/topbar-classes.ts +8 -0
  710. package/src/layout/use-nav-items.ts +40 -0
  711. package/src/lib/api.ts +7 -1
  712. package/src/lib/appearance-api.ts +4 -0
  713. package/src/lib/job-drawer.ts +424 -0
  714. package/src/lib/job-view.ts +456 -0
  715. package/src/lib/jobs-service.ts +290 -0
  716. package/src/lib/sheet-focus.ts +71 -0
  717. package/src/lib/tiers.ts +43 -0
  718. package/src/router/admin-routes.ts +8 -0
  719. package/src/router/index.ts +9 -1
  720. package/src/styles/prenta-tokens.css +263 -82
  721. package/src/styles/responsive.css +460 -22
  722. package/src/styles/theme.css +177 -28
  723. package/src/views/ApiKeys.tsx +2 -2
  724. package/src/views/AppearanceChrome.tsx +74 -0
  725. package/src/views/CollectionList.tsx +264 -205
  726. package/src/views/Dashboard.tsx +26 -13
  727. package/src/views/DocumentEdit.tsx +111 -56
  728. package/src/views/FormDeliveries.tsx +30 -17
  729. package/src/views/FormEntries.tsx +28 -20
  730. package/src/views/FormSubmissions.tsx +32 -34
  731. package/src/views/Forms.tsx +55 -34
  732. package/src/views/FormsSettings.tsx +94 -0
  733. package/src/views/Jobs/BusyButton.tsx +56 -0
  734. package/src/views/Jobs/CancelJobDialog.tsx +127 -0
  735. package/src/views/Jobs/DrawerActivity.tsx +142 -0
  736. package/src/views/Jobs/DrawerOverview.tsx +139 -0
  737. package/src/views/Jobs/DrawerResults.tsx +155 -0
  738. package/src/views/Jobs/DrawerReview.tsx +464 -0
  739. package/src/views/Jobs/JobDrawer.tsx +556 -0
  740. package/src/views/Jobs/JobsTable.tsx +260 -0
  741. package/src/views/Jobs/WorkerStrip.tsx +144 -0
  742. package/src/views/Jobs/atoms.tsx +93 -0
  743. package/src/views/Jobs/focus.ts +56 -0
  744. package/src/views/Jobs/icons.tsx +111 -0
  745. package/src/views/Jobs/placeholders.tsx +66 -0
  746. package/src/views/Jobs/useJobDetail.ts +64 -0
  747. package/src/views/Jobs/useJobsFeed.ts +249 -0
  748. package/src/views/Jobs/useReviewItems.ts +187 -0
  749. package/src/views/Jobs.tsx +282 -105
  750. package/src/views/MediaBrowser.tsx +71 -21
  751. package/src/views/Pages/PagesListView.tsx +115 -39
  752. package/src/views/Posts/NewPostTypeInstructions.tsx +15 -10
  753. package/src/views/Posts/PostTypesView.tsx +28 -6
  754. package/src/views/Posts/PostsListView.tsx +130 -40
  755. package/src/views/SEO.tsx +23 -8
  756. package/src/views/ScriptTags.tsx +233 -153
  757. package/src/views/Settings.tsx +215 -256
  758. package/src/views/SpamFolder.tsx +32 -0
  759. package/src/views/Trash.tsx +12 -1
  760. package/src/views/Users.tsx +2 -2
  761. package/src/views/WidgetsPlaceholder.tsx +33 -0
  762. package/src/views/dashboard/kpi-grid.ts +38 -0
  763. package/src/views/lazy-views.tsx +12 -0
  764. package/src/views/page-editor/PageTab.tsx +2 -2
  765. package/src/views/page-editor/ReviewBar.tsx +1 -1
  766. package/src/views/page-editor/VersionHistoryPanel.tsx +1 -1
  767. package/src/views/seo/AuditTab.tsx +1 -1
  768. package/src/views/seo/OverviewTab.tsx +6 -5
  769. package/src/views/seo/ProposalsTab.tsx +1 -1
  770. package/src/views/settings/AISettingsTab.tsx +10 -6
  771. package/src/views/settings/AccessProtectionCard.tsx +1 -1
  772. package/src/views/settings/AdminThemeCard.tsx +228 -35
  773. package/src/views/settings/AppearanceTab.tsx +10 -6
  774. package/src/views/settings/BrandPreviewCard.tsx +1 -1
  775. package/src/views/settings/BrandVoiceCard.tsx +1 -1
  776. package/src/views/settings/EmailSettingsTab.tsx +11 -7
  777. package/src/views/settings/GeneralSettingsTab.tsx +10 -6
  778. package/src/views/settings/ProviderConnectionCard.tsx +1 -1
  779. package/src/views/settings/SearchDataCard.tsx +1 -1
  780. package/src/views/settings/SecurityStatusCard.tsx +2 -2
  781. package/src/views/settings/SecurityTab.tsx +10 -6
  782. package/src/views/settings/SeoAutopilotCard.tsx +2 -2
  783. package/src/views/settings/SeoSettingsTab.tsx +13 -9
  784. package/src/views/settings/SiteIndexStatusCard.tsx +1 -1
  785. package/src/views/settings/SiteThemeCard.tsx +1 -1
  786. package/src/views/settings/UpdatesTab.tsx +107 -101
  787. package/src/views/settings/components.tsx +26 -62
  788. package/src/views/settings/useAppearanceSettings.ts +116 -25
  789. package/src/views/users/UserDetailDrawer.tsx +1 -1
  790. package/dist/components/ui/StatPill.d.ts +0 -18
  791. package/dist/components/ui/StatPill.d.ts.map +0 -1
  792. package/dist/components/ui/StatPill.js +0 -5
  793. package/dist/components/ui/StatPill.js.map +0 -1
  794. package/src/components/ui/StatPill.tsx +0 -37
@@ -0,0 +1,425 @@
1
+ import {
2
+ BookOpen,
3
+ Briefcase,
4
+ Clipboard,
5
+ ClipboardList,
6
+ Code,
7
+ Cpu,
8
+ Download,
9
+ File,
10
+ FileText,
11
+ FolderOpen,
12
+ HelpCircle,
13
+ Image,
14
+ Inbox,
15
+ Key,
16
+ Layers,
17
+ LayoutGrid,
18
+ ListChecks,
19
+ ListTree,
20
+ Lock,
21
+ Mail,
22
+ Menu as MenuIcon,
23
+ Newspaper,
24
+ Palette,
25
+ PanelBottom,
26
+ PanelTop,
27
+ PanelsTopLeft,
28
+ Plug,
29
+ Search as SearchIcon,
30
+ Settings,
31
+ ShieldX,
32
+ TextAlignStart,
33
+ Trash2,
34
+ Users,
35
+ Webhook,
36
+ } from 'lucide-react'
37
+ import type { LucideIcon } from 'lucide-react'
38
+ import type { ReactElement } from 'react'
39
+ import type { FormsSidebarCount } from '../lib/forms-service.js'
40
+
41
+ /**
42
+ * The admin's single nav config (SHELL.md §2 "Nav order (exact)", TRACKING
43
+ * P-02). It ports the kit's base NAV plus `nav-patches.js`: `forms()` (Forms
44
+ * children with per-form unread counts, nested as `cms-layouts.js` does),
45
+ * `appearance()` (a group above where Templates would be) and `jobs()` (above
46
+ * Settings, opening SETTINGS).
47
+ *
48
+ * Kit order:
49
+ * Dashboard · Pages › · Posts › · Appearance ›
50
+ * ── CONTENT ── Media · Forms › · SEO · Script Tags · <collections, plugins>
51
+ * <config collection groups, each under its own label>
52
+ * ── SETTINGS ── Jobs · Settings
53
+ *
54
+ * Templates is left out until its screen exists (CONFLICTS.md §2: never link
55
+ * to a redirect).
56
+ */
57
+
58
+ export interface NavItem {
59
+ path: string
60
+ label: string
61
+ icon: LucideIcon | NavIconComponent
62
+ /**
63
+ * Section label. Consecutive items sharing a `group` render under one
64
+ * header, shown when the group changes. Items without one render unlabelled.
65
+ */
66
+ group?: string
67
+ /** Children, shown as a sub-group (expanded sidebar) or a flyout (rail). */
68
+ children?: NavItem[]
69
+ /** Unread count. Zero or undefined renders nothing. */
70
+ badge?: number
71
+ /**
72
+ * Contributed by the config or a plugin rather than the kit. The mobile tab
73
+ * bar leaves these to the "More" drawer.
74
+ */
75
+ extra?: boolean
76
+ /** Icon size in px. Kit: 16 for top-level rows, 14 for patched children. */
77
+ iconSize?: number
78
+ }
79
+
80
+ type NavIconComponent = (props: {
81
+ className?: string
82
+ size?: number
83
+ strokeWidth?: number
84
+ }) => ReactElement
85
+
86
+ /** Per-form row marker: the kit's 12px dot (`circle r=2.5`). */
87
+ function FormDot({
88
+ className,
89
+ size = 12,
90
+ strokeWidth = 1.75,
91
+ }: {
92
+ className?: string
93
+ size?: number
94
+ strokeWidth?: number
95
+ }) {
96
+ return (
97
+ <svg
98
+ width={size}
99
+ height={size}
100
+ viewBox="0 0 24 24"
101
+ fill="none"
102
+ stroke="currentColor"
103
+ strokeWidth={strokeWidth}
104
+ className={className}
105
+ aria-hidden
106
+ >
107
+ <circle cx="12" cy="12" r="2.5" />
108
+ </svg>
109
+ )
110
+ }
111
+
112
+ const ICON_MAP: Record<string, LucideIcon> = {
113
+ file: File,
114
+ 'file-text': FileText,
115
+ briefcase: Briefcase,
116
+ folder: FolderOpen,
117
+ book: BookOpen,
118
+ help: HelpCircle,
119
+ newspaper: Newspaper,
120
+ navigation: ListTree,
121
+ menu: MenuIcon,
122
+ 'list-tree': ListTree,
123
+ PanelTop: PanelTop,
124
+ PanelBottom: PanelBottom,
125
+ Layers: Layers,
126
+ }
127
+
128
+ const PAGES_CHILDREN: NavItem[] = [
129
+ { path: '/pages', label: 'All Pages', icon: PanelsTopLeft },
130
+ { path: '/pages/trash', label: 'Trash', icon: Trash2 },
131
+ ]
132
+
133
+ /**
134
+ * Post Types are reached through the `/posts/types` cards page, not listed
135
+ * one per type, so the shape is the same however many types exist.
136
+ */
137
+ const POSTS_CHILDREN: NavItem[] = [
138
+ { path: '/posts', label: 'All Posts', icon: File },
139
+ { path: '/posts/types', label: 'Post Types', icon: Clipboard },
140
+ { path: '/posts/trash', label: 'Trash', icon: Trash2 },
141
+ ]
142
+
143
+ /**
144
+ * Appearance → Menus opens plugin-navigation's `navigations` collection: its
145
+ * list and NavBuilder editor are the working menu editor.
146
+ */
147
+ const MENUS_SLUG = 'navigations'
148
+ const MENUS_PATH = `/${MENUS_SLUG}`
149
+
150
+ /** Without the navigation plugin there are no menus, so Menus would 404. */
151
+ function appearanceItem(hasMenus: boolean): NavItem {
152
+ const children: NavItem[] = [
153
+ ...(hasMenus ? [{ path: MENUS_PATH, label: 'Menus', icon: TextAlignStart, iconSize: 14 }] : []),
154
+ { path: '/appearance/header', label: 'Header & Footer', icon: PanelsTopLeft, iconSize: 14 },
155
+ { path: '/appearance/widgets', label: 'Widgets', icon: LayoutGrid, iconSize: 14 },
156
+ ]
157
+ // No Appearance landing screen exists, so the parent opens its first child.
158
+ return { path: children[0]!.path, label: 'Appearance', icon: Palette, children }
159
+ }
160
+
161
+ const DASHBOARD: NavItem = { path: '/', label: 'Dashboard', icon: LayoutGrid }
162
+ const PAGES: NavItem = {
163
+ path: '/pages',
164
+ label: 'Pages',
165
+ icon: PanelsTopLeft,
166
+ children: PAGES_CHILDREN,
167
+ }
168
+ const POSTS: NavItem = { path: '/posts', label: 'Posts', icon: File, children: POSTS_CHILDREN }
169
+
170
+ const CONTENT_STATIC: NavItem[] = [
171
+ { path: '/media', label: 'Media', icon: Image, group: 'Content' },
172
+ { path: '/forms', label: 'Forms', icon: Clipboard, group: 'Content' },
173
+ { path: '/seo', label: 'SEO', icon: SearchIcon, group: 'Content' },
174
+ { path: '/script-tags', label: 'Script Tags', icon: Code, group: 'Content' },
175
+ ]
176
+
177
+ const SETTINGS_ITEMS: NavItem[] = [
178
+ { path: '/jobs', label: 'Jobs', icon: ListChecks, group: 'Settings' },
179
+ { path: '/settings', label: 'Settings', icon: Settings, group: 'Settings' },
180
+ ]
181
+
182
+ function resolveIcon(collection: any): LucideIcon {
183
+ const mapped = collection.admin?.icon ? ICON_MAP[collection.admin.icon] : undefined
184
+ if (mapped) return mapped
185
+ return collection.type === 'page' ? File : FileText
186
+ }
187
+
188
+ /**
189
+ * Build the nav tree from the CMS config. A config-less install gets the
190
+ * kit's default tree (no navigations collection, so no Menus).
191
+ *
192
+ * Repo-only destinations follow CONFLICTS.md §2: the `pages` collection is
193
+ * the kit's Pages item; other collections and plugin nav links continue the
194
+ * CONTENT block after Script Tags; collections with their own `admin.group`
195
+ * follow under their own labels; Users and API Keys live in Settings tabs.
196
+ */
197
+ export function buildNavItems(config: any): NavItem[] {
198
+ if (!config?.collections) {
199
+ return [DASHBOARD, PAGES, POSTS, appearanceItem(false), ...CONTENT_STATIC, ...SETTINGS_ITEMS]
200
+ }
201
+
202
+ const raw = config.collections
203
+ const collectionsList: any[] = Array.isArray(raw) ? raw : Object.values(raw)
204
+ // Appearance → Menus reaches the navigations collection, so neither it nor
205
+ // the plugin's own nav link to it is listed again.
206
+ const hasMenus = collectionsList.some((c) => c.slug === MENUS_SLUG)
207
+ const visible = collectionsList.filter((c) => !c.admin?.hidden && c.slug !== MENUS_SLUG)
208
+
209
+ // `admin.group: 'Posts'` (case-insensitive) is a synonym for `type: 'post'`.
210
+ const isPostsGroup = (c: any) =>
211
+ typeof c.admin?.group === 'string' && c.admin.group.toLowerCase() === 'posts'
212
+ const hasPages = visible.some((c) => c.slug === 'pages')
213
+ const hasPosts = visible.some((c) => c.type === 'post' || isPostsGroup(c))
214
+ // Every other ungrouped collection continues CONTENT after Script Tags.
215
+ const ungrouped = visible.filter(
216
+ (c) => c.slug !== 'pages' && c.type !== 'post' && !isPostsGroup(c) && !c.admin?.group,
217
+ )
218
+ const grouped = visible.filter((c) => c.admin?.group && !isPostsGroup(c) && c.type !== 'post')
219
+
220
+ const items: NavItem[] = [DASHBOARD]
221
+ if (hasPages) items.push(PAGES)
222
+ if (hasPosts) items.push(POSTS)
223
+ items.push(appearanceItem(hasMenus), ...CONTENT_STATIC)
224
+
225
+ const taken = (path: string) =>
226
+ items.some((n) => n.path === path || n.children?.some((c) => c.path === path))
227
+
228
+ for (const collection of ungrouped) {
229
+ const path = `/${collection.slug}`
230
+ if (taken(path)) continue
231
+ items.push({
232
+ label: collection.labels?.plural ?? collection.slug,
233
+ path,
234
+ icon: resolveIcon(collection),
235
+ group: 'Content',
236
+ extra: true,
237
+ })
238
+ }
239
+
240
+ // Plugin-contributed nav links (PluginDefinition.adminNavItems, collected
241
+ // onto `_pluginNavItems` by cms-core's applyPlugins), deduped by path so a
242
+ // plugin can't double a built-in destination or one of its own collections.
243
+ const pluginNav: Array<{ label?: string; icon?: string; path?: string }> = Array.isArray(
244
+ config?._pluginNavItems,
245
+ )
246
+ ? config._pluginNavItems
247
+ : []
248
+ for (const item of pluginNav) {
249
+ if (!item?.path || (hasMenus && item.path === MENUS_PATH) || taken(item.path)) continue
250
+ items.push({
251
+ label: item.label ?? item.path,
252
+ path: item.path,
253
+ icon: (item.icon && ICON_MAP[item.icon]) || FileText,
254
+ group: 'Content',
255
+ extra: true,
256
+ })
257
+ }
258
+
259
+ // Config collection groups, each under its own label. A grouped collection
260
+ // whose path a built-in already provides is skipped: plugin-forms' `forms`
261
+ // (admin.group 'Forms', path `/forms`) would otherwise double Forms.
262
+ const groupBuckets = new Map<string, NavItem[]>()
263
+ for (const col of grouped) {
264
+ const path = `/${col.slug}`
265
+ if (taken(path)) continue
266
+ const group = col.admin.group as string
267
+ const bucket = groupBuckets.get(group) ?? []
268
+ bucket.push({
269
+ label: col.labels?.plural ?? col.slug,
270
+ path,
271
+ icon: resolveIcon(col),
272
+ group,
273
+ extra: true,
274
+ })
275
+ groupBuckets.set(group, bucket)
276
+ }
277
+ for (const bucket of groupBuckets.values()) items.push(...bucket)
278
+
279
+ items.push(...SETTINGS_ITEMS)
280
+ return items
281
+ }
282
+
283
+ /**
284
+ * Forms children as in the kit: All Entries and All Forms each open a third
285
+ * level with one row per form (`cms-layouts.js` "3rd-level nav nesting"),
286
+ * followed by Spam, SMTP Log and Form Settings. The parent and All Entries
287
+ * carry the total unread count; each form's entries row carries its own.
288
+ *
289
+ * `forms` comes from `/forms/sidebar-counts`, which returns active forms only,
290
+ * so drafts aren't listed under All Forms yet and Spam has no count.
291
+ */
292
+ export function applyFormsNav(items: NavItem[], forms: FormsSidebarCount[]): NavItem[] {
293
+ const totalUnread = forms.reduce((sum, f) => sum + (f.unread ?? 0), 0)
294
+ return items.map((item) => {
295
+ if (item.path !== '/forms') return item
296
+ const children: NavItem[] = [
297
+ {
298
+ path: '/forms/entries',
299
+ label: 'All Entries',
300
+ icon: Inbox,
301
+ badge: totalUnread,
302
+ iconSize: 14,
303
+ children: forms.map((f) => ({
304
+ path: `/forms/${f.formId}/submissions`,
305
+ label: f.name,
306
+ icon: FormDot,
307
+ iconSize: 12,
308
+ badge: f.unread,
309
+ })),
310
+ },
311
+ {
312
+ path: '/forms',
313
+ label: 'All Forms',
314
+ icon: ClipboardList,
315
+ iconSize: 14,
316
+ children: forms.map((f) => ({
317
+ path: `/forms/${f.formId}/edit`,
318
+ label: f.name,
319
+ icon: FormDot,
320
+ iconSize: 12,
321
+ })),
322
+ },
323
+ { path: '/forms/spam', label: 'Spam', icon: ShieldX, iconSize: 14 },
324
+ { path: '/forms/deliveries', label: 'SMTP Log', icon: Mail, iconSize: 14 },
325
+ { path: '/forms/settings', label: 'Form Settings', icon: Settings, iconSize: 14 },
326
+ ]
327
+ return { ...item, badge: totalUnread > 0 ? totalUnread : undefined, children }
328
+ })
329
+ }
330
+
331
+ /**
332
+ * The Settings tabs in strip order: Settings.html `TABS` with its 15px icons,
333
+ * plus the repo's Layout and Webhooks. One list for the Settings tab strip and
334
+ * the mobile drawer, so each tab reads the same in both. `layout` exists only
335
+ * when the site defines layout regions.
336
+ */
337
+ export const SETTINGS_TABS: ReadonlyArray<{ id: string; label: string; icon: LucideIcon }> = [
338
+ { id: 'general', label: 'General', icon: Settings },
339
+ { id: 'appearance', label: 'Admin appearance', icon: Palette },
340
+ { id: 'layout', label: 'Layout', icon: Layers },
341
+ { id: 'security', label: 'Security', icon: Lock },
342
+ { id: 'users', label: 'Users', icon: Users },
343
+ { id: 'mcp', label: 'MCP', icon: Plug },
344
+ { id: 'api-keys', label: 'API Keys', icon: Key },
345
+ { id: 'seo', label: 'SEO', icon: SearchIcon },
346
+ { id: 'email', label: 'Email (SMTP)', icon: Mail },
347
+ { id: 'webhooks', label: 'Webhooks', icon: Webhook },
348
+ { id: 'ai', label: 'AI', icon: Cpu },
349
+ { id: 'updates', label: 'Updates', icon: Download },
350
+ ]
351
+
352
+ /**
353
+ * The mobile drawer (behind the tab bar's More) also lists the Settings tabs
354
+ * (CONFLICTS.md §2, DECIDED mobile nav), which don't fit in the bar. Layout
355
+ * only applies this below 769px; the desktop sidebar keeps Settings a leaf.
356
+ */
357
+ export function withSettingsTabs(
358
+ items: NavItem[],
359
+ config: { layout?: { regions?: Record<string, unknown> } } | null | undefined,
360
+ ): NavItem[] {
361
+ const hasLayout = Object.keys(config?.layout?.regions ?? {}).length > 0
362
+ const tab = (id: string, label: string, icon: LucideIcon): NavItem => ({
363
+ path: `/settings?tab=${id}`,
364
+ label,
365
+ icon,
366
+ iconSize: 14,
367
+ })
368
+ const children: NavItem[] = SETTINGS_TABS.filter(
369
+ (settingsTab) => settingsTab.id !== 'layout' || hasLayout,
370
+ ).map((settingsTab) => tab(settingsTab.id, settingsTab.label, settingsTab.icon))
371
+ return items.map((item) => (item.path === '/settings' ? { ...item, children } : item))
372
+ }
373
+
374
+ /**
375
+ * Whether `itemPath` is the current page. Query and hash are ignored so
376
+ * state-bearing routes (`/seo?tab=audit`) still match.
377
+ */
378
+ export function isPathActive(currentPath: string, itemPath: string): boolean {
379
+ const path = currentPath.split('?')[0]!.split('#')[0]!
380
+ // A query in the item path (Settings tabs, `/settings?tab=security`) must
381
+ // match too; Settings without `?tab=` shows General.
382
+ if (itemPath.includes('?')) {
383
+ const [itemPathname, itemQuery] = itemPath.split('?') as [string, string]
384
+ if (path !== itemPathname) return false
385
+ const current = new URLSearchParams(currentPath.split('#')[0]!.split('?')[1] ?? '')
386
+ const wanted = new URLSearchParams(itemQuery)
387
+ for (const [key, value] of wanted) {
388
+ const have = current.get(key) ?? (key === 'tab' ? 'general' : null)
389
+ if (have !== value) return false
390
+ }
391
+ return true
392
+ }
393
+ if (itemPath === '/') return path === '/'
394
+ // `/posts` is a prefix of `/posts/types`; All Posts is exact-only.
395
+ if (itemPath === '/posts') return path === '/posts' || path === '/posts/new'
396
+ return path === itemPath || path.startsWith(itemPath + '/')
397
+ }
398
+
399
+ /**
400
+ * The row that owns the current page, searching every level below: the
401
+ * longest matching path wins, so All Pages (`/pages`) yields to Trash on
402
+ * `/pages/trash` and All Forms (`/forms`) to All Entries on `/forms/entries`.
403
+ */
404
+ export function activeChildPath(children: NavItem[], currentPath: string): string | undefined {
405
+ let best: string | undefined
406
+ const visit = (items: NavItem[]) => {
407
+ for (const child of items) {
408
+ if (isPathActive(currentPath, child.path) && (!best || child.path.length > best.length)) {
409
+ best = child.path
410
+ }
411
+ if (child.children) visit(child.children)
412
+ }
413
+ }
414
+ visit(children)
415
+ return best
416
+ }
417
+
418
+ /** True when the current page is one of `items` or anything nested below them. */
419
+ export function hasActiveDescendant(items: NavItem[], currentPath: string): boolean {
420
+ return items.some(
421
+ (item) =>
422
+ isPathActive(currentPath, item.path) ||
423
+ (!!item.children && hasActiveDescendant(item.children, currentPath)),
424
+ )
425
+ }
@@ -1,5 +1,6 @@
1
- import { useEffect, useState } from 'react'
1
+ import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent } from 'react'
2
2
  import type { ReactNode } from 'react'
3
+ import { X } from 'lucide-react'
3
4
 
4
5
  export interface AdminShellProps {
5
6
  /** Sidebar/navigation slot. Rendered in the left column on desktop. */
@@ -10,10 +11,16 @@ export interface AdminShellProps {
10
11
  breadcrumbs?: ReactNode
11
12
  /** Main content. Scrolls independently of the sidebar. */
12
13
  children: ReactNode
14
+ /**
15
+ * Mobile bottom tab bar, rendered below `desktopBreakpoint` only. The main
16
+ * area reserves `--tabbar-h` at the bottom so content isn't hidden behind it.
17
+ */
18
+ tabBar?: ReactNode
13
19
  /**
14
20
  * Whether the mobile sidebar overlay is currently open. Controlled.
15
21
  * AdminShell renders the backdrop + slide transform; the consumer owns
16
- * the open state and the hamburger button that toggles it.
22
+ * the open state and the control that toggles it (the admin's tab bar
23
+ * More).
17
24
  */
18
25
  mobileSidebarOpen?: boolean
19
26
  onMobileSidebarClose?: () => void
@@ -24,6 +31,27 @@ export interface AdminShellProps {
24
31
  desktopBreakpoint?: number
25
32
  }
26
33
 
34
+ /** The mobile drawer's id, for an opener's `aria-controls`. */
35
+ export const DRAWER_ID = 'prenta-nav-drawer'
36
+
37
+ const FOCUSABLE =
38
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
39
+
40
+ function setInert(el: HTMLElement | null, inert: boolean) {
41
+ if (el) (el as HTMLElement & { inert?: boolean }).inert = inert
42
+ }
43
+
44
+ /**
45
+ * The drawer's Tab stops: rendered (a `display: none` ancestor leaves no
46
+ * client rects, like the sidebar's collapse footer below 1025px) and not
47
+ * inert.
48
+ */
49
+ function tabStops(root: HTMLElement): HTMLElement[] {
50
+ return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(
51
+ (el) => el.getClientRects().length > 0 && !el.closest('[inert]'),
52
+ )
53
+ }
54
+
27
55
  /**
28
56
  * The canonical admin chrome. Owns the entire viewport, splits into
29
57
  * `sidebar | (header + breadcrumbs + content)`, and handles the mobile
@@ -47,11 +75,17 @@ export function AdminShell({
47
75
  header,
48
76
  breadcrumbs,
49
77
  children,
78
+ tabBar,
50
79
  mobileSidebarOpen = false,
51
80
  onMobileSidebarClose,
52
81
  desktopBreakpoint = 768,
53
82
  }: AdminShellProps) {
54
83
  const [isDesktop, setIsDesktop] = useState(false)
84
+ const sidebarRef = useRef<HTMLDivElement>(null)
85
+ const contentRef = useRef<HTMLDivElement>(null)
86
+ const tabBarRef = useRef<HTMLDivElement>(null)
87
+ const skipRef = useRef<HTMLAnchorElement>(null)
88
+ const drawerOpen = !isDesktop && mobileSidebarOpen
55
89
 
56
90
  useEffect(() => {
57
91
  if (typeof window === 'undefined') return
@@ -62,6 +96,74 @@ export function AdminShell({
62
96
  return () => mq.removeEventListener('change', handler)
63
97
  }, [desktopBreakpoint])
64
98
 
99
+ // The closed mobile drawer is off-screen: keep it out of the tab order and
100
+ // the accessibility tree. Set on the element because React 18 has no
101
+ // `inert` prop.
102
+ useEffect(() => {
103
+ setInert(sidebarRef.current, !isDesktop && !mobileSidebarOpen)
104
+ }, [isDesktop, mobileSidebarOpen])
105
+
106
+ // The drawer only exists below the breakpoint: close it on the way out, or
107
+ // it would reopen by itself when the window narrows again.
108
+ useEffect(() => {
109
+ if (isDesktop && mobileSidebarOpen) onMobileSidebarClose?.()
110
+ }, [isDesktop, mobileSidebarOpen, onMobileSidebarClose])
111
+
112
+ // The open drawer is modal: the skip link, the content and the tab bar go
113
+ // inert, focus moves in (to its close button), and on close it returns to
114
+ // the opener (the tab bar's More, or a custom header's menu button). One effect so the
115
+ // opener is read before it goes inert and un-inerted before it takes focus
116
+ // back.
117
+ useEffect(() => {
118
+ if (!drawerOpen) return
119
+ const opener = document.activeElement as HTMLElement | null
120
+ const behind = [skipRef.current, contentRef.current, tabBarRef.current]
121
+ for (const el of behind) setInert(el, true)
122
+ const drawer = sidebarRef.current
123
+ if (drawer) (tabStops(drawer)[0] ?? drawer).focus()
124
+ // App shortcuts listening on `document` would open UI in the inert column
125
+ // (the command palette's Ctrl/Cmd+K); a modal ignores them.
126
+ const blockShortcuts = (e: KeyboardEvent) => {
127
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
128
+ e.stopPropagation()
129
+ // Also keep the browser's own Ctrl+K (focus the address bar) from
130
+ // firing; Shift combos (e.g. Edge's Duplicate tab) are left alone.
131
+ if (!e.shiftKey) e.preventDefault()
132
+ }
133
+ }
134
+ document.addEventListener('keydown', blockShortcuts, true)
135
+ return () => {
136
+ document.removeEventListener('keydown', blockShortcuts, true)
137
+ for (const el of behind) setInert(el, false)
138
+ if (opener && opener !== document.body && document.contains(opener)) opener.focus()
139
+ }
140
+ }, [drawerOpen])
141
+
142
+ // Esc closes the drawer; Tab and Shift+Tab stay inside it. Keys from
143
+ // portals rendered in the drawer's React tree (a Radix menu's Esc) bubble
144
+ // here too; they belong to that layer.
145
+ const onDrawerKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => {
146
+ if (!drawerOpen || e.defaultPrevented) return
147
+ if (!e.currentTarget.contains(e.target as Node)) return
148
+ if (e.key === 'Escape') {
149
+ e.stopPropagation()
150
+ onMobileSidebarClose?.()
151
+ return
152
+ }
153
+ if (e.key !== 'Tab') return
154
+ const items = tabStops(e.currentTarget)
155
+ if (items.length === 0) return
156
+ const first = items[0]!
157
+ const last = items[items.length - 1]!
158
+ if (e.shiftKey && document.activeElement === first) {
159
+ e.preventDefault()
160
+ last.focus()
161
+ } else if (!e.shiftKey && document.activeElement === last) {
162
+ e.preventDefault()
163
+ first.focus()
164
+ }
165
+ }
166
+
65
167
  return (
66
168
  <div
67
169
  className="bg-background text-foreground h-screen overflow-hidden"
@@ -77,7 +179,7 @@ export function AdminShell({
77
179
  * focused; styling lives in `responsive.css` because it has to survive
78
180
  * a consumer whose Tailwind build does not scan this file.
79
181
  */}
80
- <a className="prenta-skip-link" href="#prenta-main">
182
+ <a ref={skipRef} className="prenta-skip-link" href="#prenta-main">
81
183
  Skip to content
82
184
  </a>
83
185
 
@@ -90,7 +192,17 @@ export function AdminShell({
90
192
  )}
91
193
 
92
194
  <div
93
- className="z-50"
195
+ ref={sidebarRef}
196
+ id={DRAWER_ID}
197
+ // Below the desktop breakpoint the open sidebar is a modal drawer.
198
+ role={drawerOpen ? 'dialog' : undefined}
199
+ aria-modal={drawerOpen ? true : undefined}
200
+ aria-label={drawerOpen ? 'Navigation' : undefined}
201
+ tabIndex={drawerOpen ? -1 : undefined}
202
+ onKeyDown={onDrawerKeyDown}
203
+ // A class, not an inline transition, so the in-app Reduce motion
204
+ // setting (and prefers-reduced-motion) can stop the slide.
205
+ className={`z-50 outline-none ${isDesktop ? '' : 'motion-safe:transition-transform motion-safe:duration-300 motion-safe:ease-out'}`}
94
206
  style={
95
207
  isDesktop
96
208
  ? {
@@ -104,14 +216,27 @@ export function AdminShell({
104
216
  bottom: 0,
105
217
  left: 0,
106
218
  transform: mobileSidebarOpen ? 'translateX(0)' : 'translateX(-100%)',
107
- transition: 'transform 300ms ease',
108
219
  }
109
220
  }
110
221
  >
222
+ {/* The drawer's own dismiss: the scrim is pointer-only, and its
223
+ opener is inert behind the modal. First in the drawer, so it takes
224
+ focus on open. */}
225
+ {!isDesktop && onMobileSidebarClose ? (
226
+ <button
227
+ type="button"
228
+ onClick={onMobileSidebarClose}
229
+ aria-label="Close navigation"
230
+ className="rounded-7 text-sub hover:bg-acc-l hover:text-acc prenta-touch-target absolute top-2.5 right-2.5 z-[1] flex h-9 w-9 cursor-pointer items-center justify-center border-0 bg-transparent p-0"
231
+ >
232
+ <X className="h-4 w-4" strokeWidth={1.75} aria-hidden />
233
+ </button>
234
+ ) : null}
111
235
  {sidebar}
112
236
  </div>
113
237
 
114
238
  <div
239
+ ref={contentRef}
115
240
  className="flex flex-col overflow-hidden"
116
241
  style={
117
242
  isDesktop
@@ -121,10 +246,33 @@ export function AdminShell({
121
246
  >
122
247
  {header}
123
248
  {breadcrumbs}
124
- <main id="prenta-main" className="min-h-0 flex-1 overflow-y-auto">
249
+ {/* scroll-padding-bottom keeps a focused field clear of the floating
250
+ unsaved bar (52px, 16px up) when focus scrolls it into view
251
+ (WCAG 2.4.11); on mobile the fixed tab bar covers another
252
+ --tabbar-h, which the content also reserves at its end.
253
+ `relative`: visually hidden text (`sr-only`, absolutely positioned)
254
+ with no positioned ancestor otherwise sized the fixed admin root,
255
+ and on long pages the whole shell scrolled. */}
256
+ <main
257
+ id="prenta-main"
258
+ className="relative min-h-0 flex-1 scroll-pb-[84px] overflow-x-hidden overflow-y-auto"
259
+ style={
260
+ !isDesktop && tabBar
261
+ ? {
262
+ // --fab-clear: room for a floating action button
263
+ // (responsive.css sets it while the page has one).
264
+ paddingBottom:
265
+ 'calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--fab-clear, 0px))',
266
+ scrollPaddingBottom: 'calc(84px + var(--tabbar-h) + env(safe-area-inset-bottom))',
267
+ }
268
+ : undefined
269
+ }
270
+ >
125
271
  {children}
126
272
  </main>
127
273
  </div>
274
+
275
+ {!isDesktop && tabBar ? <div ref={tabBarRef}>{tabBar}</div> : null}
128
276
  </div>
129
277
  )
130
278
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The kit's topbar icon button (`.icbtn`, SHELL.md §3): 34×34, radius 8,
3
+ * `--sub`, hover `--acc-l` / `--acc`; 44×44 below 769px (cms-layouts.css) and
4
+ * at least 44px on touch screens (`.prenta-touch-target`). `relative` so the
5
+ * bell's unread dot can sit in its corner.
6
+ */
7
+ export const TOPBAR_ICON_BUTTON =
8
+ 'rounded-8 text-sub hover:bg-acc-l hover:text-acc prenta-touch-target relative flex h-[34px] w-[34px] shrink-0 cursor-pointer items-center justify-center border-0 bg-transparent p-0 mobile:h-11 mobile:w-11'