@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,82 @@
1
+ /**
2
+ * Preset Tailwind Creezio — thème générique extrait verbatim du design
3
+ * system TempoFlow (gold TF) : accent orange (palette `sky` remappée),
4
+ * neutres `slate` réchauffés (encre #14182f), fond crème via variables CSS.
5
+ * AUCUN métier. CJS volontaire : chargé par tailwind.config (Node/jiti)
6
+ * sans étape de build.
7
+ *
8
+ * Usage marque :
9
+ * presets: [require("@creezio/shell-ui/tailwind-preset")]
10
+ */
11
+
12
+ /** @type {import('tailwindcss').Config} */
13
+ module.exports = {
14
+ content: [],
15
+ theme: {
16
+ extend: {
17
+ keyframes: {
18
+ shake: {
19
+ "0%, 100%": { transform: "translateX(0)" },
20
+ "20%, 60%": { transform: "translateX(-4px)" },
21
+ "40%, 80%": { transform: "translateX(4px)" },
22
+ },
23
+ "hub-pulse": {
24
+ "0%, 100%": { boxShadow: "0 0 0 0 rgba(16, 185, 129, 0.35)" },
25
+ "50%": { boxShadow: "0 0 0 6px rgba(16, 185, 129, 0)" },
26
+ },
27
+ "unlock-pulse": {
28
+ "0%, 100%": { transform: "scale(1)" },
29
+ "50%": { transform: "scale(1.03)" },
30
+ },
31
+ "mission-win": {
32
+ "0%, 100%": { boxShadow: "0 0 0 0 rgba(16, 185, 129, 0.2)" },
33
+ "50%": { boxShadow: "0 0 12px 2px rgba(16, 185, 129, 0.35)" },
34
+ },
35
+ },
36
+ animation: {
37
+ shake: "shake 0.4s ease-in-out",
38
+ "hub-pulse": "hub-pulse 1.5s ease-in-out infinite",
39
+ "unlock-pulse": "unlock-pulse 2s ease-in-out infinite",
40
+ "mission-win": "mission-win 2.5s ease-in-out infinite",
41
+ },
42
+ colors: {
43
+ background: "var(--background)",
44
+ foreground: "var(--foreground)",
45
+ // Accent (historiquement `sky`) : orange brand — recolore l'UI
46
+ // sans toucher aux composants.
47
+ sky: {
48
+ 50: "#fef4ec",
49
+ 100: "#fde5d2",
50
+ 200: "#fac9a5",
51
+ 300: "#f7a86d",
52
+ 400: "#f48c41",
53
+ 500: "#f0701d",
54
+ 600: "#d95f12",
55
+ 700: "#b84f10",
56
+ 800: "#93400f",
57
+ 900: "#78350f",
58
+ 950: "#451a03",
59
+ },
60
+ // Neutres : clairs réchauffés (fond crème), foncés tirés vers
61
+ // l'encre #14182f (sidebar sombre).
62
+ slate: {
63
+ 50: "#f8f7f4",
64
+ 100: "#f0efea",
65
+ 200: "#e3e1da",
66
+ 300: "#cdccc5",
67
+ 400: "#98999b",
68
+ 500: "#6b7080",
69
+ 600: "#4b5063",
70
+ 700: "#383d52",
71
+ 800: "#252940",
72
+ 900: "#14182f",
73
+ 950: "#0d101f",
74
+ },
75
+ },
76
+ fontFamily: {
77
+ serif: ["var(--font-brand-serif)", "Georgia", "serif"],
78
+ },
79
+ },
80
+ },
81
+ plugins: [],
82
+ };
@@ -0,0 +1,328 @@
1
+ /*
2
+ * Thème Creezio canonique — extrait verbatim du design system TempoFlow
3
+ * (gold TF). Fond crème #faf7f1, encre #14182f, chrome onglets « Chrome »,
4
+ * sidebar sombre, animations onboarding. AUCUN métier.
5
+ *
6
+ * Usage marque (layout racine, AVANT le globals.css marque) :
7
+ * import "@creezio/shell-ui/theme.css";
8
+ * import "./globals.css"; // ne contient que les @tailwind + overrides
9
+ *
10
+ * Les directives @tailwind restent dans le globals.css de la marque (sinon
11
+ * double expansion des utilities). À utiliser avec le preset Tailwind kit :
12
+ * presets: [require("@creezio/shell-ui/tailwind-preset")]
13
+ */
14
+
15
+ :root {
16
+ --background: #faf7f1;
17
+ --foreground: #14182f;
18
+ }
19
+
20
+ body {
21
+ color: var(--foreground);
22
+ background: var(--background);
23
+ font-family:
24
+ Inter,
25
+ ui-sans-serif,
26
+ system-ui,
27
+ -apple-system,
28
+ BlinkMacSystemFont,
29
+ "Segoe UI",
30
+ sans-serif;
31
+ }
32
+
33
+ /* CSS pur (fichier kit traité hors contexte Tailwind marque) :
34
+ équivalent de `@layer base { * { @apply border-slate-200 } }` avec la
35
+ palette slate réchauffée du preset kit. */
36
+ * {
37
+ border-color: #e3e1da;
38
+ }
39
+
40
+ html {
41
+ /* Évite le jump de largeur quand une scrollbar apparaît / disparaît */
42
+ scrollbar-gutter: stable;
43
+ }
44
+
45
+ /* ------------------------------------------------------------------ */
46
+ /* Onboarding — micro-étapes façon Typeform (transitions verticales) */
47
+ /* ------------------------------------------------------------------ */
48
+
49
+ /*
50
+ * Fond crème plein + wash blanc adouci (convention “atmospheric wash” /
51
+ * eased multi-stop — Linear / Stripe / Apple) :
52
+ * pas de carte blanche bordée (sinon trait net crème↔blanc),
53
+ * plusieurs stops pour éviter la bande dure d’un dégradé 2 couleurs.
54
+ */
55
+ .onb-stage {
56
+ background-color: #faf7f1;
57
+ background-image:
58
+ radial-gradient(
59
+ 110% 75% at 50% -5%,
60
+ #ffffff 0%,
61
+ rgba(255, 255, 255, 0.72) 28%,
62
+ rgba(253, 250, 246, 0.35) 52%,
63
+ rgba(250, 247, 241, 0) 72%
64
+ ),
65
+ linear-gradient(
66
+ 180deg,
67
+ #fdfbf8 0%,
68
+ #faf7f1 42%,
69
+ #faf7f1 100%
70
+ );
71
+ }
72
+
73
+ .onb-stepper-fade {
74
+ border-bottom: none;
75
+ background: transparent;
76
+ box-shadow: none;
77
+ }
78
+
79
+ /* Zones scrollables onboarding : scroll possible, scrollbar invisible
80
+ (évite le flash pendant translateY des animations d'entrée). */
81
+ .onb-scroll {
82
+ overflow-x: hidden;
83
+ overflow-y: auto;
84
+ scrollbar-width: none;
85
+ -ms-overflow-style: none;
86
+ }
87
+ .onb-scroll::-webkit-scrollbar {
88
+ display: none;
89
+ width: 0;
90
+ height: 0;
91
+ }
92
+
93
+ @keyframes onb-in-up {
94
+ from {
95
+ opacity: 0;
96
+ transform: translateY(28px);
97
+ }
98
+ to {
99
+ opacity: 1;
100
+ transform: none;
101
+ }
102
+ }
103
+
104
+ @keyframes onb-in-down {
105
+ from {
106
+ opacity: 0;
107
+ transform: translateY(-28px);
108
+ }
109
+ to {
110
+ opacity: 1;
111
+ transform: none;
112
+ }
113
+ }
114
+
115
+ .onb-anim-fwd {
116
+ animation: onb-in-up 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
117
+ }
118
+
119
+ .onb-anim-back {
120
+ animation: onb-in-down 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
121
+ }
122
+
123
+ /* Interstitiel entre deux étapes : titre qui apparaît, tient, puis s'efface */
124
+ @keyframes onb-interstitial {
125
+ 0% {
126
+ opacity: 0;
127
+ transform: translateY(28px);
128
+ }
129
+ 14% {
130
+ opacity: 1;
131
+ transform: none;
132
+ }
133
+ 82% {
134
+ opacity: 1;
135
+ transform: none;
136
+ }
137
+ 100% {
138
+ opacity: 0;
139
+ transform: translateY(-18px);
140
+ }
141
+ }
142
+
143
+ .onb-interstitial {
144
+ animation: onb-interstitial 2.1s cubic-bezier(0.22, 1, 0.36, 1) both;
145
+ }
146
+
147
+ @keyframes onb-line-grow {
148
+ from {
149
+ transform: scaleX(0);
150
+ }
151
+ to {
152
+ transform: scaleX(1);
153
+ }
154
+ }
155
+
156
+ .onb-interstitial-line {
157
+ transform-origin: left center;
158
+ animation: onb-line-grow 1.7s cubic-bezier(0.22, 1, 0.36, 1) 0.2s both;
159
+ }
160
+
161
+ /* Apparition en cascade des éléments d'une micro-étape */
162
+ .onb-stagger > * {
163
+ opacity: 0;
164
+ animation: onb-in-up 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
165
+ }
166
+ .onb-stagger > *:nth-child(1) { animation-delay: 0.04s; }
167
+ .onb-stagger > *:nth-child(2) { animation-delay: 0.09s; }
168
+ .onb-stagger > *:nth-child(3) { animation-delay: 0.14s; }
169
+ .onb-stagger > *:nth-child(4) { animation-delay: 0.19s; }
170
+ .onb-stagger > *:nth-child(5) { animation-delay: 0.24s; }
171
+ .onb-stagger > *:nth-child(6) { animation-delay: 0.29s; }
172
+ .onb-stagger > *:nth-child(7) { animation-delay: 0.34s; }
173
+ .onb-stagger > *:nth-child(8) { animation-delay: 0.39s; }
174
+ .onb-stagger > *:nth-child(9) { animation-delay: 0.44s; }
175
+ .onb-stagger > *:nth-child(10) { animation-delay: 0.49s; }
176
+
177
+ /* Panes workspace : aucun transition involontaire sur le contenu au switch */
178
+ .workspace-pane-stack,
179
+ .workspace-pane {
180
+ transition: none !important;
181
+ }
182
+
183
+ /* ------------------------------------------------------------------ */
184
+ /* Onglets workspace — rendu Chrome (courbes inversées, séparateurs) */
185
+ /* ------------------------------------------------------------------ */
186
+
187
+ /* Courbes inversées en bas de l'onglet actif (flare Chrome).
188
+ Deux carrés 8×8 posés de part et d'autre du pied de l'onglet ;
189
+ un dégradé radial centré sur le coin haut extérieur découpe l'arc
190
+ concave, le reste est blanc et fusionne avec le fond de page. */
191
+ .tf-tab-bg::before,
192
+ .tf-tab-bg::after {
193
+ content: "";
194
+ position: absolute;
195
+ bottom: 0;
196
+ width: 8px;
197
+ height: 8px;
198
+ }
199
+
200
+ .tf-tab-bg::before {
201
+ left: -8px;
202
+ background: radial-gradient(
203
+ circle 8px at 0 0,
204
+ transparent 0,
205
+ transparent 7px,
206
+ #fff 7.75px
207
+ );
208
+ }
209
+
210
+ .tf-tab-bg::after {
211
+ right: -8px;
212
+ background: radial-gradient(
213
+ circle 8px at 100% 0,
214
+ transparent 0,
215
+ transparent 7px,
216
+ #fff 7.75px
217
+ );
218
+ }
219
+
220
+ /* Séparateurs verticaux : masqués sur le premier onglet, autour de
221
+ l'onglet actif et autour de l'onglet survolé — comme Chrome. */
222
+ .tf-tab:first-child > .tf-sep,
223
+ .tf-tab.is-active > .tf-sep,
224
+ .tf-tab.is-active + .tf-tab > .tf-sep,
225
+ .tf-tab:hover > .tf-sep,
226
+ .tf-tab:hover + .tf-tab > .tf-sep {
227
+ opacity: 0;
228
+ }
229
+
230
+ /* Apparition du contenu d'un nouvel onglet */
231
+ @keyframes tf-tab-enter {
232
+ from {
233
+ opacity: 0;
234
+ transform: translateX(-6px);
235
+ }
236
+ to {
237
+ opacity: 1;
238
+ transform: translateX(0);
239
+ }
240
+ }
241
+
242
+ .tf-tab-enter {
243
+ animation: tf-tab-enter 180ms ease-out;
244
+ }
245
+
246
+ /* Sidebar nav — thin dark scrollbar (Firefox + WebKit) */
247
+ .sidebar-scroll {
248
+ scrollbar-width: thin;
249
+ scrollbar-color: #4b5063 #0d101f;
250
+ }
251
+
252
+ .sidebar-scroll::-webkit-scrollbar {
253
+ width: 6px;
254
+ }
255
+
256
+ .sidebar-scroll::-webkit-scrollbar-track {
257
+ background: #0d101f;
258
+ border-radius: 9999px;
259
+ }
260
+
261
+ .sidebar-scroll::-webkit-scrollbar-thumb {
262
+ background: #4b5063;
263
+ border-radius: 9999px;
264
+ }
265
+
266
+ .sidebar-scroll::-webkit-scrollbar-thumb:hover {
267
+ background: #6b7080;
268
+ }
269
+
270
+ /* Electron frameless (Windows) : bandeau titre / onglets déplaçable */
271
+ .tempoflow-titlebar-drag {
272
+ -webkit-app-region: drag;
273
+ app-region: drag;
274
+ }
275
+
276
+ .tempoflow-titlebar-no-drag {
277
+ -webkit-app-region: no-drag;
278
+ app-region: no-drag;
279
+ }
280
+
281
+ /* Contrôles fenêtre Windows : curseur default (pas pointer) — hover = fond */
282
+ .tempoflow-titlebar-no-drag button {
283
+ cursor: default;
284
+ }
285
+
286
+ /* ------------------------------------------------------------------ */
287
+ /* Palette de recherche globale (Ctrl+K) */
288
+ /* ------------------------------------------------------------------ */
289
+
290
+ /*
291
+ * Styles possédés par le composant : la palette ne doit JAMAIS dépendre des
292
+ * règles génériques de modales de la marque (un `[role="dialog"]` global qui
293
+ * borne largeur/hauteur/overflow a détruit son rendu — incident winhub PR #8,
294
+ * 2026-08-10). Classe doublée + attribut = spécificité (0,3,0) : prime sur
295
+ * tout sélecteur générique à une classe/un attribut, quel que soit l ordre
296
+ * des feuilles de styles. Les classes Tailwind du composant restent pour le
297
+ * visuel (fond, ombre, bord) ; la géométrie vit ICI et nulle part ailleurs.
298
+ */
299
+ .creezio-search-palette.creezio-search-palette[role="dialog"] {
300
+ position: fixed;
301
+ z-index: 50;
302
+ top: 0;
303
+ right: 0;
304
+ bottom: 0;
305
+ left: 0;
306
+ display: flex;
307
+ flex-direction: column;
308
+ width: 100%;
309
+ height: 100dvh;
310
+ max-height: none;
311
+ overflow-x: hidden;
312
+ overflow-y: hidden;
313
+ overscroll-behavior: auto;
314
+ }
315
+
316
+ @media (min-width: 768px) {
317
+ .creezio-search-palette.creezio-search-palette[role="dialog"] {
318
+ top: 8%;
319
+ right: auto;
320
+ bottom: 0;
321
+ left: 50%;
322
+ width: min(920px, calc(100vw - 2rem));
323
+ height: auto;
324
+ max-height: min(680px, 85vh);
325
+ transform: translateX(-50%);
326
+ border-radius: 0.75rem;
327
+ }
328
+ }
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ import type { ComponentType } from "react";
4
+
5
+ type AnyProps = Record<string, unknown>;
6
+
7
+ let Panel: ComponentType<AnyProps> | null = null;
8
+
9
+ /** O9p : brancher `AiActivityPanel` depuis `@creezio/tasks/ui`. */
10
+ export function configureAiActivityPanel(
11
+ next: ComponentType<AnyProps> | null,
12
+ ): void {
13
+ Panel = next;
14
+ }
15
+
16
+ export function AiActivityPanelHost(props: AnyProps) {
17
+ if (!Panel) return null;
18
+ return <Panel {...props} />;
19
+ }
@@ -0,0 +1,54 @@
1
+ "use client";
2
+
3
+ import { getShellDesktopApi, getShellUiBrand } from "@creezio/shell-ui";
4
+
5
+ /**
6
+ * Bus agent dans chaque vue CRM (surtout partitions IA).
7
+ * Reçoit navigate / ui-action depuis le main Electron et exécute le
8
+ * même pipeline fake-cursor que le chat (UiDriver), puis ACK IPC.
9
+ */
10
+
11
+ import { useEffect } from "react";
12
+ import { runUiAction, runUiNavigate } from "@creezio/assistant/ui";
13
+ import { useTabWorkspace } from "./tab-workspace-host";
14
+
15
+ export function AiWorkspaceAgentHost() {
16
+ const { navigate } = useTabWorkspace();
17
+
18
+ useEffect(() => {
19
+ const api = typeof window !== "undefined" ? getShellDesktopApi() : undefined;
20
+ if (!api?.onAiWorkspaceNavigate && !api?.onAiWorkspaceUiAction) return;
21
+
22
+ const offNav = api.onAiWorkspaceNavigate?.((ev: { href: string; actionId?: string }) => {
23
+ void (async () => {
24
+ const result = await runUiNavigate(ev.href, (href) => {
25
+ navigate(href, { newTab: false });
26
+ });
27
+ api.ackAiWorkspaceAction?.(ev.actionId, result);
28
+ })();
29
+ });
30
+
31
+ const offUi = api.onAiWorkspaceUiAction?.((ev: { type: string; params: Record<string, unknown>; actionId?: string }) => {
32
+ void (async () => {
33
+ let result: Record<string, unknown>;
34
+ if (ev.type === "navigate") {
35
+ const href =
36
+ (typeof ev.params.href === "string" && ev.params.href) ||
37
+ (typeof ev.params.path === "string" && ev.params.path) ||
38
+ "";
39
+ result = await runUiNavigate(href, (h) => navigate(h, { newTab: false }));
40
+ } else {
41
+ result = await runUiAction({ type: ev.type, params: ev.params || {} });
42
+ }
43
+ api.ackAiWorkspaceAction?.(ev.actionId, result);
44
+ })();
45
+ });
46
+
47
+ return () => {
48
+ offNav?.();
49
+ offUi?.();
50
+ };
51
+ }, [navigate]);
52
+
53
+ return null;
54
+ }
@@ -0,0 +1,168 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Bandeau visible uniquement dans une WebContentsView espace IA.
5
+ * « Retour au workspace principal » = showOwner (pas stop-impersonate).
6
+ *
7
+ * P4 (missions) : abonné au flux SSE d'activité de l'IA
8
+ * (/api/v1/tasks/activity/:userId/stream) — quand un run démarre, une
9
+ * notification visuelle apparaît dans le bandeau (pulse + titre de la
10
+ * mission) ; elle passe en « terminé » à la fin du run.
11
+ */
12
+
13
+ import { useEffect, useState } from "react";
14
+ import { Bot, ListTodo, Loader2, Undo2 } from "lucide-react";
15
+ import Link from "next/link";
16
+ import { Button } from "../primitives/button";
17
+ import { AiActivityPanelHost } from "./ai-activity-panel-host";
18
+ import { cn, getShellDesktopApi } from "@creezio/shell-ui";
19
+ import { aidProps } from "../lib/aid";
20
+
21
+ type Identity = {
22
+ userId: string;
23
+ label: string;
24
+ active: boolean;
25
+ };
26
+
27
+ type RunNotice = {
28
+ runId: string;
29
+ status: string;
30
+ taskTitle: string | null;
31
+ };
32
+
33
+ const RUNNING_STATUSES = new Set(["queued", "running", "hitl_waiting"]);
34
+
35
+ export function AiWorkspaceBanner() {
36
+ const [identity, setIdentity] = useState<Identity | null>(null);
37
+ const [notice, setNotice] = useState<RunNotice | null>(null);
38
+
39
+ useEffect(() => {
40
+ const api = typeof window !== "undefined" ? getShellDesktopApi() : undefined;
41
+ if (!api?.getAiWorkspaceIdentity) return;
42
+
43
+ let cancelled = false;
44
+ void api.getAiWorkspaceIdentity().then((id: { userId?: string; label?: string; active?: boolean } | null) => {
45
+ if (cancelled) return;
46
+ if (id?.userId) {
47
+ setIdentity({
48
+ userId: id.userId,
49
+ label: id.label || id.userId,
50
+ active: Boolean(id.active),
51
+ });
52
+ return;
53
+ }
54
+ // Pas d’espace IA — ignorer un éventuel sessionStorage fantôme.
55
+ setIdentity(null);
56
+ });
57
+
58
+ return () => {
59
+ cancelled = true;
60
+ };
61
+ }, []);
62
+
63
+ // Notification de mission : flux SSE d'activité existant (event `run`).
64
+ useEffect(() => {
65
+ if (!identity?.userId) return;
66
+ const es = new EventSource(
67
+ `/api/v1/tasks/activity/${encodeURIComponent(identity.userId)}/stream`,
68
+ );
69
+ const onRun = (ev: MessageEvent) => {
70
+ try {
71
+ const data = JSON.parse(String(ev.data)) as {
72
+ run?: { id?: string; status?: string } | null;
73
+ task?: { title?: string } | null;
74
+ };
75
+ if (!data.run?.id) return;
76
+ setNotice({
77
+ runId: data.run.id,
78
+ status: String(data.run.status || ""),
79
+ taskTitle: data.task?.title || null,
80
+ });
81
+ } catch {
82
+ /* frame illisible — ignorer */
83
+ }
84
+ };
85
+ es.addEventListener("run", onRun);
86
+ es.onerror = () => {
87
+ /* reconnexion auto EventSource */
88
+ };
89
+ return () => {
90
+ es.removeEventListener("run", onRun);
91
+ es.close();
92
+ };
93
+ }, [identity?.userId]);
94
+
95
+ if (!identity?.userId) return null;
96
+
97
+ const running = notice ? RUNNING_STATUSES.has(notice.status) : false;
98
+
99
+ return (
100
+ <div className="border-b border-sky-300 bg-sky-50 text-sky-950">
101
+ <div className="flex items-center justify-between gap-3 px-3 py-2 text-sm">
102
+ <div className="flex min-w-0 items-center gap-2">
103
+ <Bot className="h-4 w-4 shrink-0" />
104
+ <span className="truncate">
105
+ Espace collaborateur IA <strong>{identity.label}</strong>
106
+ <span className="ml-1 opacity-70">
107
+ — vue workspace réelle (fake-cursor live)
108
+ </span>
109
+ </span>
110
+ </div>
111
+ <div className="flex shrink-0 items-center gap-1.5">
112
+ <Button
113
+ type="button"
114
+ size="sm"
115
+ variant="ghost"
116
+ className="shrink-0"
117
+ asChild
118
+ >
119
+ <Link
120
+ href={`/taches?assignee=${encodeURIComponent(identity.userId)}`}
121
+ {...aidProps("ai-banner-missions")}
122
+ >
123
+ <ListTodo className="mr-1.5 h-3.5 w-3.5" />
124
+ Mes missions
125
+ </Link>
126
+ </Button>
127
+ <Button
128
+ type="button"
129
+ size="sm"
130
+ variant="outline"
131
+ className="shrink-0 border-sky-400 bg-white"
132
+ onClick={() => {
133
+ void getShellDesktopApi()?.showOwnerWorkspace?.();
134
+ }}
135
+ >
136
+ <Undo2 className="mr-1.5 h-3.5 w-3.5" />
137
+ Retour au workspace principal
138
+ </Button>
139
+ </div>
140
+ </div>
141
+ {notice ? (
142
+ <div
143
+ {...aidProps("ai-banner-run-notice")}
144
+ data-creezio-run-status={notice.status}
145
+ className={cn(
146
+ "flex items-center gap-2 border-t px-3 py-1.5 text-xs",
147
+ running
148
+ ? "animate-pulse border-violet-200 bg-violet-50 text-violet-900"
149
+ : "border-sky-200 bg-white/70 text-slate-600",
150
+ )}
151
+ >
152
+ {running ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : null}
153
+ {running
154
+ ? `Mission en cours${notice.taskTitle ? ` : ${notice.taskTitle}` : ""}`
155
+ : `Dernière mission ${notice.status === "succeeded" ? "terminée" : notice.status}${notice.taskTitle ? ` : ${notice.taskTitle}` : ""}`}
156
+ </div>
157
+ ) : null}
158
+ <div className="border-t border-sky-200 bg-white/80 px-3 py-2">
159
+ <AiActivityPanelHost
160
+ userId={identity.userId}
161
+ compact
162
+ title={`Logs — ${identity.label}`}
163
+ className="border-sky-200"
164
+ />
165
+ </div>
166
+ </div>
167
+ );
168
+ }
@@ -0,0 +1,39 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Bandeau impersonation « Voir comme » — module natif kit.
5
+ * Rendu par WorkspaceRoot au-dessus du slot `banners` : visible dès que la
6
+ * session est impersonifiée (`me.impersonating`), quel que soit le câblage
7
+ * marque. « Revenir à mon compte » = stopImpersonate (redirect
8
+ * stopImpersonateRedirect du SessionProvider, défaut /collaborateurs).
9
+ * Le nom produit vient de getShellUiBrand().productName (CreezioUiBoot) —
10
+ * aucun libellé marque en dur ici.
11
+ */
12
+
13
+ import { Undo2 } from "lucide-react";
14
+ import { useSession } from "@creezio/auth/ui";
15
+ import { getShellUiBrand } from "@creezio/shell-ui";
16
+ import { Button } from "../primitives/button";
17
+ import { aidProps } from "../lib/aid";
18
+
19
+ export function ImpersonationBanner() {
20
+ const { me, stopImpersonate } = useSession();
21
+ if (!me?.impersonating) return null;
22
+ const productName = getShellUiBrand().productName;
23
+ return (
24
+ <div className="flex items-center justify-between gap-3 border-b border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-950">
25
+ <span className="min-w-0 truncate">
26
+ Vous voyez {productName} comme <strong>{me.user}</strong>
27
+ </span>
28
+ <Button
29
+ size="sm"
30
+ variant="outline"
31
+ className="shrink-0"
32
+ onClick={() => void stopImpersonate()}
33
+ {...aidProps("impersonation-stop")}
34
+ >
35
+ <Undo2 className="mr-1.5 h-3.5 w-3.5" /> Revenir à mon compte
36
+ </Button>
37
+ </div>
38
+ );
39
+ }