web-mojo 2.2.101 → 2.3.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 (203) hide show
  1. package/CHANGELOG.md +603 -0
  2. package/README.md +2 -2
  3. package/dist/admin-models.cjs.js +2 -0
  4. package/dist/admin-models.cjs.js.map +1 -0
  5. package/dist/admin-models.es.js +2 -0
  6. package/dist/admin-models.es.js.map +1 -0
  7. package/dist/admin.cjs.js +1 -1
  8. package/dist/admin.css +42 -0
  9. package/dist/admin.es.js +1 -1
  10. package/dist/auth.cjs.js +1 -1
  11. package/dist/auth.cjs.js.map +1 -1
  12. package/dist/auth.es.js +1 -1
  13. package/dist/auth.es.js.map +1 -1
  14. package/dist/charts.cjs.js +1 -1
  15. package/dist/charts.cjs.js.map +1 -1
  16. package/dist/charts.css +897 -1
  17. package/dist/charts.es.js +1 -1
  18. package/dist/charts.es.js.map +1 -1
  19. package/dist/chat.css +96 -0
  20. package/dist/chunks/AssistantPanelView-BG34Qbfj.js +2 -0
  21. package/dist/chunks/AssistantPanelView-BG34Qbfj.js.map +1 -0
  22. package/dist/chunks/AssistantPanelView-DCEV6VeI.js +2 -0
  23. package/dist/chunks/AssistantPanelView-DCEV6VeI.js.map +1 -0
  24. package/dist/chunks/ChatView-B73uox2v.js +2 -0
  25. package/dist/chunks/ChatView-B73uox2v.js.map +1 -0
  26. package/dist/chunks/ChatView-W8daOwIo.js +2 -0
  27. package/dist/chunks/ChatView-W8daOwIo.js.map +1 -0
  28. package/dist/chunks/Collection-BZlmtcuL.js +2 -0
  29. package/dist/chunks/Collection-BZlmtcuL.js.map +1 -0
  30. package/dist/chunks/Collection-Bwoq6muu.js +2 -0
  31. package/dist/chunks/Collection-Bwoq6muu.js.map +1 -0
  32. package/dist/chunks/ContextMenu-BPPtuqKk.js +2 -0
  33. package/dist/chunks/ContextMenu-BPPtuqKk.js.map +1 -0
  34. package/dist/chunks/ContextMenu-q76hjQb6.js +2 -0
  35. package/dist/chunks/ContextMenu-q76hjQb6.js.map +1 -0
  36. package/dist/chunks/DataView-BbrwHMV4.js +2 -0
  37. package/dist/chunks/{DataView-BFx2glFg.js.map → DataView-BbrwHMV4.js.map} +1 -1
  38. package/dist/chunks/DataView-k-7wmk5_.js +2 -0
  39. package/dist/chunks/{DataView-D5C_lDdg.js.map → DataView-k-7wmk5_.js.map} +1 -1
  40. package/dist/chunks/FormView-DPSuwWMq.js +3 -0
  41. package/dist/chunks/{FormView-e-PeRx1s.js.map → FormView-DPSuwWMq.js.map} +1 -1
  42. package/dist/chunks/FormView-Dcy7XOtC.js +3 -0
  43. package/dist/chunks/{FormView-Cu4iPfvU.js.map → FormView-Dcy7XOtC.js.map} +1 -1
  44. package/dist/chunks/ListView-DHC-yBIw.js +2 -0
  45. package/dist/chunks/ListView-DHC-yBIw.js.map +1 -0
  46. package/dist/chunks/ListView-iGBsD4a7.js +2 -0
  47. package/dist/chunks/ListView-iGBsD4a7.js.map +1 -0
  48. package/dist/chunks/MetricsCountryMapView-CAD9wR_T.js +2 -0
  49. package/dist/chunks/MetricsCountryMapView-CAD9wR_T.js.map +1 -0
  50. package/dist/chunks/MetricsCountryMapView-Dzk3Yrzx.js +2 -0
  51. package/dist/chunks/MetricsCountryMapView-Dzk3Yrzx.js.map +1 -0
  52. package/dist/chunks/Modal-DBJU16cc.js +3 -0
  53. package/dist/chunks/Modal-DBJU16cc.js.map +1 -0
  54. package/dist/chunks/Modal-DuULCMFZ.js +3 -0
  55. package/dist/chunks/Modal-DuULCMFZ.js.map +1 -0
  56. package/dist/chunks/Passkeys-CGRZ8ZMv.js +2 -0
  57. package/dist/chunks/{Passkeys-DfVHrRPY.js.map → Passkeys-CGRZ8ZMv.js.map} +1 -1
  58. package/dist/chunks/Passkeys-Dr8-oSm9.js +2 -0
  59. package/dist/chunks/{Passkeys-Bj-ufmei.js.map → Passkeys-Dr8-oSm9.js.map} +1 -1
  60. package/dist/chunks/TokenManager-CcQFvaFD.js +2 -0
  61. package/dist/chunks/TokenManager-CcQFvaFD.js.map +1 -0
  62. package/dist/chunks/TokenManager-DEWZqbuo.js +2 -0
  63. package/dist/chunks/TokenManager-DEWZqbuo.js.map +1 -0
  64. package/dist/chunks/User-DNQhdBtI.js +2 -0
  65. package/dist/chunks/User-DNQhdBtI.js.map +1 -0
  66. package/dist/chunks/User-Dg7xpYEI.js +2 -0
  67. package/dist/chunks/User-Dg7xpYEI.js.map +1 -0
  68. package/dist/chunks/UserProfileView-B5nczdfw.js +2 -0
  69. package/dist/chunks/UserProfileView-B5nczdfw.js.map +1 -0
  70. package/dist/chunks/UserProfileView-Bpz3VZmP.js +2 -0
  71. package/dist/chunks/UserProfileView-Bpz3VZmP.js.map +1 -0
  72. package/dist/chunks/View-C5n3sIFi.js +2 -0
  73. package/dist/chunks/View-C5n3sIFi.js.map +1 -0
  74. package/dist/chunks/View-Yazho7OL.js +2 -0
  75. package/dist/chunks/View-Yazho7OL.js.map +1 -0
  76. package/dist/chunks/WebApp-CeiDNV6L.js +2 -0
  77. package/dist/chunks/WebApp-CeiDNV6L.js.map +1 -0
  78. package/dist/chunks/WebApp-irKlhuFX.js +2 -0
  79. package/dist/chunks/WebApp-irKlhuFX.js.map +1 -0
  80. package/dist/chunks/admin-B5tf0zOO.js +2 -0
  81. package/dist/chunks/admin-B5tf0zOO.js.map +1 -0
  82. package/dist/chunks/admin-CGoTpXfs.js +2 -0
  83. package/dist/chunks/admin-CGoTpXfs.js.map +1 -0
  84. package/dist/chunks/exportChart-BTJBYkOz.js +2 -0
  85. package/dist/chunks/exportChart-BTJBYkOz.js.map +1 -0
  86. package/dist/chunks/exportChart-kQ-we4Cp.js +2 -0
  87. package/dist/chunks/exportChart-kQ-we4Cp.js.map +1 -0
  88. package/dist/chunks/index-BNjCQA7q.js +2 -0
  89. package/dist/chunks/{index-BY54viKF.js.map → index-BNjCQA7q.js.map} +1 -1
  90. package/dist/chunks/index-DBsIDOAa.js +2 -0
  91. package/dist/chunks/{index-Df5lx5TH.js.map → index-DBsIDOAa.js.map} +1 -1
  92. package/dist/chunks/version-BURwX10Q.js +2 -0
  93. package/dist/chunks/version-BURwX10Q.js.map +1 -0
  94. package/dist/chunks/version-CYGIXntv.js +2 -0
  95. package/dist/chunks/version-CYGIXntv.js.map +1 -0
  96. package/dist/core.css +306 -0
  97. package/dist/css/web-mojo.css +1 -1
  98. package/dist/docit.cjs.js +1 -1
  99. package/dist/docit.cjs.js.map +1 -1
  100. package/dist/docit.es.js +1 -1
  101. package/dist/docit.es.js.map +1 -1
  102. package/dist/index.cjs.js +1 -1
  103. package/dist/index.cjs.js.map +1 -1
  104. package/dist/index.es.js +1 -1
  105. package/dist/index.es.js.map +1 -1
  106. package/dist/lightbox.cjs.js +1 -1
  107. package/dist/lightbox.cjs.js.map +1 -1
  108. package/dist/lightbox.es.js +1 -1
  109. package/dist/lightbox.es.js.map +1 -1
  110. package/dist/map.cjs.js +1 -1
  111. package/dist/map.cjs.js.map +1 -1
  112. package/dist/map.es.js +1 -1
  113. package/dist/map.es.js.map +1 -1
  114. package/dist/mojo-auth.es.js +94 -65
  115. package/dist/mojo-auth.umd.js +1 -1
  116. package/dist/portal.css +86 -0
  117. package/dist/timeline.cjs.js +1 -1
  118. package/dist/timeline.cjs.js.map +1 -1
  119. package/dist/timeline.es.js +1 -1
  120. package/dist/timeline.es.js.map +1 -1
  121. package/dist/user-profile.cjs.js +1 -1
  122. package/dist/user-profile.es.js +1 -1
  123. package/dist/web-mojo.lite.iife.js +4183 -3916
  124. package/dist/web-mojo.lite.iife.js.map +1 -1
  125. package/dist/web-mojo.lite.iife.min.js +289 -311
  126. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  127. package/package.json +7 -2
  128. package/dist/chunks/AssistantPanelView-Bdpmd4z7.js +0 -2
  129. package/dist/chunks/AssistantPanelView-Bdpmd4z7.js.map +0 -1
  130. package/dist/chunks/AssistantPanelView-C0bdbEWr.js +0 -2
  131. package/dist/chunks/AssistantPanelView-C0bdbEWr.js.map +0 -1
  132. package/dist/chunks/ChatView-B1MZNPGy.js +0 -2
  133. package/dist/chunks/ChatView-B1MZNPGy.js.map +0 -1
  134. package/dist/chunks/ChatView-DXl9nVVV.js +0 -2
  135. package/dist/chunks/ChatView-DXl9nVVV.js.map +0 -1
  136. package/dist/chunks/Collection-C39Oy2q0.js +0 -2
  137. package/dist/chunks/Collection-C39Oy2q0.js.map +0 -1
  138. package/dist/chunks/Collection-CyK0u557.js +0 -2
  139. package/dist/chunks/Collection-CyK0u557.js.map +0 -1
  140. package/dist/chunks/ContextMenu-D-C7V6J6.js +0 -2
  141. package/dist/chunks/ContextMenu-D-C7V6J6.js.map +0 -1
  142. package/dist/chunks/ContextMenu-hjqR1pGW.js +0 -2
  143. package/dist/chunks/ContextMenu-hjqR1pGW.js.map +0 -1
  144. package/dist/chunks/DataView-BFx2glFg.js +0 -2
  145. package/dist/chunks/DataView-D5C_lDdg.js +0 -2
  146. package/dist/chunks/Dialog-1umNJi4B.js +0 -3
  147. package/dist/chunks/Dialog-1umNJi4B.js.map +0 -1
  148. package/dist/chunks/Dialog-BnxWLMfr.js +0 -3
  149. package/dist/chunks/Dialog-BnxWLMfr.js.map +0 -1
  150. package/dist/chunks/FormView-Cu4iPfvU.js +0 -3
  151. package/dist/chunks/FormView-e-PeRx1s.js +0 -3
  152. package/dist/chunks/ListView-D_hOtfWZ.js +0 -2
  153. package/dist/chunks/ListView-D_hOtfWZ.js.map +0 -1
  154. package/dist/chunks/ListView-Jkke6pU1.js +0 -2
  155. package/dist/chunks/ListView-Jkke6pU1.js.map +0 -1
  156. package/dist/chunks/MetricsCountryMapView-UdvJWArx.js +0 -2
  157. package/dist/chunks/MetricsCountryMapView-UdvJWArx.js.map +0 -1
  158. package/dist/chunks/MetricsCountryMapView-jLWCL6Hm.js +0 -2
  159. package/dist/chunks/MetricsCountryMapView-jLWCL6Hm.js.map +0 -1
  160. package/dist/chunks/MetricsMiniChartWidget-BaXxR9C6.js +0 -2
  161. package/dist/chunks/MetricsMiniChartWidget-BaXxR9C6.js.map +0 -1
  162. package/dist/chunks/MetricsMiniChartWidget-DSKmKY2Z.js +0 -2
  163. package/dist/chunks/MetricsMiniChartWidget-DSKmKY2Z.js.map +0 -1
  164. package/dist/chunks/MiniPieChart-B3sM4Bjv.js +0 -2
  165. package/dist/chunks/MiniPieChart-B3sM4Bjv.js.map +0 -1
  166. package/dist/chunks/MiniPieChart-DL5Rynvm.js +0 -2
  167. package/dist/chunks/MiniPieChart-DL5Rynvm.js.map +0 -1
  168. package/dist/chunks/MiniSeriesChart-C4DPVbU_.js +0 -2
  169. package/dist/chunks/MiniSeriesChart-C4DPVbU_.js.map +0 -1
  170. package/dist/chunks/MiniSeriesChart-DdNMLwfh.js +0 -2
  171. package/dist/chunks/MiniSeriesChart-DdNMLwfh.js.map +0 -1
  172. package/dist/chunks/Modal-COT4zRh3.js +0 -2
  173. package/dist/chunks/Modal-COT4zRh3.js.map +0 -1
  174. package/dist/chunks/Modal-DgMMvDnw.js +0 -2
  175. package/dist/chunks/Modal-DgMMvDnw.js.map +0 -1
  176. package/dist/chunks/Passkeys-Bj-ufmei.js +0 -2
  177. package/dist/chunks/Passkeys-DfVHrRPY.js +0 -2
  178. package/dist/chunks/TokenManager-DIEaBGJh.js +0 -2
  179. package/dist/chunks/TokenManager-DIEaBGJh.js.map +0 -1
  180. package/dist/chunks/TokenManager-DeXkJy0u.js +0 -2
  181. package/dist/chunks/TokenManager-DeXkJy0u.js.map +0 -1
  182. package/dist/chunks/UserProfileView-B_jyMUp0.js +0 -2
  183. package/dist/chunks/UserProfileView-B_jyMUp0.js.map +0 -1
  184. package/dist/chunks/UserProfileView-DcpvvGQS.js +0 -2
  185. package/dist/chunks/UserProfileView-DcpvvGQS.js.map +0 -1
  186. package/dist/chunks/WebApp-BvFnKj-I.js +0 -2
  187. package/dist/chunks/WebApp-BvFnKj-I.js.map +0 -1
  188. package/dist/chunks/WebApp-CfDWKmwH.js +0 -2
  189. package/dist/chunks/WebApp-CfDWKmwH.js.map +0 -1
  190. package/dist/chunks/WebSocketClient-BQAZr8C4.js +0 -2
  191. package/dist/chunks/WebSocketClient-BQAZr8C4.js.map +0 -1
  192. package/dist/chunks/WebSocketClient-YR5d82h8.js +0 -2
  193. package/dist/chunks/WebSocketClient-YR5d82h8.js.map +0 -1
  194. package/dist/chunks/admin-BgvcCWQp.js +0 -2
  195. package/dist/chunks/admin-BgvcCWQp.js.map +0 -1
  196. package/dist/chunks/admin-DzhyZ_Tn.js +0 -2
  197. package/dist/chunks/admin-DzhyZ_Tn.js.map +0 -1
  198. package/dist/chunks/index-BY54viKF.js +0 -2
  199. package/dist/chunks/index-Df5lx5TH.js +0 -2
  200. package/dist/chunks/version-8mBBYRDe.js +0 -2
  201. package/dist/chunks/version-8mBBYRDe.js.map +0 -1
  202. package/dist/chunks/version-CoLHxZIU.js +0 -2
  203. package/dist/chunks/version-CoLHxZIU.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -2,7 +2,610 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Feature — Cross-origin auth handoff
6
+
7
+ - `TokenManager.handleAuthCodeFromURL(app)` and `TokenManager.exchangeAuthCode(app, code)` redeem a `?auth_code=<32-hex>` URL param against `POST /api/auth/exchange` on bootstrap. The URL is scrubbed via `history.replaceState` before the network call (security bullet from the django-mojo review) and concurrent callers share one in-flight POST via the same single-flight pattern as `refreshToken()`.
8
+ - `PortalApp.checkAuthStatus()` calls `handleAuthCodeFromURL` before deciding the user is unauthenticated, so portals deployed on a different origin from the auth server boot directly into the authenticated state — no `/login` bounce, no countdown.
9
+ - Parity helpers added to the standalone auth surfaces: `MojoAuth.handleAuthCodeFromURL()` / `MojoAuth.exchangeAuthCode(code)` in `src/extensions/mojo-auth/mojo-auth.js`, and `auth.handleAuthCodeFromURL()` / `auth.exchangeAuthCode(code)` on the object returned by `createAuthClient` in `web-mojo/auth`.
10
+ - New event: `auth:exchange:failed` with `{ error }` payload. Existing `auth:login` is now also emitted by `TokenManager` on a successful exchange. Same-origin auth flows are unchanged — when no `?auth_code=` is present, `handleAuthCodeFromURL` is a synchronous no-op with no network call and no event.
11
+
12
+ ### CSS — Admin assistant panel: dark theme coverage
13
+
14
+ - The Admin extension's AI Assistant panel (`AssistantPanelView` + the
15
+ modal-fullscreen `AssistantView`) now honors `[data-bs-theme="dark"]`.
16
+ Previously the panel header, empty-state hero, suggestion chips,
17
+ composer input, history rail, conversation search, and thinking
18
+ indicator all rendered against hardcoded `#fff` / `#f7f7f8` surfaces —
19
+ loud against the framework's dark portal page (and especially loud
20
+ against the new `#0a0d11` mission-control palette). Each surface now
21
+ picks up `--bs-body-bg` / `--bs-tertiary-bg` / `--bs-secondary-bg`
22
+ under the dark theme. Hover/active states on conversation rows and
23
+ panel header buttons swap their `rgba(0, 0, 0, ...)` tints for the
24
+ matching `rgba(255, 255, 255, ...)` values. Light theme is unchanged;
25
+ no `!important`.
26
+
27
+ ### Feature — Map: disable scroll/zoom interaction at construction time
28
+
29
+ - New constructor options on `MapView`, `MapLibreView`, and (via
30
+ `mapOptions`) `MetricsCountryMapView`:
31
+ - `interactive` (default `true`) — master switch; `false` freezes all
32
+ user interaction (pan, zoom, keyboard, rotate).
33
+ - `scrollZoom`, `dragPan`, `doubleClickZoom`, `keyboard`, `touchZoom`
34
+ (all default `true`) — granular per-handler toggles.
35
+ - Cross-cutting names are translated per backend: `scrollZoom` →
36
+ Leaflet `scrollWheelZoom`, `dragPan` → Leaflet `dragging`, `touchZoom`
37
+ → MapLibre `touchZoomRotate`. Both views accept the same wrapper API.
38
+ - Defaults preserve today's fully-interactive behavior; existing call
39
+ sites are unchanged.
40
+ - `showZoomControl` / `showNavigationControl` remain independent — UI
41
+ buttons can still be shown on a non-interactive map.
42
+ - Programmatic camera changes (`setView()`, `setZoom()`, `flyTo()`,
43
+ `setPitch()`, `setBearing()`) are unaffected; the flags only gate
44
+ user input.
45
+ - Portal example pages (`extensions/map-view`, `extensions/map-libre-view`)
46
+ show both modes side-by-side.
47
+
48
+ ### CSS — Dark theme: deeper mission-control surface as default
49
+
50
+ - The framework's `[data-bs-theme="dark"]` palette now uses the deep
51
+ near-black surfaces previously scoped to `SecurityDashboardPage`:
52
+ page `#0a0d11`, card/tertiary `#11161d`, secondary `#161b22`, border
53
+ `#1f2630`, emphasis text `#e6ecf3`, muted text `#8a96a6`. Every
54
+ dark-mode page in consuming apps will look noticeably deeper and
55
+ more contrasty on upgrade.
56
+ - **Light theme is unchanged.**
57
+ - **Opt out:** apps that want the previous Bootstrap defaults can
58
+ override `--bs-body-bg`, `--bs-tertiary-bg`, etc. in their own CSS
59
+ under `[data-bs-theme="dark"]` — the framework block uses no
60
+ `!important`, so any later/higher-specificity rule wins.
61
+ - **Removed:** the `SecurityDashboardPage`-scoped overrides in
62
+ `charts.css` (`.portal-layout:has(.security-dashboard-page)`,
63
+ `.security-dashboard-page`, and the defensive `.page-container` /
64
+ `.portal-content` fall-throughs). The dashboard now inherits the
65
+ global palette seamlessly — no visual change.
66
+ - **Removed:** the `KPITile` `[data-bs-theme="dark"]` `--mojo-kpi-tile-*`
67
+ variable block. The tile component already falls back through
68
+ `--bs-tertiary-bg` / `--bs-emphasis-color` / `--bs-secondary-color` /
69
+ `--bs-border-color`, which now match the original dashboard values
70
+ 1:1, so the dedicated overrides became redundant. The
71
+ delta-badge and hover tints are kept since they don't map to a
72
+ Bootstrap token.
73
+ - **`--mojo-sidebar-dark-bg`** now drops to `#0d1117` under
74
+ `[data-bs-theme="dark"]` so `topnav-dark` and `sidebar-dark` sit
75
+ one tier above the page (`#0a0d11`) and one tier below cards
76
+ (`#11161d`) — portal chrome reads as a band rather than a raised
77
+ tile. The light-mode default (`#343a40`) is unchanged.
78
+ - **`[data-bs-theme="dark"] .sidebar-light`** now uses
79
+ `var(--bs-secondary-bg)` instead of a hardcoded `#2a2f36`, so the
80
+ rail stays one step above the page automatically and tracks any
81
+ future palette tweak.
82
+ - **Topbar default unchanged:** `--mojo-topnav-bg` still resolves to
83
+ `var(--bs-primary)` (brand-blue topbar) when no `topnav-*` class is
84
+ set. Consumers who want a deep mission-control topbar should use
85
+ `topnav-dark`, which now picks up the new `#0d1117`.
86
+
87
+ ### Changed — Admin sidebar Security menu restructured
88
+
89
+ - **Security Dashboard** is now a top-level sidebar item, placed directly
90
+ below the system **Dashboard** (route `?page=system/incident-dashboard`,
91
+ icon `bi-shield-check`).
92
+ - The single 12-child **Security** group has been split into three smaller
93
+ groups: **System Security** (Tickets, Incidents, Events, Rules),
94
+ **Network Security** (IPs, IP Sets, Blocked, Firewall Log), and
95
+ **Bouncer** (Signals, Devices, Bots).
96
+ - Labels were tightened: `Rule Engine` → `Rules`, `GeoIP` → `IPs`,
97
+ `Blocked IPs` → `Blocked`, `Bouncer Signals` → `Signals`,
98
+ `Bouncer Devices` → `Devices`, `Bot Signatures` → `Bots`.
99
+ - Routes and per-item permissions are unchanged. Pure menu-config edit in
100
+ `src/admin.js`; no page registrations or framework APIs changed.
101
+
102
+ ### Feature — SeriesChart axis label visibility
103
+
104
+ - New `showXLabels` / `showYLabels` options (default `true`) hide the X /
105
+ Y text labels independently. Gridlines (`showGrid`) are unaffected.
106
+ - When labels are hidden, the plot area grows into the freed padding
107
+ (`padBottom` 24→8 with `showXLabels: false`, `padLeft` 40→8 with
108
+ `showYLabels: false`). The X-label auto-rotation extra-padding path is
109
+ skipped when X labels are hidden.
110
+ - Plumbed through `MetricsChart` so dashboard panels can hide axis text
111
+ for compact tile-style displays.
112
+
113
+ ### Behavior — SeriesChart hover-dim is now opt-in
114
+
115
+ - New `highlightOnHover` option on SeriesChart (default `false`). Hovering
116
+ a bar or dot no longer dims the other series — the dim effect was
117
+ visually noisy on stacked-bar charts and distracting in dashboard
118
+ contexts.
119
+ - Pass `highlightOnHover: true` to restore the earlier always-on behavior.
120
+ - Plumbed through `MetricsChart`.
121
+
122
+ ### Behavior — SeriesChart legend default is now top-left
123
+
124
+ - New `legendJustify: 'start' | 'center' | 'end'` option (default
125
+ `'start'`). Combined with the existing `legendPosition: 'top'` default,
126
+ the SeriesChart legend now sits **top-left** instead of top-center.
127
+ - `legendJustify` maps to CSS `justify-content` for both the horizontal
128
+ flex (top/bottom legends) and the column flex (left/right legends).
129
+ - Invalid values fall back to `'start'` with a `console.warn`.
130
+ - To restore the prior top-center look, pass `legendJustify: 'center'`.
131
+ - Plumbed through `MetricsChart` (and via that, every dashboard chart
132
+ built on the metrics fetch path).
133
+
134
+ ### Fixed — Modal: descendant dropdowns/popovers no longer clipped at the card edge
135
+
136
+ - `.modal-content` had `overflow: hidden` (added with the hero-band redesign in
137
+ `ff27795`) which clipped any absolutely-positioned descendant — `MultiSelectDropdown`,
138
+ `ComboBox`, `CollectionMultiSelect`, plain Bootstrap `.dropdown-menu`, and any
139
+ context menu rendered inside a modal body.
140
+ - The hero band's `::before` pseudo-element already declares its own matching
141
+ `border-radius: 14px 14px 0 0`, so the ancestor clip was unnecessary for the
142
+ rounded chrome. Removing `overflow: hidden` restores Bootstrap's default
143
+ modal behavior — popovers can escape the card edge.
144
+ - No JS or component changes; the fix is a single CSS-rule removal in
145
+ `src/core/css/core.css`.
146
+
147
+ ### Feature — Security Dashboard rebuild + new framework primitives
148
+
149
+ - **`SecurityDashboardPage`** replaces the older tabbed
150
+ `IncidentDashboardPage` with a single scrolling mission-control page.
151
+ Route stays `system/incident-dashboard`. Seven sections answer the
152
+ one question a sysadmin actually asks: *what should I be doing right
153
+ now?*
154
+ - **Pulse** — 8 KPI tiles via one batched
155
+ `/api/metrics/series?with_delta=true` + parallel REST counts. Tiles
156
+ track NEW incidents (untriaged), not OPEN (already claimed).
157
+ - **Needs Attention** — list of priority>=8, status=new incidents.
158
+ Click row opens the existing `IncidentView` modal. Hover-revealed
159
+ inline resolve/pause actions for users with `manage_security`.
160
+ - **Threat Composition** — single 30-day stacked bar chart that
161
+ condenses `incident_events` / `firewall:blocks` / `bouncer:blocks`
162
+ / `auth:failures` into one view. 7D / 30D / 90D toggle.
163
+ - **Geography** — `MetricsCountryMapView` with slug-family selector
164
+ (Events / Incidents / Firewall / Logins).
165
+ - **Distributions** — three cards: status donut, priority bucket bars,
166
+ bouncer funnel (assessments → monitors → blocks).
167
+ - **Top Sources** — top IPs + top categories (last 7d). Tries
168
+ server-side `group_by` first; falls back to client-side aggregation
169
+ of recent 500 events when unsupported, with a fallback note in the
170
+ card subtitle.
171
+ - **Auth Failures** — uses the new `auth:failures` aggregate slug
172
+ directly (no client-side composition); 4 sub-tiles for password
173
+ resets / TOTP failures / sessions revoked / accounts deactivated.
174
+ - **System Health** — single `/api/incident/health/summary` call,
175
+ one row per discovered category, color dot from `level`, click row
176
+ drills into the linked incident.
177
+ - Sections 3-7 use **lazy mount** so they don't fetch until scrolled
178
+ into view.
179
+ - Refresh tiers via `Page.scheduleRefresh`: 60s for pulse +
180
+ needs-attention; 5min for everything else; manual refresh button
181
+ fires all tiers.
182
+ - Drill-downs use `Modal.drawer` for day / country / status-filter /
183
+ priority-bucket / IP / category / auth sub-tile clicks.
184
+
185
+ - **New framework primitives** (charts):
186
+ - **`KPITile`** (`web-mojo/charts`) — compact presentation-only tile:
187
+ label, big tabular value, color-coded delta badge, embedded
188
+ `MiniChart` sparkline. Renders pre-fetched data via constructor or
189
+ `setData()`. Click emits `tile:click`. Sits between `MiniChart`
190
+ (sparkline only) and `MetricsMiniChartWidget` (rich self-fetching
191
+ card). Delta rendering rules:
192
+ - `deltaPct` present → "+12%" / "−8%"
193
+ - `deltaPct` omitted (prev=0) + `delta` present → "+4" absolute
194
+ - both null → no badge
195
+ - never renders `Infinity%`
196
+ - `severity` (critical/high/warn/info/good) adds left-stripe accent
197
+ - `tone` ('bad' or 'good') decides whether rising = red or green
198
+ - **`KPIStrip`** (`web-mojo/charts`) — orchestrator for N `KPITile`s.
199
+ Single batched `/api/metrics/series?with_delta=true` call populates
200
+ all metric tiles, parallel REST count calls populate tiles defined
201
+ with `rest:` config, and one batched `/api/metrics/fetch` populates
202
+ sparklines for all metric tiles.
203
+
204
+ - **Extensions to existing components:**
205
+ - **`PieChart`** — new `centerLabel` and `centerSubLabel` options
206
+ render text in the donut center (when `cutout > 0`). Accept either
207
+ a static string or a function called with `({ total, segments })`.
208
+ - **`MetricsChart`** — new `withDelta` flag passes through to the
209
+ series endpoint; new `compactHeader` mode hides the gear menu and
210
+ shrinks the range toggle for use inside dashboard panels.
211
+ - **`Modal.drawer({ eyebrow, title, meta, view })`** — standardised
212
+ drill-down modal header (eyebrow tag, title, meta row of icon-
213
+ prefixed spans). Accepts a `View` instance OR raw HTML body.
214
+ - **`Page.scheduleRefresh(handler, intervalMs, { tier, immediate })`**
215
+ — registers a recurring handler that auto-clears in `onExit`.
216
+ Replaces the `setInterval`/`clearInterval` boilerplate in every
217
+ dashboard. `runScheduledRefreshes(tier?)` fires all (or one tier).
218
+ - **`View.addChild(child, { lazyMount: true })`** — defers the
219
+ child's render until its container scrolls into viewport via
220
+ `IntersectionObserver`. Container gets a 1px placeholder min-height
221
+ so the observer can detect 0-content placeholders. Disconnects on
222
+ destroy. Falls back to immediate render when IO isn't available.
223
+
224
+ - **Examples portal:**
225
+ - New `KPIStripExample` at `extensions/charts/kpi-strip` —
226
+ demonstrates standalone `KPITile`s (delta rules) and `KPIStrip`
227
+ (batched fetch).
228
+ - `PieChartExample` updated to show the new doughnut center label.
229
+
230
+ ### Feature — App-level theme management
231
+
232
+ - **`WebApp` now owns the user's light/dark theme.** New public API:
233
+ - `app.setTheme('light' | 'dark' | 'system')` — persists the preference,
234
+ applies `data-bs-theme` to `<html>`, emits `'theme:changed'` on
235
+ `app.events` with `{ theme, resolved }`.
236
+ - `app.getTheme()` — returns the stored preference.
237
+ - `app.getResolvedTheme()` — returns the currently applied
238
+ `'light' | 'dark'` (resolves `'system'` via `prefers-color-scheme`).
239
+ - **Default preference is `'system'`** — first-time visitors automatically
240
+ get the theme that matches their OS. The `prefers-color-scheme` media
241
+ listener tracks OS theme changes live while the preference is `'system'`.
242
+ - **Storage:** the preference is persisted to `localStorage` under
243
+ `${appName}:theme` (mirrors the existing PortalApp sidebar-state
244
+ pattern). All reads/writes are wrapped in try/catch — private mode and
245
+ disabled storage degrade gracefully.
246
+ - **No flash:** the manager runs in the WebApp constructor so
247
+ `data-bs-theme` is set before the first view renders.
248
+ - **PortalApp auto-injects a topbar theme toggle** into the usermenu:
249
+ Light / Dark / System items with `bi-sun`, `bi-moon-stars`,
250
+ `bi-circle-half` icons. The currently selected option is marked
251
+ active. Opt out with `topbar.themeToggle: false`.
252
+ - **TopNav dropdown items now honor an `active: true` flag** — the
253
+ template renders `class="dropdown-item active"` for selected items
254
+ (used by the new theme toggle and available to any caller).
255
+ - **`examples/portal/app.js` simplified** — the manual `theme-light` /
256
+ `theme-dark` action handlers are gone; the framework toggle handles
257
+ them.
258
+ - **New module:** `src/core/utils/ThemeManager.js`.
259
+
260
+ ### CSS — Dark-theme coverage for sidebar treatments, SideNavView, ChatView, TimelineView
261
+
262
+ - **`sidebar-light` under `data-bs-theme="dark"`** now renders against a
263
+ softer dark surface (`#2a2f36`) instead of bright white. Hover, active,
264
+ group-header, and muted-text selectors all adapt to the dark palette.
265
+ Treatment classes remain independent of the global theme — devs can
266
+ still mix `sidebar-light` / `sidebar-dark` with either.
267
+ - **`sidebar-dark` under `data-bs-theme="dark"`** got a sanity-pass hover
268
+ override so the active state remains distinguishable from the base.
269
+ - **`SideNavView`** now has dark-theme overrides in `portal.css` covering
270
+ the rail bg, active accent, hover, group-label, and dropdown-collapse
271
+ mode. Base inline styles in the component template are unchanged.
272
+ - **`ChatView` (`chat.css`)** picks up dark-theme rules for the
273
+ container, message bubbles (left), input area, attachment states,
274
+ file-attachment overlay, and the WebKit scrollbar. Bubble `right`
275
+ keeps `--bs-primary` from the base rule (theme-aware).
276
+ - **`TimelineView`** ships its own `src/extensions/timeline/timeline.css`
277
+ for the first time — class-based base styles for the connector line,
278
+ marker, dot, content card, and meta surfaces, plus `data-bs-theme="dark"`
279
+ overrides where Bootstrap tokens aren't enough on their own.
280
+ Auto-imported from `TimelineView.js`.
281
+
282
+ ### Refactor — In-`src/` callers migrated from Dialog.* to Modal.* / ModalView
283
+
284
+ - **All in-`src/` callers** migrated from the deprecated `Dialog.*` API
285
+ to the canonical `Modal.*` (static API) / `ModalView` (instance class)
286
+ surface. 60 files touched across `src/core/`, `src/extensions/admin/*`,
287
+ `src/extensions/lightbox/*`, `src/extensions/charts/*`,
288
+ `src/extensions/map/*`, `src/extensions/user-profile/*`.
289
+ - **Pure fire-and-forget `new Dialog({...})` sites** (7) collapsed to
290
+ one-line `Modal.show(view, { size, header, title })` calls.
291
+ - **Instance-handle `new Dialog({...})` sites** (11) now use
292
+ `new ModalView({...})` — same instance API (`on('action:*')`,
293
+ `setLoading`, `element`, `hide()`, `destroy()`) since `Dialog`
294
+ already re-exported `ModalView` under the hood.
295
+ - **`Dialog.show*()` static calls** mechanically renamed: `showDialog
296
+ → dialog`, `showForm → form`, `showModelForm → modelForm`, `showData
297
+ → data`, `showCode → code`, `showModelView → showModelView`,
298
+ `updateModelImage → updateModelImage`, `showBusy/hideBusy` (alias
299
+ preserved on `Modal.*`), `alert/confirm/prompt` (identical signatures).
300
+ - **`WebApp.showLoading/hideLoading/showModelView/showModelForm/showForm/
301
+ showDialog/showAlert`** internal lazy imports now resolve `Modal.js`
302
+ instead of `Dialog.js`.
303
+ - **Pre-existing bug fixed**: `JobHealthView.onActionSystemSettings()`
304
+ called `Dialog.showAlert(...)` — `showAlert` was never wired on the
305
+ shim. The System Settings button now resolves through `Modal.alert`.
306
+ - **`Model.showError()`** also migrated from a (broken, unimported)
307
+ `Dialog.alert(...)` global reference to a dynamic `Modal.alert`
308
+ import, matching the lazy-import pattern WebApp uses.
309
+ - **Public surface unchanged**: the `Dialog.js` shim and the public
310
+ `Dialog` re-exports in `src/index.js` / `src/lite/index.js` remain in
311
+ place for downstream consumers. Their removal is a separate breaking
312
+ change PR.
313
+
314
+ ### Refactor — Dialog.js split into ModalView + Modal + focused helpers
315
+
316
+ - **`Dialog.js` (1,987 lines) split** into focused modules in
317
+ `src/core/views/feedback/`:
318
+ - **`ModalView.js`** — the underlying `View` class. Owns Bootstrap 5
319
+ modal mechanics (lifecycle, sizing, z-index stacking, header/body/
320
+ footer composition, button rendering, context menu).
321
+ - **`Modal.js`** — canonical static API: `dialog`, `show`, `showModel`,
322
+ `showModelView`, `alert`, `confirm`, `prompt`, `form`, `modelForm`,
323
+ `data`, `code`, `htmlPreview`, `updateModelImage`, `loading`. A new
324
+ `_renderAndAwait` helper consolidates ~300 lines of duplicated
325
+ render/show/resolve/destroy code.
326
+ - **`BusyIndicator.js`** — singleton frosted-glass loading overlay.
327
+ - **`CodeViewer.js`** — Prism-highlighted code block view + statics.
328
+ - **`HtmlPreview.js`** — sandboxed iframe preview view.
329
+ - **`Dialog.js`** — thin compatibility shim. Default-exports
330
+ `ModalView`; every legacy static (`Dialog.alert`, `Dialog.showForm`,
331
+ `Dialog.showBusy`, …) is a one-line forward to the matching
332
+ `Modal.*` method. Existing `new Dialog({...})` and `Dialog.show*()`
333
+ callers continue to work unchanged.
334
+ - **Busy-indicator overlays consolidated**. The legacy dark
335
+ `mojo-busy-indicator` is gone; only the modern frosted-card
336
+ `mojo-loading-overlay` remains. `Modal.loading()` / `Modal.showBusy()`
337
+ / `Dialog.showBusy()` all route through the same singleton.
338
+ - **`ModalView` is now a public export** (`src/index.js`,
339
+ `src/lite/index.js`) — use it directly when you need a long-lived
340
+ modal handle (streaming `setContent`, external event wiring,
341
+ subclassing). Most callers should still prefer the static `Modal.*`
342
+ API.
343
+ - **No consumer change required.** The 24 `new Dialog({...})` and
344
+ `Dialog.show*()` sites already in `src/` continue to work via the
345
+ shim. A separate request (`planning/requests/migrate-legacy-dialog-callers.md`)
346
+ tracks the eventual sweep.
347
+ - New docs: `docs/web-mojo/components/ModalView.md`. Updated:
348
+ `components/Modal.md`, `components/Dialog.md` (now a deprecation
349
+ notice + migration table), `README.md`, `docs/agent/architecture.md`.
350
+
351
+ ### Improved — SeriesChart axis labels (nice numbers, formats, rotation)
352
+
353
+ - **Y-axis ticks** now snap to clean `1/2/5 × 10ⁿ` values via the Heckbert
354
+ nice-number algorithm. The "API Metrics" chart and similar `MetricsChart`
355
+ consumers now show `0, 25, 50, 75, 100` instead of `0, 28.77, 57.54,
356
+ 86.31, 115.08, 143.85`. `gridLines` becomes a target count; the algorithm
357
+ picks the closest clean fit.
358
+ - **X-axis labels auto-rotate** `-45°` when they would overlap their slots.
359
+ The chart's bottom padding expands automatically. No configuration
360
+ required; rotation kicks in when labels collide.
361
+ - **`MetricsChart` defaults `xLabelFormat`** based on `granularity`:
362
+ `minutes`/`hours` → `date:'HH:mm'` (`17:00`), `days`/`weeks` → `date:'MMM
363
+ D'` (`Apr 26`), `months` → `date:'MMM YYYY'` (`Apr 2026`). Caller-supplied
364
+ `tooltip.x` still wins. The default is re-applied when `setGranularity()`
365
+ is called.
366
+ - Truncation cap raised from 10 → 24 chars (rotation handles long labels;
367
+ truncation is the fallback for pathological cases like UUIDs).
368
+ - `_formatAxisValue` adds a `B` (billion) branch and step-aware decimal
369
+ precision so very small or very large nice-tick ranges read cleanly.
370
+
371
+ ### Docs — Phase 3 of taxonomy realignment (undocumented public exports)
372
+
373
+ - New doc pages in `docs/web-mojo/`:
374
+ - `core/Router.md` — `Router` class: `?page=` URL handling, `navigate`, route patterns, `route:changed` / `route:notfound` events.
375
+ - `components/ProgressView.md` — file-upload progress UI; `updateProgress`, `markCompleted`, etc.
376
+ - `components/SimpleSearchView.md` — searchable list bound to a `Collection`; emits `item:selected`.
377
+ - `utils/MustacheFormatter.md` — lower-level template renderer behind `View`; `registerFormatter` for custom pipes.
378
+ - `mixins/FileDropMixin.md` — `applyFileDropMixin(ViewClass)` + `enableFileDrop({…})` + `onFileDrop(files, …)`.
379
+ - **Breaking**: `DataWrapper` named export removed from `src/index.js`. Triage found zero consumers (no `src/`, no `examples/`, no `test/` references). The class itself remains in `src/core/utils/MOJOUtils.js`; only the public re-export is gone.
380
+ - README + AGENT cross-links updated to surface the new pages.
381
+
382
+ ### Added — Assistant: `assistant_text` event + chart-option passthrough
383
+
384
+ - **New `assistant_text` WS event** is now handled in `AssistantView`,
385
+ `AssistantPanelView`, and `AssistantContextChat`. When the model writes
386
+ prose alongside tool calls in the same turn, that intermediate text now
387
+ renders as an assistant bubble before the tool-call status indicators.
388
+ `assistant_response` remains the terminal signal that clears the thinking
389
+ indicator and re-enables input. Conversations that don't emit
390
+ `assistant_text` are unchanged.
391
+ - **`AssistantContextChat` gains a small `_adoptConversationId` helper** so
392
+ all three assistant views handle new-conversation events uniformly. The
393
+ adapter remains the canonical owner of `conversationId`.
394
+ - **Chart blocks now forward new `SeriesChart` / `PieChart` options** from
395
+ the LLM into the chart constructor via a strict snake_case → camelCase
396
+ allowlist. New block-level fields: `stacked`, `grouped`, `crosshair_tracking`,
397
+ `cutout`, `show_labels`, `show_percentages`, `colors`, `show_legend`,
398
+ `legend_position`. New per-series fields: `color`, `fill`, `smoothing`.
399
+ Existing minimal chart blocks render identically.
400
+ - Stale doc comment in `AssistantMessageView._renderChartBlock` corrected
401
+ (`MiniPieChart`/`MiniSeriesChart` → `PieChart`/`SeriesChart`).
402
+
403
+ ### Docs — Phase 2 of taxonomy realignment
404
+
405
+ - New doc pages in `docs/web-mojo/`:
406
+ - `extensions/Auth.md` — `mountAuth` + `createAuthClient` (`web-mojo/auth`).
407
+ - `extensions/UserProfile.md` — `UserProfileView`, `PasskeySetupView`, the 11 section views (`web-mojo/user-profile`).
408
+ - `extensions/DocIt.md` — `DocItApp` and the four documentation pages (`web-mojo/docit`).
409
+ - `services/TokenManager.md` — JWT lifecycle, single-flight refresh, the auth gate.
410
+ - `utils/DjangoLookups.md` — `field__lookup` syntax, `LOOKUPS` map, `parseFilterKey`, `formatFilterDisplay`.
411
+ - `utils/ConsoleSilencer.md` — log-level filtering, URL/`localStorage` runtime overrides.
412
+ - `src/extensions/auth/README.md` deleted (the new `extensions/Auth.md` is canonical).
413
+ - `src/extensions/mojo-auth/mojo-auth.js` gets a `LEGACY shim` JSDoc header. No package entry, no internal callers; new code uses `web-mojo/auth`. File is kept for downstream apps still linking it directly.
414
+ - `docs/web-mojo/README.md`, `docs/web-mojo/AGENT.md`, and root `AGENT.md` cross-links updated to surface the new pages.
415
+ - `planning/notes/taxonomy-audit.md` notes that `src/core/utils/TemplateResolver.js` is orphaned (zero consumers, not exported) — tracked for a future cleanup pass.
416
+
417
+ ### Bug fixes — typed alerts now actually render their type
418
+
419
+ - `Dialog.alert(message, title, options)` (and `Modal.alert(...)`) silently
420
+ dropped the second and third arguments — every typed alert rendered as
421
+ `info` regardless of the `type` option. The signature is now correctly
422
+ honored: `Modal.alert('Saved!', 'Done', { type: 'success' })` produces a
423
+ success-styled alert. Object-form (`Modal.alert({ message, title, type })`)
424
+ and single-string form (`Modal.alert('hi')`) continue to work unchanged.
425
+ - `WebApp.showError / showSuccess / showInfo / showWarning` were broken in
426
+ the same way and rendered identically. They now produce visually distinct
427
+ typed alerts and route through `Modal.alert` directly.
428
+ - `WebApp.confirm` also routes through `Modal.confirm` for consistency.
429
+
430
+ ### API direction — Modal is the canonical modal/dialog surface
431
+
432
+ - `Modal.alert / Modal.confirm / Modal.prompt` are now the canonical
433
+ implementations. `Dialog.alert / Dialog.confirm / Dialog.prompt` have been
434
+ rewritten as thin pass-throughs that delegate to Modal — all existing
435
+ `Dialog.*` callers continue to work unchanged, but new code should call
436
+ `Modal.*` directly.
437
+ - `Dialog` itself (the underlying View class) is unchanged: the constructor,
438
+ `Dialog.showDialog / showForm / showModelForm / showCode / showHtmlPreview`,
439
+ z-index management, and `Dialog.showBusy / hideBusy` continue to live there.
440
+ Only the three top-level helpers moved.
441
+ - `docs/web-mojo/components/Modal.md` is now the canonical doc; Dialog.md
442
+ retains its deprecation banner with pass-through notes under each helper.
443
+
444
+ ### UI / CSS — refreshed dialog chrome and typed-alert accents
445
+
446
+ - All dialogs share a refreshed chrome: rounded corners (14px), soft
447
+ drop-shadow, gradient header tint, and a small offset circular close
448
+ button anchored to the top-right corner.
449
+ - Typed alerts (`Modal.alert(... { type })`) now get a 6px colored hero
450
+ band across the top of the modal card and a subtle tinted card background,
451
+ so each type is visually distinct without relying on an icon alone. Color
452
+ tokens: success=`#198754`, error=`#dc3545`, warning=`#ffc107`;
453
+ info/default uses `--mojo-dialog-accent` (see below).
454
+ - New CSS variable `--mojo-dialog-accent`, defined at `:root` and defaulting
455
+ to `var(--bs-primary)`. Drives the header gradient tint and the info-typed
456
+ alert accent. Override at `:root` (or any scope) to set a custom brand
457
+ color without touching `--bs-primary`.
458
+ - Dark-mode rules added under `prefers-color-scheme: dark`, mirroring the
459
+ shape of the existing toast.css dark-mode block.
460
+ - Internal styling hook: typed alerts add `modal-alert modal-alert-{type}`
461
+ to the modal root for downstream apps that want to override the look.
462
+
463
+ ### Breaking — Admin models moved to a separate package entry
464
+
465
+ - 14 admin-coupled `Model` / `Collection` classes have moved out of `src/core/models/`
466
+ into `src/extensions/admin/models/`. The affected models: `AWS`, `Assistant`,
467
+ `Bouncer`, `Email`, `Incident`, `IPSet`, `Job`, `JobRunner`, `LoginEvent`,
468
+ `PublicMessage`, `Push`, `Phonehub`, `ScheduledTask`, `Tickets`.
469
+ - 7 of those (`AWS`, `Email`, `Incident`, `Job`, `JobRunner`, `Push`, `Tickets`)
470
+ were previously re-exported from the main `web-mojo` entry. They are no longer.
471
+ **Migration**: switch to the new `web-mojo/admin-models` entry.
472
+ ```js
473
+ // before
474
+ import { Job, JobList, Incident } from 'web-mojo';
475
+ // after
476
+ import { Job, JobList, Incident } from 'web-mojo/admin-models';
477
+ ```
478
+ - New package entry `web-mojo/admin-models` ships the 14 admin models as **data
479
+ only** — no DOM, Bootstrap, or template deps. Use this entry from a Node
480
+ script, an API client, or any non-portal UI. The `web-mojo/admin` entry
481
+ remains the way to get the admin **pages** (sidebar, dashboards, table pages).
482
+ - `Log` and `ShortLink` stay in `src/core/models/` because they have legitimate
483
+ non-admin consumers (`FileView`'s share-link feature, `user-profile`'s
484
+ activity section). The audit's "admin-only" classification was overzealous on
485
+ those two; their import paths and main-entry export are unchanged.
486
+ - `docs/web-mojo/models/BuiltinModels.md` now covers only the 10 still-core
487
+ models. Admin models documented in `docs/web-mojo/extensions/Admin.md`.
488
+ - 73 internal `@core/models/<X>.js` import statements rewritten to
489
+ `@ext/admin/models/<X>.js` across the 58 admin files that consume them.
490
+
491
+ ### Examples Portal — area-mismatch realignment
492
+
493
+ - `TabView` moved from `extensions/` → `components/` (source has always been at
494
+ `src/core/views/navigation/TabView.js`). Routes change:
495
+ `?page=extensions/tab-view` → `?page=components/tab-view`. Doc moves to
496
+ `docs/web-mojo/components/TabView.md`.
497
+ - `TablePage` doc moved from `components/` → `pages/` (source is at
498
+ `src/core/pages/TablePage.js`). Doc path:
499
+ `docs/web-mojo/pages/TablePage.md`. Example folder unchanged (already at
500
+ `examples/portal/examples/pages/TablePage/`).
501
+ - `FileUpload` moved from `extensions/` → `services/` (source is at
502
+ `src/core/services/FileUpload.js`). Routes change:
503
+ `?page=extensions/file-upload` → `?page=services/file-upload`. Doc moves to
504
+ `docs/web-mojo/services/FileUpload.md`.
505
+ - `docs/web-mojo/extensions/metricsminichartwidget.md` renamed to
506
+ `MetricsMiniChartWidget.md` to match sibling-doc casing.
507
+ - New `FormBuilder` example at
508
+ `examples/portal/examples/forms/FormBuilder/FormBuilderExample.js`. Demos
509
+ `buildFormHTML()` and `buildFieldsHTML()`. `FormBuilder` is now exported
510
+ from the main `web-mojo` entry (was previously only available via
511
+ `@core/forms/FormBuilder.js`).
512
+ - Dead `src/core/views/map/MapView.js` duplicate removed (canonical version
513
+ is `src/extensions/map/MapView.js`, exported via `web-mojo/map`).
514
+ - `docs/web-mojo/forms/FORMS_DOCUMENTATION_PLAN.md` (an internal planning
515
+ doc that snuck into published docs) moved to `planning/notes/`.
516
+
517
+ ### Examples Portal — hub-and-spoke navigation
518
+
519
+ - Replaced the single 75-item sidebar with a hub menu plus four topic
520
+ sub-sidebars: **Architecture**, **Components**, **Forms**, **Extensions**.
521
+ The hub pins a curated **Start Here** path (View, Templates, Model, Page,
522
+ WebApp). Each topic sub-sidebar ends with a "Back to Examples" item that
523
+ returns to the hub, mirroring the existing admin "Exit Admin" pattern.
524
+ - Component variants (Dialog form / context-menu / custom-body, TableView
525
+ batch-actions / custom-row / server-collection, …) now collapse under their
526
+ parent in the sidebar instead of rendering as siblings. Routes are unchanged.
527
+ - `examples.registry.json` now exposes a `topics` tree (each topic → groups →
528
+ items with optional one-level children), and every page record carries
529
+ `topic` and `group` fields. The legacy `menu` array is kept for one cycle.
530
+ - `docs/web-mojo/examples.md` is regenerated under H2/H3 topic/group
531
+ headings and now correctly links each variant row to its own source file.
532
+ - Sidebar widened from the framework default 250px to 300px in this portal
533
+ (set via `--mojo-sidebar-width` CSS variable; framework default unchanged).
534
+ - No framework changes — `Sidebar` already supported multiple registered
535
+ menus, route-driven menu switching, and per-item `handler` callbacks.
536
+
537
+ ### Breaking — Charts extension rebuilt on native SVG
538
+
539
+ - **Chart.js dependency removed.** `BaseChart` previously injected
540
+ `https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js` at runtime;
541
+ that fetch no longer happens. `chart.js` was never in `package.json` and
542
+ remains absent. ~2,400 LOC of source removed: `BaseChart.js` (1,329),
543
+ the old Chart.js-backed `SeriesChart.js` (533), and the old `PieChart.js` (567).
544
+ - **`SeriesChart` rewritten as a native SVG component** (promoted from
545
+ `MiniSeriesChart`). Multi-dataset line/bar/area, click-to-toggle legend,
546
+ hover-isolated highlighting, animated `setData` updates (`animate: false`
547
+ to opt out), built-in 10-color palette + golden-angle HSL fallback, and
548
+ per-series `color` overrides.
549
+ - **Bar charts default to stacked.** `chartType: 'bar'` is stacked unless you
550
+ pass `stacked: false` (or the alias `grouped: true`). `stacked: 'auto'` is
551
+ the default and resolves to `true` for bar, `false` for line/area.
552
+ - **`PieChart` rewritten as a native SVG component** (promoted from
553
+ `MiniPieChart`). Adds slice-edge labels, `chart:click` drill-down, animated
554
+ slice tweens, and an optional `endpoint:` shim that auto-fetches via
555
+ `app.rest.GET` in `onInit`.
556
+ - **`MetricsChart` rewritten on top of native `SeriesChart`.** Public API
557
+ preserved: `endpoint`, `account`, `granularity`, `slugs`, `category`,
558
+ `dateStart`/`dateEnd`, `defaultDateRange`, `quickRanges`, `availableMetrics`,
559
+ `maxDatasets`, `groupRemainingLabel`, `chartType`, `title` (HTML), `height`,
560
+ `yAxis`, `tooltip`, `showDateRange`, `showGranularity`. Methods unchanged:
561
+ `fetchData`, `refresh`, `setGranularity`, `setDateRange`, `setMetrics`,
562
+ `getStats`. Admin call sites (`AdminDashboardPage`, `CloudWatchChart`,
563
+ `ShortLinkView`, `PushDashboardPage`) need no changes.
564
+ - **PNG export moved out of charts into a standalone helper.**
565
+ `MetricsChart.export(format)` removed. Use:
566
+ `import { exportChartPng } from 'web-mojo/charts'; exportChartPng(chart);`
567
+ Works on any view containing an `<svg>`.
568
+ - **`MiniSeriesChart` and `MiniPieChart` exports removed.** The dynamic imports
569
+ in `AssistantMessageView` were updated to `SeriesChart`/`PieChart`. If any
570
+ downstream code imported `MiniSeriesChart`/`MiniPieChart` directly, switch
571
+ to `SeriesChart`/`PieChart`.
572
+ - **Removed at the chart level**: WebSocket integration, `autoRefresh`,
573
+ `setEndpoint`, `setWebSocketUrl`, theme toggle, `chartOptions` passthrough,
574
+ `dataTransform`. Pages own those concerns.
575
+ - New examples portal entries: `SeriesChartExample.js`, `PieChartExample.js`.
576
+ `ChartsExample.js` continues to demo `MiniChart` (the dedicated single-series
577
+ sparkline — kept as-is).
578
+ - `src/charts.js` bumped from 2.1.0 → 3.0.0.
579
+
580
+ ### Added — Charts: floating crosshair tooltip on line charts
581
+
582
+ - **`SeriesChart` gains optional `crosshairTracking` mode** for line/area
583
+ charts. With `crosshairTracking: true`, a transparent rect overlays the
584
+ plot area; on `mousemove` the chart snaps to the nearest column and shows
585
+ a vertical crosshair, a per-dataset ghost dot, and the existing multi-row
586
+ tooltip. Off by default — bar charts ignore the flag.
587
+ - **Bootstrap-theme-aware** — the crosshair line uses
588
+ `var(--bs-secondary-color)` via `currentColor` and auto-adapts under
589
+ `data-bs-theme="dark"`. Pass `crosshairColor` for an explicit override
590
+ (accepts CSS color strings or `var(--…)` references) and `crosshairWidth`
591
+ for a thicker line.
592
+ - **`chart:click` semantics** — in tracking mode, click emits the column
593
+ for the first visible dataset (matches Chart.js `mode: 'index'`). Per-
594
+ dataset clicks remain available with `crosshairTracking: false`.
595
+ - New examples-portal demo card under
596
+ `examples/portal/examples/extensions/Charts/SeriesChartExample.js`.
597
+
5
598
  ### Changed
599
+ - **Examples directory rewritten** — The previous `examples/portal/` (37 pages, 17 templates, ~13k LOC) and all standalone HTML demos have been moved to `examples/legacy/` (with git history preserved). The new `examples/portal/` is a single canonical [`PortalWebApp`](docs/web-mojo/core/PortalWebApp.md) shell whose taxonomy mirrors `docs/web-mojo/`. Each documented component has a folder under `examples/portal/examples/<area>/<Component>/` with a single-file canonical-and-demo `<Component>Example.js` (≤150 LOC, inline template, imports only from `web-mojo`) and an `example.json` manifest. The portal sidebar and route registration are generated by `examples/portal/scripts/build-registry.js` from those manifests, so adding an example never touches `app.js`. Coverage: **59 examples across 8 areas** (`core`, `pages`, `services`, `components`, `extensions`, `forms`, `forms/inputs`, `models`).
600
+ - **`docs/web-mojo/examples.md`** — Generated index of every example file, written by the registry generator. Linked from `docs/web-mojo/README.md`.
601
+ - **Per-doc cross-links** — Each component doc under `docs/web-mojo/` now ends with an `## Examples` section listing the runnable example file(s) for that component. Sections are managed by `examples/portal/scripts/cross-link-docs.js` and bracketed with `<!-- examples:cross-link begin/end -->` markers so reruns are idempotent.
602
+ - **`vite.config.js`** — Added `web-mojo/timeline` and `web-mojo/models` aliases so per-extension package imports resolve in dev.
603
+
604
+ ### Added
605
+ - **`examples/auth/`** — Fresh standalone login flow built on `FormView` + `Rest`, posts to `/login` and redirects to `/examples/portal/` on success. Replaces the old multi-page auth example (now under `examples/legacy/auth/`).
606
+ - **`docs/web-mojo/components/ContextMenu.md`**, **`docs/web-mojo/forms/MultiStepWizard.md`**, **`docs/web-mojo/forms/SearchFilterForms.md`** — Three new doc pages covering components and patterns the legacy portal demonstrated but the docs hadn't.
607
+
608
+ ### Changed (legacy)
6
609
  - **FileView consolidated** — The three overlapping file components (`src/core/views/data/FileView.js` legacy, `src/extensions/admin/storage/FileView.js` admin, and the small `FilePreviewView` chat card) have been reduced to one canonical `FileView` at `src/core/views/data/FileView.js`, exported from both `web-mojo` and `web-mojo/admin`. The new component uses a `SideNavView` layout (Preview / Details / Renditions / Metadata) and drives its Preview section from the backend `category` field (`image`, `video`, `audio`, `pdf`, `document`, `spreadsheet`, `presentation`, `archive`, `other`) — each category gets a purpose-built preview (inline `<video>`/`<audio>`, lightbox gallery, PDF viewer, or download-focused card). `LightboxGallery` and `PDFViewer` are accessed optionally via `window.MOJO.plugins.*` and fall back to `window.open` when the lightbox extension isn't loaded. Metadata section is hidden when empty. `FilePreviewView` (chat attachment card) is unchanged.
7
610
  - **FileView handles async renditions** — The backend now generates renditions asynchronously: `upload_status` flips to `completed` immediately while thumbnails/transcodes run on a background worker. FileView now shows a "Renditions are being generated" placeholder in the Renditions section (with a manual Refresh button) when `File.isRenditionsProcessing()` returns true, and kicks off an automatic background poll (`model.fetch()` every 5s, up to 5 minutes) that stops as soon as the renditions map populates. The poll is cancelled in `onBeforeDestroy`. Preview and Renditions sections listen for `change` on the model and re-render in place as the new URLs arrive, so video posters and rendition rows appear without a manual refresh.
8
611
  - **`File` model helpers** — Added `getCategory()` (with content_type fallback), `hasRenditions()`, `isRenditionsProcessing()`, `getRenditions()`, `getBestImageRendition()`, `getThumbnailUrl()`, and `regenerateRenditions(roles?)` to the `File` model.
package/README.md CHANGED
@@ -131,7 +131,7 @@ Complete authentication system with JWT tokens, login/register forms, and sessio
131
131
  Image and PDF viewers with editing capabilities including cropping and annotation.
132
132
 
133
133
  #### 📊 Charts (`web-mojo/charts`)
134
- Interactive charts built on Chart.js with PieChart, SeriesChart, and more.
134
+ Native SVG charts (SeriesChart, PieChart, MetricsChart) with no Chart.js dependency. `SeriesChart` supports opt-in `crosshairTracking` for floating crosshair + multi-row tooltip on line/area charts. Y-axis ticks snap to clean values via the Heckbert nice-number algorithm; X labels auto-rotate when they overflow their slots. `MetricsChart` defaults `xLabelFormat` from granularity (minutes/hours → `HH:mm`, days/weeks → `MMM D`, months → `MMM YYYY`).
135
135
 
136
136
  #### 📚 Documentation (`web-mojo/docit`)
137
137
  Full-featured documentation portal system with markdown editing and syntax highlighting.
@@ -316,7 +316,7 @@ Our documentation is organized into focused sections — browse online or click
316
316
  - [DataView](https://nativemojo.com/web-mojo/#components/DataView.md) — structured data display
317
317
 
318
318
  ### Extensions
319
- - [Charts](https://nativemojo.com/web-mojo/#extensions/Charts.md) — Chart.js integration (SeriesChart, PieChart)
319
+ - [Charts](https://nativemojo.com/web-mojo/#extensions/Charts.md) — Native SVG charts (SeriesChart, PieChart, MetricsChart)
320
320
  - [Admin](https://nativemojo.com/web-mojo/#extensions/Admin.md) — pre-built admin pages and views
321
321
  - [TabView](https://nativemojo.com/web-mojo/#extensions/TabView.md) — tab navigation
322
322
  - [MapView](https://nativemojo.com/web-mojo/#extensions/MapView.md) / [MapLibreView](https://nativemojo.com/web-mojo/#extensions/MapLibreView.md) — maps