@creezio/shell-ui 0.22.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 (232) hide show
  1. package/README.md +336 -0
  2. package/dist/adapters/nav-shell.d.ts +54 -0
  3. package/dist/adapters/nav-shell.d.ts.map +1 -0
  4. package/dist/adapters/nav-shell.js +112 -0
  5. package/dist/adapters/nav-shell.js.map +1 -0
  6. package/dist/brand.d.ts +64 -0
  7. package/dist/brand.d.ts.map +1 -0
  8. package/dist/brand.js +59 -0
  9. package/dist/brand.js.map +1 -0
  10. package/dist/core-nav.d.ts +17 -0
  11. package/dist/core-nav.d.ts.map +1 -0
  12. package/dist/core-nav.js +40 -0
  13. package/dist/core-nav.js.map +1 -0
  14. package/dist/index.d.ts +29 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +24 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/lib/api-scopes.d.ts +24 -0
  19. package/dist/lib/api-scopes.d.ts.map +1 -0
  20. package/dist/lib/api-scopes.js +78 -0
  21. package/dist/lib/api-scopes.js.map +1 -0
  22. package/dist/lib/catalog-suspense-key.d.ts +3 -0
  23. package/dist/lib/catalog-suspense-key.d.ts.map +1 -0
  24. package/dist/lib/catalog-suspense-key.js +12 -0
  25. package/dist/lib/catalog-suspense-key.js.map +1 -0
  26. package/dist/lib/data-changed.d.ts +51 -0
  27. package/dist/lib/data-changed.d.ts.map +1 -0
  28. package/dist/lib/data-changed.js +227 -0
  29. package/dist/lib/data-changed.js.map +1 -0
  30. package/dist/lib/desktop-home-path.d.ts +14 -0
  31. package/dist/lib/desktop-home-path.d.ts.map +1 -0
  32. package/dist/lib/desktop-home-path.js +29 -0
  33. package/dist/lib/desktop-home-path.js.map +1 -0
  34. package/dist/lib/geo-distance.d.ts +6 -0
  35. package/dist/lib/geo-distance.d.ts.map +1 -0
  36. package/dist/lib/geo-distance.js +27 -0
  37. package/dist/lib/geo-distance.js.map +1 -0
  38. package/dist/lib/img.d.ts +2 -0
  39. package/dist/lib/img.d.ts.map +1 -0
  40. package/dist/lib/img.js +40 -0
  41. package/dist/lib/img.js.map +1 -0
  42. package/dist/lib/keepalive-eviction.d.ts +14 -0
  43. package/dist/lib/keepalive-eviction.d.ts.map +1 -0
  44. package/dist/lib/keepalive-eviction.js +44 -0
  45. package/dist/lib/keepalive-eviction.js.map +1 -0
  46. package/dist/lib/ops-track.d.ts +29 -0
  47. package/dist/lib/ops-track.d.ts.map +1 -0
  48. package/dist/lib/ops-track.js +37 -0
  49. package/dist/lib/ops-track.js.map +1 -0
  50. package/dist/lib/optimize-cover-url.d.ts +10 -0
  51. package/dist/lib/optimize-cover-url.d.ts.map +1 -0
  52. package/dist/lib/optimize-cover-url.js +80 -0
  53. package/dist/lib/optimize-cover-url.js.map +1 -0
  54. package/dist/lib/page-trails.d.ts +9 -0
  55. package/dist/lib/page-trails.d.ts.map +1 -0
  56. package/dist/lib/page-trails.js +20 -0
  57. package/dist/lib/page-trails.js.map +1 -0
  58. package/dist/lib/public-origin.d.ts +32 -0
  59. package/dist/lib/public-origin.d.ts.map +1 -0
  60. package/dist/lib/public-origin.js +125 -0
  61. package/dist/lib/public-origin.js.map +1 -0
  62. package/dist/lib/server-incident.d.ts +12 -0
  63. package/dist/lib/server-incident.d.ts.map +1 -0
  64. package/dist/lib/server-incident.js +72 -0
  65. package/dist/lib/server-incident.js.map +1 -0
  66. package/dist/lib/tab-document-url.d.ts +13 -0
  67. package/dist/lib/tab-document-url.d.ts.map +1 -0
  68. package/dist/lib/tab-document-url.js +60 -0
  69. package/dist/lib/tab-document-url.js.map +1 -0
  70. package/dist/lib/utils.d.ts +20 -0
  71. package/dist/lib/utils.d.ts.map +1 -0
  72. package/dist/lib/utils.js +87 -0
  73. package/dist/lib/utils.js.map +1 -0
  74. package/dist/nav-catalog.d.ts +97 -0
  75. package/dist/nav-catalog.d.ts.map +1 -0
  76. package/dist/nav-catalog.js +282 -0
  77. package/dist/nav-catalog.js.map +1 -0
  78. package/dist/registry.d.ts +11 -0
  79. package/dist/registry.d.ts.map +1 -0
  80. package/dist/registry.js +60 -0
  81. package/dist/registry.js.map +1 -0
  82. package/dist/types.d.ts +22 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +2 -0
  85. package/dist/types.js.map +1 -0
  86. package/dist-cjs/adapters/nav-shell.js +115 -0
  87. package/dist-cjs/adapters/nav-shell.js.map +1 -0
  88. package/dist-cjs/brand.js +66 -0
  89. package/dist-cjs/brand.js.map +1 -0
  90. package/dist-cjs/core-nav.js +43 -0
  91. package/dist-cjs/core-nav.js.map +1 -0
  92. package/dist-cjs/index.js +95 -0
  93. package/dist-cjs/index.js.map +1 -0
  94. package/dist-cjs/lib/api-scopes.js +86 -0
  95. package/dist-cjs/lib/api-scopes.js.map +1 -0
  96. package/dist-cjs/lib/catalog-suspense-key.js +15 -0
  97. package/dist-cjs/lib/catalog-suspense-key.js.map +1 -0
  98. package/dist-cjs/lib/data-changed.js +236 -0
  99. package/dist-cjs/lib/data-changed.js.map +1 -0
  100. package/dist-cjs/lib/desktop-home-path.js +34 -0
  101. package/dist-cjs/lib/desktop-home-path.js.map +1 -0
  102. package/dist-cjs/lib/geo-distance.js +30 -0
  103. package/dist-cjs/lib/geo-distance.js.map +1 -0
  104. package/dist-cjs/lib/img.js +43 -0
  105. package/dist-cjs/lib/img.js.map +1 -0
  106. package/dist-cjs/lib/keepalive-eviction.js +49 -0
  107. package/dist-cjs/lib/keepalive-eviction.js.map +1 -0
  108. package/dist-cjs/lib/ops-track.js +41 -0
  109. package/dist-cjs/lib/ops-track.js.map +1 -0
  110. package/dist-cjs/lib/optimize-cover-url.js +83 -0
  111. package/dist-cjs/lib/optimize-cover-url.js.map +1 -0
  112. package/dist-cjs/lib/page-trails.js +25 -0
  113. package/dist-cjs/lib/page-trails.js.map +1 -0
  114. package/dist-cjs/lib/public-origin.js +130 -0
  115. package/dist-cjs/lib/public-origin.js.map +1 -0
  116. package/dist-cjs/lib/server-incident.js +75 -0
  117. package/dist-cjs/lib/server-incident.js.map +1 -0
  118. package/dist-cjs/lib/tab-document-url.js +65 -0
  119. package/dist-cjs/lib/tab-document-url.js.map +1 -0
  120. package/dist-cjs/lib/utils.js +97 -0
  121. package/dist-cjs/lib/utils.js.map +1 -0
  122. package/dist-cjs/nav-catalog.js +293 -0
  123. package/dist-cjs/nav-catalog.js.map +1 -0
  124. package/dist-cjs/package.json +3 -0
  125. package/dist-cjs/registry.js +64 -0
  126. package/dist-cjs/registry.js.map +1 -0
  127. package/dist-cjs/types.js +3 -0
  128. package/dist-cjs/types.js.map +1 -0
  129. package/package.json +163 -0
  130. package/ui/app-error-boundary.tsx +59 -0
  131. package/ui/data-table.tsx +121 -0
  132. package/ui/desktop/auth-window-chrome.tsx +51 -0
  133. package/ui/desktop/desktop-bridge.tsx +87 -0
  134. package/ui/desktop/external-site-slot.tsx +313 -0
  135. package/ui/desktop/external-site-surface.ts +58 -0
  136. package/ui/desktop/site-link.tsx +105 -0
  137. package/ui/desktop/window-chrome-controls.tsx +5 -0
  138. package/ui/desktop-types.ts +30 -0
  139. package/ui/faceted-filters.tsx +119 -0
  140. package/ui/global-search-host.ts +20 -0
  141. package/ui/global-search.tsx +66 -0
  142. package/ui/index.ts +178 -0
  143. package/ui/kit.ts +30 -0
  144. package/ui/layout/app-shell.tsx +58 -0
  145. package/ui/layout/desktop-update-banner.tsx +100 -0
  146. package/ui/layout/entity-header.tsx +44 -0
  147. package/ui/layout/native-os-nav.ts +104 -0
  148. package/ui/layout/nav-catalog-loader.tsx +98 -0
  149. package/ui/layout/nav-icons.ts +67 -0
  150. package/ui/layout/page-chrome.tsx +90 -0
  151. package/ui/layout/page-toolbar-context.tsx +82 -0
  152. package/ui/layout/sandbox-banner.tsx +38 -0
  153. package/ui/layout/section-view-shell.tsx +73 -0
  154. package/ui/layout/sidebar-host.ts +144 -0
  155. package/ui/layout/sidebar.tsx +1150 -0
  156. package/ui/lib/ai-screencast-hub.ts +127 -0
  157. package/ui/lib/ai-workspace-client.ts +46 -0
  158. package/ui/lib/aid.ts +22 -0
  159. package/ui/lib/desktop-host.ts +114 -0
  160. package/ui/lib/fleet-tracker-client.ts +237 -0
  161. package/ui/lib/hermes-ui.ts +114 -0
  162. package/ui/lib/n8n-ui.ts +115 -0
  163. package/ui/lib/use-creezio-resource.tsx +121 -0
  164. package/ui/lib/use-shell-ui-brand.ts +22 -0
  165. package/ui/list-toolbar.tsx +309 -0
  166. package/ui/os-pages/desktop-settings-page.tsx +158 -0
  167. package/ui/os-pages/index.ts +8 -0
  168. package/ui/page-loading/entity-page-loading.tsx +59 -0
  169. package/ui/page-loading/list-page-loading.tsx +61 -0
  170. package/ui/pagination.tsx +56 -0
  171. package/ui/primitives/avatar.tsx +33 -0
  172. package/ui/primitives/badge.tsx +36 -0
  173. package/ui/primitives/breadcrumb.tsx +93 -0
  174. package/ui/primitives/button.tsx +44 -0
  175. package/ui/primitives/card.tsx +44 -0
  176. package/ui/primitives/chart.tsx +317 -0
  177. package/ui/primitives/command.tsx +111 -0
  178. package/ui/primitives/dialog.tsx +100 -0
  179. package/ui/primitives/dropdown-menu.tsx +187 -0
  180. package/ui/primitives/input.tsx +19 -0
  181. package/ui/primitives/label.tsx +17 -0
  182. package/ui/primitives/resizable.tsx +45 -0
  183. package/ui/primitives/scroll-area.tsx +41 -0
  184. package/ui/primitives/select.tsx +73 -0
  185. package/ui/primitives/separator.tsx +26 -0
  186. package/ui/primitives/sheet.tsx +90 -0
  187. package/ui/primitives/skeleton.tsx +7 -0
  188. package/ui/primitives/sonner.tsx +22 -0
  189. package/ui/primitives/tabs.tsx +51 -0
  190. package/ui/primitives/textarea.tsx +22 -0
  191. package/ui/primitives/tooltip.tsx +31 -0
  192. package/ui/pwa/client-error-reporter.tsx +62 -0
  193. package/ui/pwa/register-sw.tsx +50 -0
  194. package/ui/range-filters.tsx +174 -0
  195. package/ui/search/global-search-config.ts +32 -0
  196. package/ui/search/global-search-provider.tsx +528 -0
  197. package/ui/search/search-history.ts +74 -0
  198. package/ui/search-input.tsx +146 -0
  199. package/ui/settings/account-settings.tsx +140 -0
  200. package/ui/settings/agent-profile-settings.tsx +184 -0
  201. package/ui/settings/api-keys-settings.tsx +349 -0
  202. package/ui/settings/desktop-background-settings.tsx +128 -0
  203. package/ui/settings/desktop-connection-settings.tsx +251 -0
  204. package/ui/settings/desktop-embed-env-panel.tsx +257 -0
  205. package/ui/settings/desktop-fleet-telemetry-settings.tsx +269 -0
  206. package/ui/settings/desktop-hermes-settings.tsx +341 -0
  207. package/ui/settings/desktop-llm-keys.tsx +192 -0
  208. package/ui/settings/desktop-n8n-settings.tsx +306 -0
  209. package/ui/settings/desktop-tunnel.tsx +264 -0
  210. package/ui/settings/desktop-update-settings.tsx +175 -0
  211. package/ui/settings/factory-reset-settings.tsx +89 -0
  212. package/ui/settings/host-only-settings.tsx +62 -0
  213. package/ui/settings/locked-config-field.tsx +30 -0
  214. package/ui/settings/ops-diagnostic-settings.tsx +242 -0
  215. package/ui/settings/search-reindex-settings.tsx +158 -0
  216. package/ui/settings/server-mode-cards.tsx +801 -0
  217. package/ui/theme/tailwind-preset.cjs +82 -0
  218. package/ui/theme/theme.css +328 -0
  219. package/ui/workspace/ai-activity-panel-host.tsx +19 -0
  220. package/ui/workspace/ai-workspace-agent-host.tsx +54 -0
  221. package/ui/workspace/ai-workspace-banner.tsx +168 -0
  222. package/ui/workspace/impersonation-banner.tsx +39 -0
  223. package/ui/workspace/keep-alive.tsx +220 -0
  224. package/ui/workspace/tab-workspace-context.tsx +1172 -0
  225. package/ui/workspace/tab-workspace-host.ts +89 -0
  226. package/ui/workspace/types.ts +500 -0
  227. package/ui/workspace/use-location-search.tsx +50 -0
  228. package/ui/workspace/window-chrome-controls.tsx +116 -0
  229. package/ui/workspace/workspace-config.ts +47 -0
  230. package/ui/workspace/workspace-root.tsx +147 -0
  231. package/ui/workspace/workspace-shell.tsx +292 -0
  232. package/ui/workspace/workspace-tab-bar.tsx +572 -0
@@ -0,0 +1,220 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ type ReactNode,
10
+ } from "react";
11
+ import { LayoutRouterContext } from "next/dist/shared/lib/app-router-context.shared-runtime";
12
+ import { cn } from "@creezio/shell-ui";
13
+ import { rankKeepAliveEvictionKeys } from "@creezio/shell-ui";
14
+ import { MAX_KEEPALIVE, isFullscreenHref } from "./types";
15
+
16
+ type CacheEntry = {
17
+ node: ReactNode;
18
+ lastActiveAt: number;
19
+ };
20
+
21
+ /**
22
+ * Href (clé de cache) de la pane keep-alive dans laquelle un composant est
23
+ * rendu. Stable pour toute la vie de la pane — contrairement à `currentHref`
24
+ * qui suit l'URL globale et change quand ON NAVIGUE DANS UN AUTRE ONGLET.
25
+ * C'est la cible correcte pour publier le titre d'une page vers son onglet.
26
+ */
27
+ const PaneHrefContext = createContext<string | null>(null);
28
+ /** true hors keep-alive ; false sur les panes inactives (évite d'écraser la toolbar). */
29
+ const PaneActiveContext = createContext(true);
30
+
31
+ export function usePaneHref(): string | null {
32
+ return useContext(PaneHrefContext);
33
+ }
34
+
35
+ export function usePaneActive(): boolean {
36
+ return useContext(PaneActiveContext);
37
+ }
38
+
39
+ /**
40
+ * Gel du router App Router par pane (pattern « FrozenRouter »).
41
+ *
42
+ * Dans App Router, `children` n'est PAS un instantané : c'est un élément
43
+ * `LayoutRouter` qui lit la route courante via `LayoutRouterContext`. Sans
44
+ * gel, chaque navigation re-rend TOUTES les panes vers la nouvelle route
45
+ * (naviguer dans l'onglet B changeait le contenu de l'onglet A).
46
+ *
47
+ * Tant que la pane correspond à l'URL réellement rendue (`live`), on laisse
48
+ * passer le contexte réel (données fraîches, router.refresh, etc.) et on le
49
+ * mémorise. Dès que la route s'en va, la pane rend le dernier contexte
50
+ * capturé : son contenu reste figé sur SA route.
51
+ */
52
+ function PaneRouterFreeze({
53
+ live,
54
+ children,
55
+ }: {
56
+ live: boolean;
57
+ children: ReactNode;
58
+ }) {
59
+ const ctx = useContext(LayoutRouterContext);
60
+ const frozenRef = useRef(ctx);
61
+ if (live) {
62
+ frozenRef.current = ctx;
63
+ }
64
+ return (
65
+ <LayoutRouterContext.Provider value={live ? ctx : frozenRef.current}>
66
+ {children}
67
+ </LayoutRouterContext.Provider>
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Pool keep-alive : panes empilées (absolute + visibility), pas de display:none.
73
+ *
74
+ * - `routeKey` : URL réellement rendue par Next (source de `children`)
75
+ * - `activeHref` : URL de l'onglet actif (switch optimiste)
76
+ * - Chaque pane rend `children` sous un LayoutRouterContext gelé sur SA route
77
+ * (voir PaneRouterFreeze) + un PaneHrefContext stable pour le titre
78
+ * - Scroll natif par pane ; charts Leaflet/Recharts gardent leurs dimensions
79
+ */
80
+ export function KeepAliveOutlet({
81
+ routeKey,
82
+ activeHref,
83
+ children,
84
+ max = MAX_KEEPALIVE,
85
+ }: {
86
+ routeKey: string;
87
+ activeHref: string;
88
+ children: ReactNode;
89
+ max?: number;
90
+ }) {
91
+ const cacheRef = useRef(new Map<string, CacheEntry>());
92
+ const paneRefs = useRef(new Map<string, HTMLDivElement | null>());
93
+ const [, bump] = useState(0);
94
+
95
+ // Sync children uniquement sous routeKey (jamais sous une URL pas encore naviguée)
96
+ const routeEntry = cacheRef.current.get(routeKey);
97
+ if (!routeEntry) {
98
+ cacheRef.current.set(routeKey, {
99
+ node: children,
100
+ lastActiveAt: Date.now(),
101
+ });
102
+ } else {
103
+ routeEntry.node = children;
104
+ routeEntry.lastActiveAt = Date.now();
105
+ }
106
+
107
+ // Affichage optimiste : si l'onglet cible a déjà une pane, la montrer tout
108
+ // de suite (sans attendre router.replace). Si la cible est froide (pas en
109
+ // cache) → placeholder, JAMAIS l'ancienne pane (bug « contenu page précédente »).
110
+ const coldTarget =
111
+ activeHref !== routeKey && !cacheRef.current.has(activeHref);
112
+ const displayKey: string | null = coldTarget
113
+ ? null
114
+ : activeHref !== routeKey && cacheRef.current.has(activeHref)
115
+ ? activeHref
116
+ : routeKey;
117
+
118
+ if (cacheRef.current.size > max) {
119
+ // Ne jamais évincer /site/* (Hermes, sites externes) ni optimiser fullscreen :
120
+ // sinon au retour d'onglet displayKey retombe sur routeKey (CRM précédent).
121
+ const lastActiveAt: Record<string, number> = {};
122
+ Array.from(cacheRef.current.entries()).forEach(([key, entry]) => {
123
+ lastActiveAt[key] = entry.lastActiveAt;
124
+ });
125
+ const ranked = rankKeepAliveEvictionKeys(
126
+ Array.from(cacheRef.current.keys()),
127
+ lastActiveAt,
128
+ { displayKey: displayKey ?? routeKey, routeKey },
129
+ );
130
+ while (cacheRef.current.size > max && ranked.length) {
131
+ const evictKey = ranked.shift()!;
132
+ cacheRef.current.delete(evictKey);
133
+ paneRefs.current.delete(evictKey);
134
+ }
135
+ }
136
+
137
+ useEffect(() => {
138
+ function onInvalidate(e: Event) {
139
+ const key = (e as CustomEvent<string>).detail;
140
+ if (!key) return;
141
+ cacheRef.current.delete(key);
142
+ paneRefs.current.delete(key);
143
+ bump((n) => n + 1);
144
+ }
145
+ window.addEventListener("creezio-keepalive-invalidate", onInvalidate);
146
+ window.addEventListener("creezio-keepalive-unfreeze", onInvalidate);
147
+ return () => {
148
+ window.removeEventListener("creezio-keepalive-invalidate", onInvalidate);
149
+ window.removeEventListener("creezio-keepalive-unfreeze", onInvalidate);
150
+ };
151
+ }, []);
152
+
153
+ return (
154
+ <div className="workspace-pane-stack relative h-full min-h-0 w-full">
155
+ {Array.from(cacheRef.current.entries()).map(([key, entry]) => {
156
+ const active = displayKey != null && key === displayKey;
157
+ const fullscreen = isFullscreenHref(key);
158
+ return (
159
+ <div
160
+ key={key}
161
+ ref={(el) => {
162
+ paneRefs.current.set(key, el);
163
+ if (el) {
164
+ if (active) el.removeAttribute("inert");
165
+ else el.setAttribute("inert", "");
166
+ }
167
+ }}
168
+ data-workspace-pane={key}
169
+ data-active={active ? "true" : "false"}
170
+ aria-hidden={!active}
171
+ className={cn(
172
+ "workspace-pane absolute inset-0 overflow-y-auto overflow-x-hidden overscroll-contain",
173
+ fullscreen
174
+ ? "overflow-hidden bg-white p-0"
175
+ : "bg-white p-4 pt-3 md:p-6 md:pt-4",
176
+ "[scrollbar-gutter:stable]",
177
+ // visibility (pas display:none) : préserve dimensions → pas de reflow Recharts/Leaflet
178
+ active
179
+ ? "z-[1] visible pointer-events-auto"
180
+ : "z-0 invisible pointer-events-none",
181
+ )}
182
+ >
183
+ <PaneHrefContext.Provider value={key}>
184
+ <PaneActiveContext.Provider value={active}>
185
+ <PaneRouterFreeze live={key === routeKey}>
186
+ {entry.node}
187
+ </PaneRouterFreeze>
188
+ </PaneActiveContext.Provider>
189
+ </PaneHrefContext.Provider>
190
+ </div>
191
+ );
192
+ })}
193
+ {coldTarget ? (
194
+ <div
195
+ data-workspace-pane-placeholder={activeHref}
196
+ data-active="true"
197
+ className="workspace-pane absolute inset-0 z-[2] flex items-center justify-center bg-white"
198
+ aria-busy="true"
199
+ aria-live="polite"
200
+ >
201
+ <div className="h-8 w-8 animate-pulse rounded-full bg-slate-200" />
202
+ </div>
203
+ ) : null}
204
+ </div>
205
+ );
206
+ }
207
+
208
+ export function invalidateKeepAlive(href: string) {
209
+ if (typeof window === "undefined") return;
210
+ window.dispatchEvent(
211
+ new CustomEvent("creezio-keepalive-invalidate", { detail: href }),
212
+ );
213
+ }
214
+
215
+ export function unfreezeKeepAlive(href: string) {
216
+ if (typeof window === "undefined") return;
217
+ window.dispatchEvent(
218
+ new CustomEvent("creezio-keepalive-unfreeze", { detail: href }),
219
+ );
220
+ }