@open-mercato/ui 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3045.b4b3320cc2

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 (148) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +2 -1
  3. package/__integration__/TC-AI-UI-003-aichat-registry.spec.tsx +204 -0
  4. package/dist/ai/AiAssistantLauncher.js +596 -0
  5. package/dist/ai/AiAssistantLauncher.js.map +7 -0
  6. package/dist/ai/AiChat.js +1092 -0
  7. package/dist/ai/AiChat.js.map +7 -0
  8. package/dist/ai/AiChatSessions.js +297 -0
  9. package/dist/ai/AiChatSessions.js.map +7 -0
  10. package/dist/ai/AiDock.js +347 -0
  11. package/dist/ai/AiDock.js.map +7 -0
  12. package/dist/ai/AiMessageContent.js +369 -0
  13. package/dist/ai/AiMessageContent.js.map +7 -0
  14. package/dist/ai/ChatPaneTabs.js +251 -0
  15. package/dist/ai/ChatPaneTabs.js.map +7 -0
  16. package/dist/ai/index.js +115 -0
  17. package/dist/ai/index.js.map +7 -0
  18. package/dist/ai/parts/ConfirmationCard.js +211 -0
  19. package/dist/ai/parts/ConfirmationCard.js.map +7 -0
  20. package/dist/ai/parts/FieldDiffCard.js +119 -0
  21. package/dist/ai/parts/FieldDiffCard.js.map +7 -0
  22. package/dist/ai/parts/MutationPreviewCard.js +224 -0
  23. package/dist/ai/parts/MutationPreviewCard.js.map +7 -0
  24. package/dist/ai/parts/MutationResultCard.js +240 -0
  25. package/dist/ai/parts/MutationResultCard.js.map +7 -0
  26. package/dist/ai/parts/approval-cards-map.js +15 -0
  27. package/dist/ai/parts/approval-cards-map.js.map +7 -0
  28. package/dist/ai/parts/index.js +24 -0
  29. package/dist/ai/parts/index.js.map +7 -0
  30. package/dist/ai/parts/pending-action-api.js +60 -0
  31. package/dist/ai/parts/pending-action-api.js.map +7 -0
  32. package/dist/ai/parts/types.js +1 -0
  33. package/dist/ai/parts/types.js.map +7 -0
  34. package/dist/ai/parts/useAiPendingActionPolling.js +126 -0
  35. package/dist/ai/parts/useAiPendingActionPolling.js.map +7 -0
  36. package/dist/ai/records/ActivityCard.js +83 -0
  37. package/dist/ai/records/ActivityCard.js.map +7 -0
  38. package/dist/ai/records/CompanyCard.js +81 -0
  39. package/dist/ai/records/CompanyCard.js.map +7 -0
  40. package/dist/ai/records/DealCard.js +76 -0
  41. package/dist/ai/records/DealCard.js.map +7 -0
  42. package/dist/ai/records/PersonCard.js +68 -0
  43. package/dist/ai/records/PersonCard.js.map +7 -0
  44. package/dist/ai/records/ProductCard.js +68 -0
  45. package/dist/ai/records/ProductCard.js.map +7 -0
  46. package/dist/ai/records/RecordCard.js +29 -0
  47. package/dist/ai/records/RecordCard.js.map +7 -0
  48. package/dist/ai/records/RecordCardShell.js +103 -0
  49. package/dist/ai/records/RecordCardShell.js.map +7 -0
  50. package/dist/ai/records/index.js +31 -0
  51. package/dist/ai/records/index.js.map +7 -0
  52. package/dist/ai/records/registry.js +51 -0
  53. package/dist/ai/records/registry.js.map +7 -0
  54. package/dist/ai/records/types.js +1 -0
  55. package/dist/ai/records/types.js.map +7 -0
  56. package/dist/ai/ui-part-registry.js +112 -0
  57. package/dist/ai/ui-part-registry.js.map +7 -0
  58. package/dist/ai/ui-part-slots.js +14 -0
  59. package/dist/ai/ui-part-slots.js.map +7 -0
  60. package/dist/ai/ui-parts/pending-phase3-placeholder.js +35 -0
  61. package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +7 -0
  62. package/dist/ai/upload-adapter.js +256 -0
  63. package/dist/ai/upload-adapter.js.map +7 -0
  64. package/dist/ai/useAiChat.js +549 -0
  65. package/dist/ai/useAiChat.js.map +7 -0
  66. package/dist/ai/useAiChatUpload.js +127 -0
  67. package/dist/ai/useAiChatUpload.js.map +7 -0
  68. package/dist/ai/useAiShortcuts.js +43 -0
  69. package/dist/ai/useAiShortcuts.js.map +7 -0
  70. package/dist/backend/AppShell.js +8 -4
  71. package/dist/backend/AppShell.js.map +2 -2
  72. package/dist/backend/BackendChromeProvider.js +2 -0
  73. package/dist/backend/BackendChromeProvider.js.map +2 -2
  74. package/dist/backend/DataTable.js +19 -2
  75. package/dist/backend/DataTable.js.map +2 -2
  76. package/dist/backend/FilterBar.js +19 -15
  77. package/dist/backend/FilterBar.js.map +2 -2
  78. package/dist/backend/dashboard/DashboardScreen.js +31 -3
  79. package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
  80. package/dist/backend/injection/spotIds.js +6 -0
  81. package/dist/backend/injection/spotIds.js.map +2 -2
  82. package/dist/backend/notifications/useNotificationEffect.js +38 -2
  83. package/dist/backend/notifications/useNotificationEffect.js.map +2 -2
  84. package/dist/index.js +1 -0
  85. package/dist/index.js.map +2 -2
  86. package/jest.config.cjs +7 -1
  87. package/jest.markdown-mock.tsx +7 -0
  88. package/package.json +10 -4
  89. package/src/ai/AiAssistantLauncher.tsx +805 -0
  90. package/src/ai/AiChat.tsx +1483 -0
  91. package/src/ai/AiChatSessions.tsx +429 -0
  92. package/src/ai/AiDock.tsx +505 -0
  93. package/src/ai/AiMessageContent.tsx +515 -0
  94. package/src/ai/ChatPaneTabs.tsx +310 -0
  95. package/src/ai/__tests__/AiChat.conversation.test.tsx +160 -0
  96. package/src/ai/__tests__/AiChat.debug.test.tsx +152 -0
  97. package/src/ai/__tests__/AiChat.registry.test.tsx +213 -0
  98. package/src/ai/__tests__/AiChat.test.tsx +257 -0
  99. package/src/ai/__tests__/AiDock.test.tsx +124 -0
  100. package/src/ai/__tests__/AiMessageContent.test.ts +111 -0
  101. package/src/ai/__tests__/ui-part-registry.test.ts +199 -0
  102. package/src/ai/__tests__/ui-part-slots.test.ts +43 -0
  103. package/src/ai/__tests__/upload-adapter.test.ts +213 -0
  104. package/src/ai/__tests__/useAiChatUpload.test.tsx +163 -0
  105. package/src/ai/__tests__/useAiShortcuts.test.tsx +100 -0
  106. package/src/ai/index.ts +125 -0
  107. package/src/ai/parts/ConfirmationCard.tsx +310 -0
  108. package/src/ai/parts/FieldDiffCard.tsx +173 -0
  109. package/src/ai/parts/MutationPreviewCard.tsx +302 -0
  110. package/src/ai/parts/MutationResultCard.tsx +360 -0
  111. package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +169 -0
  112. package/src/ai/parts/__tests__/FieldDiffCard.test.tsx +74 -0
  113. package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +177 -0
  114. package/src/ai/parts/__tests__/MutationResultCard.test.tsx +127 -0
  115. package/src/ai/parts/__tests__/useAiPendingActionPolling.test.tsx +151 -0
  116. package/src/ai/parts/approval-cards-map.ts +24 -0
  117. package/src/ai/parts/index.ts +27 -0
  118. package/src/ai/parts/pending-action-api.ts +123 -0
  119. package/src/ai/parts/types.ts +84 -0
  120. package/src/ai/parts/useAiPendingActionPolling.ts +210 -0
  121. package/src/ai/records/ActivityCard.tsx +102 -0
  122. package/src/ai/records/CompanyCard.tsx +89 -0
  123. package/src/ai/records/DealCard.tsx +85 -0
  124. package/src/ai/records/PersonCard.tsx +77 -0
  125. package/src/ai/records/ProductCard.tsx +83 -0
  126. package/src/ai/records/RecordCard.tsx +37 -0
  127. package/src/ai/records/RecordCardShell.tsx +169 -0
  128. package/src/ai/records/index.ts +30 -0
  129. package/src/ai/records/registry.tsx +80 -0
  130. package/src/ai/records/types.ts +90 -0
  131. package/src/ai/ui-part-registry.ts +233 -0
  132. package/src/ai/ui-part-slots.ts +32 -0
  133. package/src/ai/ui-parts/pending-phase3-placeholder.tsx +50 -0
  134. package/src/ai/upload-adapter.ts +421 -0
  135. package/src/ai/useAiChat.ts +865 -0
  136. package/src/ai/useAiChatUpload.ts +180 -0
  137. package/src/ai/useAiShortcuts.ts +79 -0
  138. package/src/backend/AppShell.tsx +12 -5
  139. package/src/backend/BackendChromeProvider.tsx +2 -0
  140. package/src/backend/DataTable.tsx +20 -1
  141. package/src/backend/FilterBar.tsx +26 -13
  142. package/src/backend/__tests__/BackendChromeProvider.test.tsx +45 -0
  143. package/src/backend/dashboard/DashboardScreen.tsx +38 -3
  144. package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +24 -1
  145. package/src/backend/injection/spotIds.ts +6 -0
  146. package/src/backend/notifications/__tests__/useNotificationEffect.test.tsx +77 -0
  147. package/src/backend/notifications/useNotificationEffect.ts +47 -2
  148. package/src/index.ts +1 -0
@@ -0,0 +1,347 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Maximize2, Minimize2, X } from "lucide-react";
5
+ import { ChatPaneTabs } from "./ChatPaneTabs.js";
6
+ import { useAiChatSessions } from "./AiChatSessions.js";
7
+ import { IconButton } from "../primitives/icon-button.js";
8
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
9
+ import { cn } from "@open-mercato/shared/lib/utils";
10
+ const LazyAiChat = React.lazy(async () => {
11
+ const mod = await import("./AiChat.js");
12
+ return { default: mod.AiChat };
13
+ });
14
+ const STORAGE_KEY = "om-ai-dock-v1";
15
+ const MIN_WIDTH = 320;
16
+ const MAX_WIDTH = 960;
17
+ const DEFAULT_WIDTH = 420;
18
+ const COLLAPSED_WIDTH = 48;
19
+ const AiDockContext = React.createContext(null);
20
+ function isRecord(value) {
21
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
22
+ }
23
+ function readOptionalString(value) {
24
+ return typeof value === "string" && value.length > 0 ? value : void 0;
25
+ }
26
+ function readWidth(value) {
27
+ return Math.min(
28
+ MAX_WIDTH,
29
+ Math.max(MIN_WIDTH, typeof value === "number" ? value : DEFAULT_WIDTH)
30
+ );
31
+ }
32
+ function readPersistedSuggestions(value) {
33
+ if (!Array.isArray(value)) return void 0;
34
+ const suggestions = value.filter((entry) => isRecord(entry)).map((entry) => {
35
+ const label = readOptionalString(entry.label);
36
+ const prompt = readOptionalString(entry.prompt);
37
+ if (!label || !prompt) return null;
38
+ return { label, prompt };
39
+ }).filter((entry) => entry !== null);
40
+ return suggestions.length > 0 ? suggestions : void 0;
41
+ }
42
+ function readPersistedContextItems(value) {
43
+ if (!Array.isArray(value)) return void 0;
44
+ const contextItems = value.filter((entry) => isRecord(entry)).map((entry) => {
45
+ const label = readOptionalString(entry.label);
46
+ if (!label) return null;
47
+ const detail = readOptionalString(entry.detail);
48
+ return detail ? { label, detail } : { label };
49
+ }).filter((entry) => entry !== null);
50
+ return contextItems.length > 0 ? contextItems : void 0;
51
+ }
52
+ function readPersistedAssistant(value) {
53
+ if (!isRecord(value)) return null;
54
+ const agent = readOptionalString(value.agent);
55
+ const label = readOptionalString(value.label);
56
+ if (!agent || !label) return null;
57
+ return {
58
+ agent,
59
+ label,
60
+ description: readOptionalString(value.description),
61
+ pageContext: isRecord(value.pageContext) ? value.pageContext : void 0,
62
+ placeholder: readOptionalString(value.placeholder),
63
+ welcomeTitle: readOptionalString(value.welcomeTitle),
64
+ welcomeDescription: readOptionalString(value.welcomeDescription),
65
+ suggestions: readPersistedSuggestions(value.suggestions),
66
+ contextItems: readPersistedContextItems(value.contextItems)
67
+ };
68
+ }
69
+ function serializeAssistant(assistant) {
70
+ if (!assistant) return null;
71
+ return {
72
+ agent: assistant.agent,
73
+ label: assistant.label,
74
+ description: assistant.description,
75
+ pageContext: assistant.pageContext,
76
+ placeholder: assistant.placeholder,
77
+ welcomeTitle: assistant.welcomeTitle,
78
+ welcomeDescription: assistant.welcomeDescription,
79
+ suggestions: assistant.suggestions?.map((suggestion) => ({
80
+ label: suggestion.label,
81
+ prompt: suggestion.prompt
82
+ })),
83
+ contextItems: assistant.contextItems?.map((item) => ({
84
+ label: item.label,
85
+ detail: item.detail
86
+ }))
87
+ };
88
+ }
89
+ function readPersisted() {
90
+ if (typeof window === "undefined") return { assistant: null, width: DEFAULT_WIDTH, collapsed: false };
91
+ try {
92
+ const raw = window.localStorage.getItem(STORAGE_KEY);
93
+ if (!raw) return { assistant: null, width: DEFAULT_WIDTH, collapsed: false };
94
+ const parsed = JSON.parse(raw);
95
+ return {
96
+ assistant: readPersistedAssistant(parsed?.assistant),
97
+ width: readWidth(parsed?.width),
98
+ collapsed: parsed?.collapsed === true
99
+ };
100
+ } catch {
101
+ return { assistant: null, width: DEFAULT_WIDTH, collapsed: false };
102
+ }
103
+ }
104
+ function writePersisted(state) {
105
+ if (typeof window === "undefined") return;
106
+ try {
107
+ window.localStorage.setItem(
108
+ STORAGE_KEY,
109
+ JSON.stringify({
110
+ assistant: serializeAssistant(state.assistant),
111
+ width: state.width,
112
+ collapsed: state.collapsed
113
+ })
114
+ );
115
+ } catch {
116
+ }
117
+ }
118
+ function AiDockProvider({ children }) {
119
+ const [state, setState] = React.useState(() => ({
120
+ assistant: null,
121
+ width: DEFAULT_WIDTH,
122
+ collapsed: false
123
+ }));
124
+ const [hydrated, setHydrated] = React.useState(false);
125
+ React.useEffect(() => {
126
+ const persisted = readPersisted();
127
+ setState(persisted);
128
+ setHydrated(true);
129
+ }, []);
130
+ React.useEffect(() => {
131
+ if (!hydrated) return;
132
+ writePersisted(state);
133
+ }, [hydrated, state]);
134
+ const dock = React.useCallback((assistant) => {
135
+ setState((prev) => ({ ...prev, assistant, collapsed: false }));
136
+ }, []);
137
+ const undock = React.useCallback(() => {
138
+ setState((prev) => ({ ...prev, assistant: null, collapsed: false }));
139
+ }, []);
140
+ const isDocked = React.useCallback(
141
+ (agentId) => state.assistant?.agent === agentId,
142
+ [state.assistant?.agent]
143
+ );
144
+ const setWidth = React.useCallback((width) => {
145
+ setState((prev) => ({ ...prev, width }));
146
+ }, []);
147
+ const setCollapsed = React.useCallback((collapsed) => {
148
+ setState((prev) => ({ ...prev, collapsed }));
149
+ }, []);
150
+ const api = React.useMemo(
151
+ () => ({ state, dock, undock, isDocked }),
152
+ [state, dock, undock, isDocked]
153
+ );
154
+ const reservedWidth = state.assistant ? state.collapsed ? COLLAPSED_WIDTH : state.width : null;
155
+ return /* @__PURE__ */ jsxs(AiDockContext.Provider, { value: api, children: [
156
+ /* @__PURE__ */ jsx(
157
+ "div",
158
+ {
159
+ className: reservedWidth != null ? "lg:pr-[var(--om-ai-dock-width)]" : void 0,
160
+ style: reservedWidth != null ? { ["--om-ai-dock-width"]: `${reservedWidth}px` } : void 0,
161
+ children
162
+ }
163
+ ),
164
+ state.assistant ? /* @__PURE__ */ jsx(
165
+ AiDockPanel,
166
+ {
167
+ assistant: state.assistant,
168
+ width: state.width,
169
+ collapsed: state.collapsed,
170
+ onCollapsedChange: setCollapsed,
171
+ onWidthChange: setWidth,
172
+ onClose: undock
173
+ }
174
+ ) : null
175
+ ] });
176
+ }
177
+ function useAiDock() {
178
+ const ctx = React.useContext(AiDockContext);
179
+ if (ctx) return ctx;
180
+ return {
181
+ state: { assistant: null, width: DEFAULT_WIDTH, collapsed: false },
182
+ dock: () => {
183
+ },
184
+ undock: () => {
185
+ },
186
+ isDocked: () => false
187
+ };
188
+ }
189
+ function AiDockPanel({
190
+ assistant,
191
+ width,
192
+ collapsed,
193
+ onCollapsedChange,
194
+ onWidthChange,
195
+ onClose
196
+ }) {
197
+ const t = useT();
198
+ const dragStateRef = React.useRef(null);
199
+ const handlePointerDown = React.useCallback(
200
+ (event) => {
201
+ event.preventDefault();
202
+ const target = event.currentTarget;
203
+ target.setPointerCapture(event.pointerId);
204
+ dragStateRef.current = { startX: event.clientX, startWidth: width };
205
+ },
206
+ [width]
207
+ );
208
+ const handlePointerMove = React.useCallback(
209
+ (event) => {
210
+ if (!dragStateRef.current) return;
211
+ const delta = dragStateRef.current.startX - event.clientX;
212
+ const next = Math.min(
213
+ MAX_WIDTH,
214
+ Math.max(MIN_WIDTH, dragStateRef.current.startWidth + delta)
215
+ );
216
+ onWidthChange(next);
217
+ },
218
+ [onWidthChange]
219
+ );
220
+ const handlePointerUp = React.useCallback(
221
+ (event) => {
222
+ const target = event.currentTarget;
223
+ try {
224
+ target.releasePointerCapture(event.pointerId);
225
+ } catch {
226
+ }
227
+ dragStateRef.current = null;
228
+ },
229
+ []
230
+ );
231
+ return /* @__PURE__ */ jsxs(
232
+ "aside",
233
+ {
234
+ "data-ai-dock-panel": "",
235
+ "data-ai-dock-agent": assistant.agent,
236
+ className: cn(
237
+ // Dock is desktop-only — on small screens the AiChat dialog is the
238
+ // primary surface (full-screen sheet) and a fixed side panel would
239
+ // crowd the viewport.
240
+ "hidden lg:flex",
241
+ "fixed top-0 right-0 z-overlay h-svh flex-col border-l bg-background shadow-lg",
242
+ collapsed ? "w-12" : ""
243
+ ),
244
+ style: collapsed ? void 0 : { width },
245
+ "aria-label": assistant.label,
246
+ children: [
247
+ !collapsed ? /* @__PURE__ */ jsx(
248
+ "div",
249
+ {
250
+ role: "separator",
251
+ "aria-orientation": "vertical",
252
+ tabIndex: -1,
253
+ onPointerDown: handlePointerDown,
254
+ onPointerMove: handlePointerMove,
255
+ onPointerUp: handlePointerUp,
256
+ onPointerCancel: handlePointerUp,
257
+ className: "absolute left-0 top-0 h-full w-1 cursor-col-resize bg-transparent hover:bg-primary/20",
258
+ "data-ai-dock-resize-handle": ""
259
+ }
260
+ ) : null,
261
+ /* @__PURE__ */ jsx("header", { className: "flex items-center gap-2 px-3 py-2", children: collapsed ? /* @__PURE__ */ jsx(
262
+ IconButton,
263
+ {
264
+ type: "button",
265
+ variant: "ghost",
266
+ size: "sm",
267
+ "aria-label": t("ai_assistant.chat.dock.expand", "Expand AI dock"),
268
+ title: t("ai_assistant.chat.dock.expand", "Expand AI dock"),
269
+ onClick: () => onCollapsedChange(false),
270
+ children: /* @__PURE__ */ jsx(Maximize2, { className: "size-4" })
271
+ }
272
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
273
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
274
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", "data-ai-dock-label": "", children: [
275
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: assistant.label }),
276
+ /* @__PURE__ */ jsx(
277
+ "span",
278
+ {
279
+ className: "inline-flex shrink-0 items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium uppercase tracking-wide text-secondary-foreground",
280
+ "data-ai-beta-chip": "",
281
+ children: t("ai_assistant.chat.betaChip", "beta")
282
+ }
283
+ )
284
+ ] }),
285
+ assistant.description ? /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-muted-foreground", children: assistant.description }) : null
286
+ ] }),
287
+ /* @__PURE__ */ jsx(
288
+ IconButton,
289
+ {
290
+ type: "button",
291
+ variant: "ghost",
292
+ size: "sm",
293
+ "aria-label": t("ai_assistant.chat.dock.collapse", "Collapse AI dock"),
294
+ title: t("ai_assistant.chat.dock.collapse", "Collapse AI dock"),
295
+ onClick: () => onCollapsedChange(true),
296
+ children: /* @__PURE__ */ jsx(Minimize2, { className: "size-4" })
297
+ }
298
+ ),
299
+ /* @__PURE__ */ jsx(
300
+ IconButton,
301
+ {
302
+ type: "button",
303
+ variant: "ghost",
304
+ size: "sm",
305
+ "aria-label": t("ai_assistant.chat.dock.close", "Close AI dock"),
306
+ title: t("ai_assistant.chat.dock.close", "Close AI dock"),
307
+ onClick: onClose,
308
+ "data-ai-dock-close": "",
309
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
310
+ }
311
+ )
312
+ ] }) }),
313
+ !collapsed ? /* @__PURE__ */ jsx(DockedChatBody, { assistant }) : null
314
+ ]
315
+ }
316
+ );
317
+ }
318
+ function DockedChatBody({ assistant }) {
319
+ const sessions = useAiChatSessions();
320
+ const session = sessions.getActiveSession(assistant.agent);
321
+ React.useEffect(() => {
322
+ if (!session) sessions.ensureSession(assistant.agent);
323
+ }, [assistant.agent, session, sessions]);
324
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
325
+ /* @__PURE__ */ jsx(ChatPaneTabs, { agentId: assistant.agent, className: "border-b" }),
326
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", "data-ai-dock-chat-container": "", children: session ? /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
327
+ LazyAiChat,
328
+ {
329
+ agent: assistant.agent,
330
+ conversationId: session.conversationId,
331
+ pageContext: assistant.pageContext,
332
+ className: "h-full",
333
+ placeholder: assistant.placeholder,
334
+ suggestions: assistant.suggestions,
335
+ contextItems: assistant.contextItems,
336
+ welcomeTitle: assistant.welcomeTitle,
337
+ welcomeDescription: assistant.welcomeDescription
338
+ },
339
+ session.id
340
+ ) }) : null })
341
+ ] });
342
+ }
343
+ export {
344
+ AiDockProvider,
345
+ useAiDock
346
+ };
347
+ //# sourceMappingURL=AiDock.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/ai/AiDock.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\n/**\n * Global AI Dock \u2014 a persistent right-side panel that hosts an `<AiChat>`\n * surface across page navigations.\n *\n * Modules invoke `useAiDock().dock({ agent, label, pageContext })` (typically\n * from the dialog header of an injection trigger) to move an active\n * assistant from a transient dialog into the dock. The dock survives router\n * navigation because the provider is mounted at the layout root (AppShell).\n *\n * The panel renders only when something is docked. The docked assistant, its\n * collapsed state, and its width are persisted in localStorage so refreshing\n * the page restores the same open dock.\n */\n\nimport * as React from 'react'\nimport { Maximize2, Minimize2, X } from 'lucide-react'\nimport type { AiChatContextItem, AiChatSuggestion } from './AiChat'\nimport { ChatPaneTabs } from './ChatPaneTabs'\nimport { useAiChatSessions } from './AiChatSessions'\nimport { IconButton } from '../primitives/icon-button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\n// Lazy import keeps the heavy chat surface (AI SDK + streaming runtime) out\n// of the AppShell import graph. The dock provider only renders the chat when\n// something is actually docked, so tests that never dock skip the import.\nconst LazyAiChat = React.lazy(async () => {\n const mod = await import('./AiChat')\n return { default: mod.AiChat }\n})\n\nconst STORAGE_KEY = 'om-ai-dock-v1'\nconst MIN_WIDTH = 320\nconst MAX_WIDTH = 960\nconst DEFAULT_WIDTH = 420\n\nexport interface AiDockedAssistant {\n /** AI agent id (must be enabled for the current user). */\n agent: string\n /** Human-readable label shown in the dock header. */\n label: string\n /** Optional secondary description (e.g. module name). */\n description?: string\n /** Spec \u00A710.1 page-context payload sent with each turn. */\n pageContext?: Record<string, unknown>\n /** Composer placeholder copy. */\n placeholder?: string\n /** Welcome card title. */\n welcomeTitle?: string\n /** Welcome card description. */\n welcomeDescription?: string\n /** Optional starter suggestions. */\n suggestions?: AiChatSuggestion[]\n /** Optional pinned context chips (e.g. \"3 selected\"). */\n contextItems?: AiChatContextItem[]\n}\n\ninterface AiDockState {\n assistant: AiDockedAssistant | null\n width: number\n collapsed: boolean\n}\n\ninterface PersistedAiChatSuggestion {\n label: string\n prompt: string\n}\n\ninterface PersistedAiDockedAssistant {\n agent: string\n label: string\n description?: string\n pageContext?: Record<string, unknown>\n placeholder?: string\n welcomeTitle?: string\n welcomeDescription?: string\n suggestions?: PersistedAiChatSuggestion[]\n contextItems?: AiChatContextItem[]\n}\n\ninterface PersistedAiDockState {\n assistant?: PersistedAiDockedAssistant | null\n width?: number\n collapsed?: boolean\n}\n\ninterface AiDockApi {\n state: AiDockState\n /** Open / replace the docked assistant. */\n dock: (assistant: AiDockedAssistant) => void\n /** Close the dock. */\n undock: () => void\n /** True when the docked agent matches `agentId`. */\n isDocked: (agentId: string) => boolean\n}\n\nconst COLLAPSED_WIDTH = 48\n\nconst AiDockContext = React.createContext<AiDockApi | null>(null)\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction readWidth(value: unknown): number {\n return Math.min(\n MAX_WIDTH,\n Math.max(MIN_WIDTH, typeof value === 'number' ? value : DEFAULT_WIDTH),\n )\n}\n\nfunction readPersistedSuggestions(value: unknown): PersistedAiChatSuggestion[] | undefined {\n if (!Array.isArray(value)) return undefined\n const suggestions = value\n .filter((entry): entry is Record<string, unknown> => isRecord(entry))\n .map((entry) => {\n const label = readOptionalString(entry.label)\n const prompt = readOptionalString(entry.prompt)\n if (!label || !prompt) return null\n return { label, prompt }\n })\n .filter((entry): entry is PersistedAiChatSuggestion => entry !== null)\n return suggestions.length > 0 ? suggestions : undefined\n}\n\nfunction readPersistedContextItems(value: unknown): AiChatContextItem[] | undefined {\n if (!Array.isArray(value)) return undefined\n const contextItems = value\n .filter((entry): entry is Record<string, unknown> => isRecord(entry))\n .map((entry) => {\n const label = readOptionalString(entry.label)\n if (!label) return null\n const detail = readOptionalString(entry.detail)\n return detail ? { label, detail } : { label }\n })\n .filter((entry): entry is AiChatContextItem => entry !== null)\n return contextItems.length > 0 ? contextItems : undefined\n}\n\nfunction readPersistedAssistant(value: unknown): AiDockedAssistant | null {\n if (!isRecord(value)) return null\n const agent = readOptionalString(value.agent)\n const label = readOptionalString(value.label)\n if (!agent || !label) return null\n\n return {\n agent,\n label,\n description: readOptionalString(value.description),\n pageContext: isRecord(value.pageContext) ? value.pageContext : undefined,\n placeholder: readOptionalString(value.placeholder),\n welcomeTitle: readOptionalString(value.welcomeTitle),\n welcomeDescription: readOptionalString(value.welcomeDescription),\n suggestions: readPersistedSuggestions(value.suggestions),\n contextItems: readPersistedContextItems(value.contextItems),\n }\n}\n\nfunction serializeAssistant(assistant: AiDockedAssistant | null): PersistedAiDockedAssistant | null {\n if (!assistant) return null\n return {\n agent: assistant.agent,\n label: assistant.label,\n description: assistant.description,\n pageContext: assistant.pageContext,\n placeholder: assistant.placeholder,\n welcomeTitle: assistant.welcomeTitle,\n welcomeDescription: assistant.welcomeDescription,\n suggestions: assistant.suggestions?.map((suggestion) => ({\n label: suggestion.label,\n prompt: suggestion.prompt,\n })),\n contextItems: assistant.contextItems?.map((item) => ({\n label: item.label,\n detail: item.detail,\n })),\n }\n}\n\nfunction readPersisted(): AiDockState {\n if (typeof window === 'undefined') return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY)\n if (!raw) return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n const parsed = JSON.parse(raw) as PersistedAiDockState | null\n return {\n assistant: readPersistedAssistant(parsed?.assistant),\n width: readWidth(parsed?.width),\n collapsed: parsed?.collapsed === true,\n }\n } catch {\n return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n }\n}\n\nfunction writePersisted(state: AiDockState) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(\n STORAGE_KEY,\n JSON.stringify({\n assistant: serializeAssistant(state.assistant),\n width: state.width,\n collapsed: state.collapsed,\n } satisfies PersistedAiDockState),\n )\n } catch {\n /* ignore */\n }\n}\n\nexport function AiDockProvider({ children }: { children: React.ReactNode }) {\n const [state, setState] = React.useState<AiDockState>(() => ({\n assistant: null,\n width: DEFAULT_WIDTH,\n collapsed: false,\n }))\n const [hydrated, setHydrated] = React.useState(false)\n\n React.useEffect(() => {\n const persisted = readPersisted()\n setState(persisted)\n setHydrated(true)\n }, [])\n\n React.useEffect(() => {\n if (!hydrated) return\n writePersisted(state)\n }, [hydrated, state])\n\n const dock = React.useCallback((assistant: AiDockedAssistant) => {\n // Always reset `collapsed` when (re)docking \u2014 the operator just clicked\n // \"dock to side\" and expects the panel to be visible at full width.\n setState((prev) => ({ ...prev, assistant, collapsed: false }))\n }, [])\n\n const undock = React.useCallback(() => {\n // Hard reset: drop the assistant AND the collapsed flag so the next\n // dock call starts from a clean slate. The wrapper observes `assistant`\n // turning null and clears its layout shift in the same render tick.\n setState((prev) => ({ ...prev, assistant: null, collapsed: false }))\n }, [])\n\n const isDocked = React.useCallback(\n (agentId: string) => state.assistant?.agent === agentId,\n [state.assistant?.agent],\n )\n\n const setWidth = React.useCallback((width: number) => {\n setState((prev) => ({ ...prev, width }))\n }, [])\n\n const setCollapsed = React.useCallback((collapsed: boolean) => {\n setState((prev) => ({ ...prev, collapsed }))\n }, [])\n\n const api = React.useMemo<AiDockApi>(\n () => ({ state, dock, undock, isDocked }),\n [state, dock, undock, isDocked],\n )\n\n // The right-side padding the page must reserve for the dock panel. Stays\n // null while nothing is docked so the wrapper renders no className/style\n // and the underlying layout (DataTable, sidebar grid) reclaims its full\n // width on undock.\n const reservedWidth = state.assistant\n ? state.collapsed\n ? COLLAPSED_WIDTH\n : state.width\n : null\n\n return (\n <AiDockContext.Provider value={api}>\n <div\n // The dock is desktop-only (`lg+`); reserve right-side padding only\n // at that breakpoint so mobile layout stays full-width.\n className={reservedWidth != null ? 'lg:pr-[var(--om-ai-dock-width)]' : undefined}\n style={\n reservedWidth != null\n ? ({ ['--om-ai-dock-width' as string]: `${reservedWidth}px` } as React.CSSProperties)\n : undefined\n }\n >\n {children}\n </div>\n {state.assistant ? (\n <AiDockPanel\n assistant={state.assistant}\n width={state.width}\n collapsed={state.collapsed}\n onCollapsedChange={setCollapsed}\n onWidthChange={setWidth}\n onClose={undock}\n />\n ) : null}\n </AiDockContext.Provider>\n )\n}\n\nexport function useAiDock(): AiDockApi {\n const ctx = React.useContext(AiDockContext)\n if (ctx) return ctx\n // Fallback no-op API \u2014 keeps consumers safe when the provider is absent\n // (e.g. unit tests rendering a widget in isolation).\n return {\n state: { assistant: null, width: DEFAULT_WIDTH, collapsed: false },\n dock: () => {},\n undock: () => {},\n isDocked: () => false,\n }\n}\n\ninterface AiDockPanelProps {\n assistant: AiDockedAssistant\n width: number\n collapsed: boolean\n onCollapsedChange: (collapsed: boolean) => void\n onWidthChange: (width: number) => void\n onClose: () => void\n}\n\nfunction AiDockPanel({\n assistant,\n width,\n collapsed,\n onCollapsedChange,\n onWidthChange,\n onClose,\n}: AiDockPanelProps) {\n const t = useT()\n const dragStateRef = React.useRef<{ startX: number; startWidth: number } | null>(null)\n\n const handlePointerDown = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault()\n const target = event.currentTarget\n target.setPointerCapture(event.pointerId)\n dragStateRef.current = { startX: event.clientX, startWidth: width }\n },\n [width],\n )\n\n const handlePointerMove = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!dragStateRef.current) return\n const delta = dragStateRef.current.startX - event.clientX\n const next = Math.min(\n MAX_WIDTH,\n Math.max(MIN_WIDTH, dragStateRef.current.startWidth + delta),\n )\n onWidthChange(next)\n },\n [onWidthChange],\n )\n\n const handlePointerUp = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n const target = event.currentTarget\n try {\n target.releasePointerCapture(event.pointerId)\n } catch {\n /* ignore */\n }\n dragStateRef.current = null\n },\n [],\n )\n\n return (\n <aside\n data-ai-dock-panel=\"\"\n data-ai-dock-agent={assistant.agent}\n className={cn(\n // Dock is desktop-only \u2014 on small screens the AiChat dialog is the\n // primary surface (full-screen sheet) and a fixed side panel would\n // crowd the viewport.\n 'hidden lg:flex',\n 'fixed top-0 right-0 z-overlay h-svh flex-col border-l bg-background shadow-lg',\n collapsed ? 'w-12' : '',\n )}\n style={collapsed ? undefined : { width }}\n aria-label={assistant.label}\n >\n {!collapsed ? (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabIndex={-1}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n className=\"absolute left-0 top-0 h-full w-1 cursor-col-resize bg-transparent hover:bg-primary/20\"\n data-ai-dock-resize-handle=\"\"\n />\n ) : null}\n <header className=\"flex items-center gap-2 px-3 py-2\">\n {collapsed ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.expand', 'Expand AI dock')}\n title={t('ai_assistant.chat.dock.expand', 'Expand AI dock')}\n onClick={() => onCollapsedChange(false)}\n >\n <Maximize2 className=\"size-4\" />\n </IconButton>\n ) : (\n <>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 text-sm font-medium\" data-ai-dock-label=\"\">\n <span className=\"truncate\">{assistant.label}</span>\n <span\n className=\"inline-flex shrink-0 items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium uppercase tracking-wide text-secondary-foreground\"\n data-ai-beta-chip=\"\"\n >\n {t('ai_assistant.chat.betaChip', 'beta')}\n </span>\n </div>\n {assistant.description ? (\n <div className=\"truncate text-xs text-muted-foreground\">\n {assistant.description}\n </div>\n ) : null}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.collapse', 'Collapse AI dock')}\n title={t('ai_assistant.chat.dock.collapse', 'Collapse AI dock')}\n onClick={() => onCollapsedChange(true)}\n >\n <Minimize2 className=\"size-4\" />\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.close', 'Close AI dock')}\n title={t('ai_assistant.chat.dock.close', 'Close AI dock')}\n onClick={onClose}\n data-ai-dock-close=\"\"\n >\n <X className=\"size-4\" />\n </IconButton>\n </>\n )}\n </header>\n {!collapsed ? (\n <DockedChatBody assistant={assistant} />\n ) : null}\n </aside>\n )\n}\n\nfunction DockedChatBody({ assistant }: { assistant: AiDockedAssistant }) {\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(assistant.agent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns\n // it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(assistant.agent)\n }, [assistant.agent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={assistant.agent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-dock-chat-container=\"\">\n {session ? (\n <React.Suspense fallback={null}>\n <LazyAiChat\n // `key` forces a fresh `<AiChat>` mount whenever the active\n // session changes \u2014 without it the AI SDK's internal status\n // would carry across tabs and surface the previous tab's\n // streaming indicator on a brand-new conversation.\n key={session.id}\n agent={assistant.agent}\n conversationId={session.conversationId}\n pageContext={assistant.pageContext}\n className=\"h-full\"\n placeholder={assistant.placeholder}\n suggestions={assistant.suggestions}\n contextItems={assistant.contextItems}\n welcomeTitle={assistant.welcomeTitle}\n welcomeDescription={assistant.welcomeDescription}\n />\n </React.Suspense>\n ) : null}\n </div>\n </>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }