@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,135 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { dirname, resolve } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { describe, expect, it } from 'vitest'
5
+ import { ACCENT_PRESET_LIST, contrastRatio, tuneAccent } from '@prenta/core/appearance'
6
+
7
+ const tokens = JSON.parse(
8
+ readFileSync(
9
+ resolve(dirname(fileURLToPath(import.meta.url)), '../../../design-system/tokens.json'),
10
+ 'utf8',
11
+ ),
12
+ )
13
+
14
+ /** `oklch(from var(--acc) calc(l ± d) c calc(h - r))` → the lightness and hue shifts. */
15
+ function ai2Shift(value: string): { dl: number; dh: number } {
16
+ const m = /calc\(l ([+-]) ([\d.]+)\) c calc\(h - ([\d.]+)\)/.exec(value)
17
+ if (!m) throw new Error(`unexpected --ai2: ${value}`)
18
+ return { dl: (m[1] === '-' ? -1 : 1) * Number(m[2]), dh: -Number(m[3]) }
19
+ }
20
+
21
+ const toLinear = (c: number) => {
22
+ const v = c / 255
23
+ return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4
24
+ }
25
+ const toGamma = (v: number) => {
26
+ const c = Math.min(1, Math.max(0, v))
27
+ return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055
28
+ }
29
+
30
+ /** `#rrggbb` → OKLCH (Björn Ottosson's OKLab). */
31
+ function hexToOklch(hex: string) {
32
+ const n = parseInt(hex.slice(1), 16)
33
+ const r = toLinear((n >> 16) & 255)
34
+ const g = toLinear((n >> 8) & 255)
35
+ const b = toLinear(n & 255)
36
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)
37
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)
38
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)
39
+ const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s
40
+ const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s
41
+ const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
42
+ return { L, C: Math.hypot(A, B), H: ((Math.atan2(B, A) * 180) / Math.PI + 360) % 360 }
43
+ }
44
+
45
+ /** OKLCH → `#rrggbb`, channels clipped to sRGB as the browser paints them. */
46
+ function oklchToHex({ L, C, H }: { L: number; C: number; H: number }) {
47
+ const a = C * Math.cos((H * Math.PI) / 180)
48
+ const b = C * Math.sin((H * Math.PI) / 180)
49
+ const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3
50
+ const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3
51
+ const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3
52
+ const rgb = [
53
+ 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
54
+ -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
55
+ -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,
56
+ ]
57
+ return `#${rgb
58
+ .map((v) =>
59
+ Math.round(toGamma(v) * 255)
60
+ .toString(16)
61
+ .padStart(2, '0'),
62
+ )
63
+ .join('')}`
64
+ }
65
+
66
+ /** The second gradient stop for an accent, as the token computes it. */
67
+ function ai2(accent: string, shift: { dl: number; dh: number }) {
68
+ const o = hexToOklch(accent)
69
+ return oklchToHex({
70
+ L: Math.min(1, Math.max(0, o.L + shift.dl)),
71
+ C: o.C,
72
+ H: (o.H + shift.dh + 360) % 360,
73
+ })
74
+ }
75
+
76
+ /** Hover is `filter: brightness(1.07)`, which scales the encoded channels. */
77
+ function hover(hex: string) {
78
+ return `#${[1, 3, 5]
79
+ .map((i) =>
80
+ Math.min(255, Math.round(parseInt(hex.slice(i, i + 2), 16) * 1.07))
81
+ .toString(16)
82
+ .padStart(2, '0'),
83
+ )
84
+ .join('')}`
85
+ }
86
+
87
+ const light = ai2Shift(tokens.color.light.ai.ai2.value)
88
+ const dark = ai2Shift(tokens.color.dark.ai.ai2.value)
89
+
90
+ /** Every 17th step per channel: 4,096 custom picks. */
91
+ function* grid() {
92
+ for (let r = 0; r < 256; r += 17)
93
+ for (let g = 0; g < 256; g += 17)
94
+ for (let b = 0; b < 256; b += 17)
95
+ yield `#${[r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('')}`
96
+ }
97
+
98
+ /**
99
+ * The AI button keeps its label on the gradient, so white (Porcelain, Linen)
100
+ * or near-black (Graphite) text must clear 4.5:1 at both ends, at rest and on
101
+ * hover. The kit's lighter second stop fails on light bases (Cobalt 2.85:1),
102
+ * and hover's brightness(1.07) costs custom blues about 0.4; CONFLICTS.md §2.
103
+ */
104
+ describe('AI gradient contrast', () => {
105
+ it('keeps white text at 4.5:1 at both stops for every preset on light bases', () => {
106
+ for (const preset of ACCENT_PRESET_LIST) {
107
+ const stops = [preset.light.accentLt, ai2(preset.light.accentLt, light)]
108
+ for (const stop of stops) {
109
+ expect(contrastRatio('#ffffff', stop), `${preset.name} ${stop}`).toBeGreaterThanOrEqual(4.5)
110
+ expect(
111
+ contrastRatio('#ffffff', hover(stop)),
112
+ `${preset.name} ${stop} hover`,
113
+ ).toBeGreaterThanOrEqual(4.5)
114
+ }
115
+ }
116
+ })
117
+
118
+ it('keeps white text at 4.5:1 for custom accents on light bases, on hover too', () => {
119
+ for (const pick of grid()) {
120
+ const tuned = tuneAccent(pick)!
121
+ for (const stop of [tuned.lt, ai2(tuned.lt, light)]) {
122
+ expect(contrastRatio('#ffffff', hover(stop)), `${pick} ${stop}`).toBeGreaterThanOrEqual(4.5)
123
+ }
124
+ }
125
+ })
126
+
127
+ it('keeps dark text at 4.5:1 on the second stop in Graphite', () => {
128
+ for (const preset of ACCENT_PRESET_LIST) {
129
+ expect(
130
+ contrastRatio('#0e0f11', ai2(preset.dark.accentDk, dark)),
131
+ preset.name,
132
+ ).toBeGreaterThanOrEqual(4.5)
133
+ }
134
+ })
135
+ })
@@ -0,0 +1,68 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { createRequire } from 'node:module'
3
+ import { dirname } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { compile } from 'tailwindcss'
6
+ import { describe, expect, it } from 'vitest'
7
+
8
+ /**
9
+ * Any `--breakpoint-*` theme.css adds must sort in width order against
10
+ * Tailwind's own. Tailwind 4.2 compares breakpoints with parseInt() on the
11
+ * value, so px sorts before every rem tier, and a fractional rem tier ties with
12
+ * the stock one that shares its integer part (64.0625rem vs lg's 64rem) and
13
+ * falls back to name order. Either way `laptop:p-4` can lose to `lg:p-2`
14
+ * without any error. This compiles the real declarations and checks the order.
15
+ */
16
+
17
+ const require = createRequire(import.meta.url)
18
+ const twTheme = require.resolve('tailwindcss/theme.css')
19
+ const theme = readFileSync(
20
+ fileURLToPath(new URL('../../styles/theme.css', import.meta.url)),
21
+ 'utf8',
22
+ )
23
+
24
+ const declared = [...theme.matchAll(/^\s*--breakpoint-([\w-]+):\s*([^;]+);/gm)].map(
25
+ ([, name, value]) => ({ name: name!, value: value!.trim() }),
26
+ )
27
+
28
+ const STOCK = ['sm', 'md', 'lg', 'xl', '2xl']
29
+
30
+ /** Emitted `@media (width >= …)` thresholds, in output order, as rem. */
31
+ async function emittedOrder(): Promise<number[]> {
32
+ const compiler = await compile(
33
+ [
34
+ "@import 'tailwindcss/theme.css';",
35
+ `@theme inline {\n${declared.map((b) => `--breakpoint-${b.name}: ${b.value};`).join('\n')}\n}`,
36
+ '@tailwind utilities;',
37
+ ].join('\n'),
38
+ {
39
+ base: dirname(twTheme),
40
+ loadStylesheet: async () => ({
41
+ path: twTheme,
42
+ base: dirname(twTheme),
43
+ content: readFileSync(twTheme, 'utf8'),
44
+ }),
45
+ },
46
+ )
47
+ const names = [...new Set([...STOCK, ...declared.map((b) => b.name)])]
48
+ const css = compiler.build(names.map((n, i) => `${n}:p-${i + 1}`))
49
+ return [...css.matchAll(/@media \(width >= ([\d.]+)(rem|px)\)/g)].map(([, n, unit]) =>
50
+ unit === 'px' ? Number(n) / 16 : Number(n),
51
+ )
52
+ }
53
+
54
+ describe('breakpoint variants', () => {
55
+ it('emit narrowest first, stock and custom together', async () => {
56
+ const widths = await emittedOrder()
57
+ expect(widths.length).toBe(new Set([...STOCK, ...declared.map((b) => b.name)]).size)
58
+ expect(widths).toEqual([...widths].sort((a, b) => a - b))
59
+ })
60
+
61
+ it('never share an integer rem part with a stock breakpoint (Tailwind ties those)', () => {
62
+ const stockInts = [40, 48, 64, 80, 96]
63
+ for (const { name, value } of declared.filter((b) => !STOCK.includes(b.name))) {
64
+ const rem = value.endsWith('px') ? parseFloat(value) / 16 : parseFloat(value)
65
+ expect(stockInts, `--breakpoint-${name}: ${value}`).not.toContain(Math.trunc(rem))
66
+ }
67
+ })
68
+ })
@@ -44,18 +44,47 @@ describe('responsive + accessibility layer', () => {
44
44
  it('scopes every rule to the admin root', () => {
45
45
  // The admin renders inside consumer applications. A bare `:focus-visible`
46
46
  // or `@media print` rule here would restyle the host's own pages.
47
- const selectors = responsive
48
- .replace(/\/\*[\s\S]*?\*\//g, '')
49
- .split('\n')
50
- .map((line) => line.trim())
51
- .filter((line) => line.endsWith('{') && !line.startsWith('@'))
47
+ // Selectors can span lines (Prettier breaks long ones), so each one is
48
+ // the whole prelude between the previous brace and its own `{`; every
49
+ // comma-separated part of it must be scoped.
50
+ const preludes = [...responsive.replace(/\/\*[\s\S]*?\*\//g, '').matchAll(/([^{};]+)\{/g)]
51
+ .map(([, prelude]) => prelude!.replace(/\s+/g, ' ').trim())
52
+ .filter((prelude) => !prelude.startsWith('@'))
53
+ // Split on top-level commas only, not those inside :is() / :not().
54
+ const topLevel = (prelude: string) => {
55
+ const parts = ['']
56
+ let depth = 0
57
+ for (const char of prelude) {
58
+ if (char === '(') depth++
59
+ if (char === ')') depth--
60
+ if (char === ',' && depth === 0) parts.push('')
61
+ else parts[parts.length - 1] += char
62
+ }
63
+ return parts.map((part) => part.trim())
64
+ }
65
+ const selectors = preludes.flatMap(topLevel)
52
66
 
53
67
  expect(selectors.length).toBeGreaterThan(10)
54
68
  for (const selector of selectors) {
55
- expect(selector, `unscoped selector: ${selector}`).toMatch(/\.prenta-admin/)
69
+ expect(selector, `unscoped selector: ${selector}`).toMatch(/\.prenta-admin\b/)
56
70
  }
57
71
  })
58
72
 
73
+ it("keeps each control's own shape on focus and rings inverted surfaces in --bg", () => {
74
+ const rule = (selector: string) => {
75
+ const start = responsive.indexOf(`${selector} {`)
76
+ expect(start, selector).toBeGreaterThan(-1)
77
+ return responsive.slice(start, responsive.indexOf('}', start))
78
+ }
79
+ // A border-radius here beat the rounded-* utilities (this file is unlayered),
80
+ // so switches, chips and buttons changed shape when focused.
81
+ expect(rule('.prenta-admin :focus-visible')).not.toContain('border-radius')
82
+ // An --acc ring on the dark unsaved bar and toasts fell to ~2:1 (WCAG 1.4.11).
83
+ expect(rule('.prenta-admin .prenta-inverse :focus-visible')).toContain(
84
+ 'outline-color: var(--bg)',
85
+ )
86
+ })
87
+
59
88
  it('raises contrast rather than lowering it, in both themes', () => {
60
89
  // The shipped --muted is already an AA correction over the handoff's own
61
90
  // value (see prenta-tokens.css). A "more contrast" preference that moved
@@ -89,18 +118,68 @@ describe('responsive + accessibility layer', () => {
89
118
  }
90
119
  })
91
120
 
92
- it('grows touch targets only under a coarse pointer', () => {
121
+ it("uses the kit's high-contrast values (cms-responsive.css)", () => {
122
+ const kit = readFileSync(
123
+ PKG + '../../docs/design/prenta/prototype/design-system/cms-responsive.css',
124
+ 'utf8',
125
+ )
126
+ const values = (css: string) =>
127
+ [
128
+ ...css
129
+ .slice(css.indexOf('@media (prefers-contrast: more)'))
130
+ .matchAll(/(--muted|--sub|--bdr):\s*(#[0-9a-f]{6})/gi),
131
+ ]
132
+ .slice(0, 6)
133
+ .map(([, name, hex]) => `${name}: ${hex!.toLowerCase()}`)
134
+ expect(values(responsive)).toHaveLength(6)
135
+ expect(values(responsive)).toEqual(values(kit))
136
+ })
137
+
138
+ it('reaches Linen: the Linen block weighs no more than the contrast rule', () => {
139
+ // The contrast rule is `.prenta-admin` (0,1,0) and comes later. A Linen
140
+ // selector with its conditions outside :where() would outweigh it, and
141
+ // Linen users would never get the raised values.
142
+ const linenSelector = /^(\.prenta-admin[^{\n]*data-light='linen'[^{\n]*)\{/m.exec(tokens)![1]!
143
+ expect(linenSelector.trim()).toMatch(/^\.prenta-admin:where\(.*\)$/)
144
+
145
+ const luminance = (hex: string) => {
146
+ const n = parseInt(hex.slice(1), 16)
147
+ return ((n >> 16) & 255) * 0.299 + ((n >> 8) & 255) * 0.587 + (n & 255) * 0.114
148
+ }
149
+ const linenBlock = tokens.slice(tokens.indexOf(linenSelector))
150
+ const contrastBlock = responsive.slice(responsive.indexOf('@media (prefers-contrast: more)'))
151
+ for (const name of ['--muted', '--sub', '--bdr']) {
152
+ const pattern = new RegExp(`${name}:\\s*(#[0-9a-f]{6})`, 'i')
153
+ const linen = pattern.exec(linenBlock)![1]!
154
+ const raised = pattern.exec(contrastBlock)![1]!
155
+ expect(luminance(raised), `Linen ${name}`).toBeLessThan(luminance(linen))
156
+ }
157
+ })
158
+
159
+ it('grows touch targets only under a coarse pointer or in the mobile tier', () => {
93
160
  const coarse = responsive.slice(
94
161
  responsive.indexOf('@media (pointer: coarse)'),
95
162
  responsive.indexOf('@media (min-width: 2000px)'),
96
163
  )
97
164
  expect(coarse).toContain('min-height: 44px')
98
165
  expect(coarse).toContain('min-width: 44px')
99
- // A 26px button is right with a mouse. No control floor is imposed outside
100
- // this block — the 44px that appears in the stacked-layout rules is the
101
- // collapsed strip's own height, not a minimum applied to a control.
102
- const elsewhere = responsive.replace(coarse, '').replace(/\/\*[\s\S]*?\*\//g, '')
103
- expect(elsewhere).not.toMatch(/min-(height|width):\s*44px/)
166
+ // A 26px button is right with a mouse on a laptop. Outside the coarse
167
+ // block, a 44px floor belongs only to the mobile tier (SHELL.md §8: "All
168
+ // actions become 44px icons"), never to tablet or wider. The 44px in the
169
+ // stacked-layout rules is the collapsed strip's own height, not a floor.
170
+ const blocks = [
171
+ ...responsive.replace(/\/\*[\s\S]*?\*\//g, '').matchAll(/@media ([^{]+)\{([\s\S]*?)\n\}/g),
172
+ ]
173
+ const floors = blocks.filter(([, , body]) => /min-(height|width):\s*44px/.test(body!))
174
+ expect(floors.length).toBeGreaterThan(1)
175
+ for (const [, query] of floors) {
176
+ expect(['(pointer: coarse)', '(width < 769px)']).toContain(query!.trim())
177
+ }
178
+ const outside = blocks.reduce(
179
+ (css, [block]) => css.replace(block!, ''),
180
+ responsive.replace(/\/\*[\s\S]*?\*\//g, ''),
181
+ )
182
+ expect(outside).not.toMatch(/min-(height|width):\s*44px/)
104
183
  })
105
184
 
106
185
  it('grows the switch by its hit area, never by its box', () => {
@@ -112,6 +191,57 @@ describe('responsive + accessibility layer', () => {
112
191
  expect(hit.slice(0, hit.indexOf('}'))).not.toContain('min-height')
113
192
  })
114
193
 
194
+ it('leaves position to the control, so a positioned chevron stays in its row', () => {
195
+ // The layer is unlayered: a `position: relative` here would beat the
196
+ // sidebar's layered `absolute` chevron and drop it out of the row on touch.
197
+ const coarse = responsive.slice(
198
+ responsive.indexOf('@media (pointer: coarse)'),
199
+ responsive.indexOf('@media (min-width: 2000px)'),
200
+ )
201
+ const rules = coarse
202
+ .replace(/\/\*[\s\S]*?\*\//g, '')
203
+ .replace(/\.prenta-touch-hit::after\s*\{[^}]*\}/, '')
204
+ expect(rules).not.toMatch(/\.prenta-touch-hit[^{]*\{[^}]*position/)
205
+ const after = /\.prenta-touch-hit::after\s*\{([^}]*)\}/.exec(coarse)![1]!
206
+ expect(after).toContain('inset-block: var(--touch-hit-block, -13px)')
207
+ expect(after).toContain('inset-inline: var(--touch-hit-inline, -6px)')
208
+
209
+ // So every control using the hook positions itself.
210
+ const files = [
211
+ 'src/components/ui/Toggle.tsx',
212
+ 'src/components/page-tab/DirectiveSwitch.tsx',
213
+ 'src/layout/Sidebar.tsx',
214
+ ]
215
+ for (const file of files) {
216
+ const uses = read(file)
217
+ .split('\n')
218
+ .filter((line) => line.includes('prenta-touch-hit') && !/^\s*(\/\/|\*|\{\/\*)/.test(line))
219
+ expect(uses.length, file).toBeGreaterThan(0)
220
+ for (const line of uses) expect(line, file).toMatch(/\b(relative|absolute)\b/)
221
+ }
222
+ })
223
+
224
+ it('keeps the sidebar chevrons’ touch areas inside their rows', () => {
225
+ // 24px chevrons: the parent row is 36px (h-9), a per-form row 30px. Each
226
+ // area is 44px wide and exactly its row tall.
227
+ const chevrons = read('src/layout/Sidebar.tsx')
228
+ .split('\n')
229
+ .filter((line) => line.includes('prenta-touch-hit') && line.includes('h-6 w-6'))
230
+ const classes = (line: string) => line.split(/[\s"`]+/)
231
+ const parent = chevrons.find((line) => classes(line).includes('relative'))!
232
+ const nested = chevrons.find((line) => classes(line).includes('absolute'))!
233
+ for (const [line, block] of [
234
+ [parent, '-6px'],
235
+ [nested, '-3px'],
236
+ ] as const) {
237
+ expect(classes(line)).toContain(`[--touch-hit-block:${block}]`)
238
+ expect(classes(line)).toContain('[--touch-hit-inline:-10px]')
239
+ }
240
+ expect(24 + 2 * 6).toBe(36)
241
+ expect(24 + 2 * 3).toBe(30)
242
+ expect(24 + 2 * 10).toBe(44)
243
+ })
244
+
115
245
  it('overrides the rail width with !important, because it is set inline', () => {
116
246
  // The rail is drag-resizable and its width is an inline style, which no
117
247
  // class can beat. Without this the stacked layout silently keeps a
@@ -119,6 +249,222 @@ describe('responsive + accessibility layer', () => {
119
249
  const stacked = responsive.slice(responsive.indexOf('@media (max-width: 900px)'))
120
250
  expect(stacked).toMatch(/\.prenta-editor-rail\s*\{[^}]*width:\s*100%\s*!important/)
121
251
  })
252
+
253
+ it('grows the topbar to 64px at the kit 4K tier (SHELL.md §8)', () => {
254
+ // The topbar and the sidebar logo row are both h-[var(--tbh)].
255
+ const block = /@media \(width >= 2200px\) \{([\s\S]*?)\n\}/.exec(responsive)?.[1] ?? ''
256
+ expect(block).toMatch(/\.prenta-admin \{\s*--tbh: 64px;/)
257
+ expect(read('src/layout/Header.tsx')).toContain('h-[var(--tbh)]')
258
+ expect(read('src/layout/Sidebar.tsx')).toContain('h-[var(--tbh)]')
259
+ })
260
+ })
261
+
262
+ /** P-05: the layout tiers (SHELL.md §8, the kit's cms-layouts.css). */
263
+ describe('layout tier rules', () => {
264
+ const css = responsive.replace(/\/\*[\s\S]*?\*\//g, '')
265
+ const BLOCK = /@media ([^{]+)\{([\s\S]*?)\n\}/g
266
+ const flat = (text: string) => text.replace(/\s+/g, ' ')
267
+ /** Every `@media <query>` block's body, joined, whitespace collapsed. */
268
+ const tier = (query: string) =>
269
+ flat(
270
+ [...css.matchAll(BLOCK)]
271
+ .filter(([, q]) => q!.trim() === query)
272
+ .map(([, , body]) => body!)
273
+ .join('\n'),
274
+ )
275
+ /** The stylesheet without its `@media <query>` blocks. */
276
+ const without = (query: string) =>
277
+ flat(css.replace(BLOCK, (block, q: string) => (q.trim() === query ? '' : block)))
278
+ const MOBILE = '(width < 769px)'
279
+ const TABLET = '(769px <= width < 1025px)'
280
+ /** The declarations of the first rule whose selector contains `needle`. */
281
+ const ruleIn = (block: string, needle: string) => {
282
+ const at = block.indexOf(needle)
283
+ expect(at, needle).toBeGreaterThan(-1)
284
+ return block.slice(block.indexOf('{', at), block.indexOf('}', at))
285
+ }
286
+
287
+ it('sets the gutter and drawer width per tier, laptop being the token baseline', () => {
288
+ expect(tokens).toMatch(/--gut: 28px;/)
289
+ expect(tokens).toMatch(/--drawer-w: 440px;/)
290
+ const gut = (query: string) => /--gut: (\d+)px;/.exec(tier(query))?.[1]
291
+ expect(gut('(width >= 2200px)')).toBe('44')
292
+ expect(gut('(1441px <= width < 2200px)')).toBe('36')
293
+ expect(gut(TABLET)).toBe('20')
294
+ expect(gut(MOBILE)).toBe('16')
295
+ expect(tier('(width >= 2200px)')).toContain('--drawer-w: 600px')
296
+ expect(tier('(1441px <= width < 2200px)')).toContain('--drawer-w: 500px')
297
+ expect(tier(TABLET)).toContain('--drawer-w: min(440px, 72vw)')
298
+ })
299
+
300
+ it('collapses secondary header actions to 40px icons at tablet, never AI or primary', () => {
301
+ const block = tier(TABLET).replace(/\s+/g, ' ')
302
+ const selector =
303
+ /\.prenta-admin \[data-header-actions\] (:is\([^{]*\)):has\(svg\):not\( ?\[data-keep-label\] ?\) \{/.exec(
304
+ block,
305
+ )
306
+ expect(selector, 'tablet collapse rule').not.toBeNull()
307
+ const variants = selector![1]!
308
+ for (const kept of ['secondary', 'outline', 'ghost']) expect(variants).toContain(`'${kept}'`)
309
+ for (const labelled of ['primary', 'ai']) expect(variants).not.toContain(`'${labelled}'`)
310
+ const box = ruleIn(block, selector![0]!)
311
+ expect(box).toContain('width: 40px')
312
+ expect(box).toContain('height: 40px')
313
+ expect(box).toContain('font-size: 0')
314
+ })
315
+
316
+ it('collapses every mobile header action to a 44px icon except the FAB', () => {
317
+ const block = tier(MOBILE).replace(/\s+/g, ' ')
318
+ const selector =
319
+ /\.prenta-admin \[data-header-actions\] :is\(([^)]*)\):has\(svg\):not\(\[data-fab\]\) \{/.exec(
320
+ block,
321
+ )
322
+ expect(selector, 'mobile collapse rule').not.toBeNull()
323
+ for (const variant of ['primary', 'secondary', 'ghost', 'ai']) {
324
+ expect(selector![1]).toContain(`'${variant}'`)
325
+ }
326
+ const box = ruleIn(block, selector![0]!)
327
+ expect(box).toContain('width: 44px')
328
+ expect(box).toContain('height: 44px')
329
+ })
330
+
331
+ it('keeps a collapsed label as the accessible name (clipped, never display: none)', () => {
332
+ // display: none would drop the label from the accessibility tree and
333
+ // leave an unnamed icon button.
334
+ for (const block of [tier(TABLET), tier(MOBILE)]) {
335
+ const labels = [
336
+ ...block.replace(/\s+/g, ' ').matchAll(/> :not\(svg\):not\(:has\(svg\)\) \{([^}]*)\}/g),
337
+ ]
338
+ expect(labels.length).toBeGreaterThan(0)
339
+ for (const [, declarations] of labels) {
340
+ expect(declarations).toContain('clip-path: inset(50%)')
341
+ expect(declarations).toContain('position: absolute')
342
+ expect(declarations).not.toContain('display: none')
343
+ }
344
+ }
345
+ })
346
+
347
+ it('floats the FAB above the tab bar, under every overlay, and clears room for it', () => {
348
+ const block = tier(MOBILE)
349
+ const fab = ruleIn(block, '.prenta-admin [data-fab] {')
350
+ expect(fab).toContain('position: fixed')
351
+ expect(fab).toContain('bottom: calc(var(--tabbar-h) + 16px + env(safe-area-inset-bottom, 0px))')
352
+ expect(fab).toContain('z-index: var(--z-tabbar)')
353
+ expect(fab).toContain('width: 56px')
354
+ expect(fab).toContain('height: 56px')
355
+ expect(fab).toContain('border-radius: 18px')
356
+ // On the admin root, so both the content column and the toasts read it,
357
+ // and not while an open side pane hides the FAB.
358
+ expect(
359
+ ruleIn(
360
+ block,
361
+ '.prenta-admin:has(#prenta-main [data-fab]):not(:has([data-inspector-open])) {',
362
+ ),
363
+ ).toContain('--fab-clear: 88px')
364
+ expect(read('src/layout/primitives/AdminShell.tsx')).toContain('var(--fab-clear, 0px)')
365
+ // Floating only on mobile; elsewhere just a forced-colors edge.
366
+ const FORCED = '(forced-colors: active)'
367
+ expect(ruleIn(tier(FORCED), '.prenta-admin [data-fab] {')).toContain(
368
+ 'border: 1px solid ButtonText',
369
+ )
370
+ const others = flat(
371
+ css.replace(BLOCK, (rules, q: string) => ([MOBILE, FORCED].includes(q.trim()) ? '' : rules)),
372
+ )
373
+ expect(others).not.toContain('[data-fab] {')
374
+ })
375
+
376
+ it('lifts phone toasts above the FAB (review: a 358px error toast covered it)', () => {
377
+ // sonner's mobile layout (≤600px) makes a toast as wide as the screen; its
378
+ // offset adds the FAB's room. Wider, a centred toast misses the FAB.
379
+ const toaster = read('src/components/ui/Toaster.tsx')
380
+ const mobileOffset = toaster.slice(toaster.indexOf('mobileOffset='))
381
+ expect(mobileOffset.slice(0, mobileOffset.indexOf('duration='))).toContain(
382
+ 'var(--fab-clear, 0px)',
383
+ )
384
+ const offset = toaster.slice(toaster.indexOf(' offset='), toaster.indexOf('mobileOffset='))
385
+ expect(offset).not.toContain('--fab-clear')
386
+ })
387
+
388
+ it('draws list tables as cards on mobile only, header kept for screen readers', () => {
389
+ const block = tier(MOBILE)
390
+ expect(ruleIn(block, 'table[data-cards] {')).toContain('display: block')
391
+ const head = ruleIn(block, 'table[data-cards] thead {')
392
+ expect(head).toContain('clip-path: inset(50%)')
393
+ expect(head).not.toContain('display: none')
394
+ // Review: its sort buttons and select-all stayed in the tab order,
395
+ // invisibly. CardListControls brings them back for phones.
396
+ expect(
397
+ ruleIn(
398
+ block,
399
+ 'table[data-cards] thead :is(a, button, input, select, textarea, [tabindex]) {',
400
+ ),
401
+ ).toContain('visibility: hidden')
402
+ expect(ruleIn(block, 'table[data-cards] tbody tr {')).toContain('display: grid')
403
+ expect(ruleIn(block, 'td[data-label]:not([data-role])::before')).toContain(
404
+ 'content: attr(data-label)',
405
+ )
406
+ expect(ruleIn(block, "td[data-role='title'] {")).toContain('grid-row: 1')
407
+ expect(ruleIn(block, "td[data-role='check'] {")).toContain('grid-column: 2')
408
+ // Review: a 13px checkbox. The kit's 24px box inside a 44px hit area.
409
+ const hit = ruleIn(block, "td[data-role='check'] label {")
410
+ expect(hit).toContain('min-width: 44px')
411
+ expect(hit).toContain('min-height: 44px')
412
+ expect(ruleIn(block, "td[data-role='check'] input {")).toContain('min-width: 24px')
413
+ // Review: long values were cut off with no ellipsis. They wrap.
414
+ const value = ruleIn(block, 'td[data-label]:not([data-role]) * {')
415
+ expect(value).toContain('white-space: normal')
416
+ expect(value).toContain('overflow-wrap: anywhere')
417
+ expect(ruleIn(block, 'td[data-label]:not([data-role]) > * {')).toContain('min-width: 0')
418
+ expect(ruleIn(block, "td[data-role='media'] {")).toContain('display: none')
419
+ const actions = ruleIn(block, "td[data-role='actions'] :is(button, a)")
420
+ expect(actions).toContain('min-width: 44px')
421
+ expect(actions).toContain('min-height: 44px')
422
+ // Analytic tables scroll in their own box instead.
423
+ expect(ruleIn(block, ':has(> table:not([data-cards]))')).toContain('overflow-x: auto')
424
+ // Review: not tables drawn for a narrow pane or dialog.
425
+ expect(
426
+ ruleIn(
427
+ block,
428
+ "table:not([data-cards]):not(:where([data-inspector-open], [role='dialog']) table) {",
429
+ ),
430
+ ).toContain('min-width: 640px')
431
+ expect(without(MOBILE)).not.toContain('table[data-cards]')
432
+ })
433
+
434
+ it('turns an open side pane into a sheet over the content column on mobile', () => {
435
+ // Everything off the pane's path hides (the list, the page header and
436
+ // filters), rather than sitting behind it where focus could still reach.
437
+ const rule = ruleIn(
438
+ tier(MOBILE),
439
+ '#prenta-main :has([data-inspector-open]) > :not([data-inspector-open], :has([data-inspector-open]))',
440
+ )
441
+ expect(rule).toContain('display: none')
442
+ // Review: inline widths and 100vh-minus layouts left it short of the
443
+ // column. Fixed between the topbar and the tab bar instead.
444
+ const sheet = ruleIn(tier(MOBILE), '.prenta-admin #prenta-main [data-inspector-open] {')
445
+ expect(sheet).toContain('position: fixed')
446
+ expect(sheet).toContain('top: var(--tbh)')
447
+ expect(sheet).toContain('bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px))')
448
+ expect(sheet).toContain('left: 0')
449
+ expect(sheet).toContain('right: 0')
450
+ expect(sheet).toContain('width: auto !important')
451
+ })
452
+
453
+ it('anchors a table’s absolute content (sr-only headers) to its own scroll box', () => {
454
+ // Without it the "Actions" header's static position widened #prenta-main
455
+ // by 199px on Posts at 924px. Layered, so a box's own `sticky` /
456
+ // `absolute` utility still wins over this default.
457
+ const layer = /@layer components \{([\s\S]*?)\n\}/.exec(css)?.[1] ?? ''
458
+ expect(flat(layer)).toContain(
459
+ '.prenta-admin #prenta-main :has(> table) { position: relative; }',
460
+ )
461
+ })
462
+
463
+ it('keeps the content scroller from scrolling sideways', () => {
464
+ expect(read('src/layout/primitives/AdminShell.tsx')).toMatch(
465
+ /id="prenta-main"\s+className="[^"]*\boverflow-x-hidden\b/,
466
+ )
467
+ })
122
468
  })
123
469
 
124
470
  describe('class hooks the layer targets', () => {
@@ -135,6 +481,15 @@ describe('class hooks the layer targets', () => {
135
481
  ['prenta-canvas-fit', 'src/views/page-editor/CanvasSurface.tsx'],
136
482
  ['prenta-touch-target', 'src/components/EditorRail.tsx'],
137
483
  ['prenta-touch-hit', 'src/components/page-tab/DirectiveSwitch.tsx'],
484
+ ['data-header-actions', 'src/components/ui/PageHeader.tsx'],
485
+ ['data-variant', 'src/components/ui/Button.tsx'],
486
+ ['data-keep-label', 'src/components/ui/Button.tsx'],
487
+ ['data-fab', 'src/views/Pages/PagesListView.tsx'],
488
+ ['data-cards', 'src/components/ui/card-table.ts'],
489
+ ['data-role', 'src/components/ui/card-table.ts'],
490
+ ['data-label', 'src/components/ui/card-table.ts'],
491
+ ['data-inspector-open', 'src/components/ui/InspectorPane.tsx'],
492
+ ['data-inspector-open', 'src/views/MediaBrowser.tsx'],
138
493
  ]
139
494
 
140
495
  for (const [hook, file] of hooks) {