@devicai/ui 0.13.0 → 0.14.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 (49) hide show
  1. package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js +334 -0
  2. package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js.map +1 -0
  3. package/dist/cjs/components/AIElementWrapper/activeWrapperRegistry.js +30 -0
  4. package/dist/cjs/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -0
  5. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js +161 -0
  6. package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -0
  7. package/dist/cjs/components/ChatDrawer/ChatDrawer.js +34 -4
  8. package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
  9. package/dist/cjs/components/ChatDrawer/ChatInput.js +8 -2
  10. package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -1
  11. package/dist/cjs/components/ChatDrawer/ChatMessages.js +29 -2
  12. package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
  13. package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js +1 -1
  14. package/dist/cjs/hooks/useDevicChat.js +1 -1
  15. package/dist/cjs/index.js +4 -0
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/provider/DevicProvider.js +53 -14
  18. package/dist/cjs/provider/DevicProvider.js.map +1 -1
  19. package/dist/cjs/styles.css +1 -1
  20. package/dist/esm/components/AIElementWrapper/AIElementWrapper.d.ts +14 -0
  21. package/dist/esm/components/AIElementWrapper/AIElementWrapper.js +332 -0
  22. package/dist/esm/components/AIElementWrapper/AIElementWrapper.js.map +1 -0
  23. package/dist/esm/components/AIElementWrapper/AIElementWrapper.types.d.ts +185 -0
  24. package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.d.ts +10 -0
  25. package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js +26 -0
  26. package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -0
  27. package/dist/esm/components/AIElementWrapper/index.d.ts +3 -0
  28. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.d.ts +26 -0
  29. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js +159 -0
  30. package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -0
  31. package/dist/esm/components/ChatDrawer/ChatDrawer.js +34 -4
  32. package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
  33. package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +11 -0
  34. package/dist/esm/components/ChatDrawer/ChatInput.d.ts +1 -1
  35. package/dist/esm/components/ChatDrawer/ChatInput.js +8 -2
  36. package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -1
  37. package/dist/esm/components/ChatDrawer/ChatMessages.js +29 -2
  38. package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
  39. package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js +1 -1
  40. package/dist/esm/hooks/useDevicChat.js +1 -1
  41. package/dist/esm/index.d.ts +3 -1
  42. package/dist/esm/index.js +2 -0
  43. package/dist/esm/index.js.map +1 -1
  44. package/dist/esm/provider/DevicProvider.js +54 -15
  45. package/dist/esm/provider/DevicProvider.js.map +1 -1
  46. package/dist/esm/provider/index.d.ts +1 -1
  47. package/dist/esm/provider/types.d.ts +50 -0
  48. package/dist/esm/styles.css +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,332 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { forwardRef, useMemo, useRef, useState, useEffect, useCallback, useLayoutEffect, useImperativeHandle } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { useOptionalDevicContext } from '../../provider/DevicContext.js';
5
+ import { generateId } from '../../utils/index.js';
6
+ import { useAIElementWrapper } from './useAIElementWrapper.js';
7
+ import { getActiveWrapper, subscribeActiveWrapper, setActiveWrapper } from './activeWrapperRegistry.js';
8
+
9
+ const DEFAULT_OPTIONS = {
10
+ showOn: 'hover',
11
+ triggerPlacement: 'bottom',
12
+ tooltipPlacement: 'bottom',
13
+ tooltipWidth: 360,
14
+ triggerLabel: 'Preguntar a IA',
15
+ highlightOnInteract: true,
16
+ zIndex: 2147483000,
17
+ triggerBorderRadius: 999,
18
+ color: undefined,
19
+ drawerPromptPrefix: undefined,
20
+ defaultInlinePrompt: undefined,
21
+ };
22
+ function rectFromDom(rect) {
23
+ return { top: rect.top, left: rect.left, width: rect.width, height: rect.height };
24
+ }
25
+ function placementStyle(placement, anchor, offset = 8) {
26
+ const cx = anchor.left + anchor.width / 2;
27
+ const cy = anchor.top + anchor.height / 2;
28
+ switch (placement) {
29
+ case 'top':
30
+ return { position: 'fixed', top: anchor.top - offset, left: cx, transform: 'translate(-50%, -100%)' };
31
+ case 'bottom':
32
+ return { position: 'fixed', top: anchor.top + anchor.height + offset, left: cx, transform: 'translateX(-50%)' };
33
+ case 'left':
34
+ return { position: 'fixed', top: cy, left: anchor.left - offset, transform: 'translate(-100%, -50%)' };
35
+ case 'right':
36
+ return { position: 'fixed', top: cy, left: anchor.left + anchor.width + offset, transform: 'translateY(-50%)' };
37
+ }
38
+ }
39
+ /**
40
+ * AIElementWrapper wraps an arbitrary React node and exposes an AI trigger
41
+ * that can either show an inline floating tooltip with the assistant's
42
+ * answer (`behavior='inline'`) or push a reference to the registered
43
+ * ChatDrawer (`behavior='drawer'`).
44
+ *
45
+ * The trigger and inline tooltip are rendered through a React portal anchored
46
+ * to the wrapped element, so they always sit above other UI (including the
47
+ * ChatDrawer) regardless of stacking context.
48
+ */
49
+ const AIElementWrapper = forwardRef(function AIElementWrapper(props, ref) {
50
+ const { label, data, referenceContent, behavior = 'inline', trigger, options = {}, assistantId, getPrompt, apiKey, baseUrl, tenantId, tenantMetadata, modelInterfaceTools, inlineRenderer, onActivate, onInlineResponse, onError, className, style, children, } = props;
51
+ const merged = useMemo(() => ({ ...DEFAULT_OPTIONS, ...options }), [options]);
52
+ // Stable instance ID used by the active-wrapper registry (singleton)
53
+ const wrapperIdRef = useRef('');
54
+ if (!wrapperIdRef.current)
55
+ wrapperIdRef.current = generateId();
56
+ const [activeWrapperId, setActiveWrapperLocal] = useState(getActiveWrapper());
57
+ useEffect(() => subscribeActiveWrapper(setActiveWrapperLocal), []);
58
+ const context = useOptionalDevicContext();
59
+ const containerRef = useRef(null);
60
+ const tooltipRef = useRef(null);
61
+ const triggerRef = useRef(null);
62
+ const [isHovered, setIsHovered] = useState(false);
63
+ const hoverTimerRef = useRef(null);
64
+ const setHoveredImmediately = useCallback((v) => {
65
+ if (hoverTimerRef.current) {
66
+ clearTimeout(hoverTimerRef.current);
67
+ hoverTimerRef.current = null;
68
+ }
69
+ if (v) {
70
+ setIsHovered(true);
71
+ }
72
+ else {
73
+ // Grace period so the cursor can travel from the wrapper to the
74
+ // portal-rendered trigger without the trigger disappearing mid-flight.
75
+ hoverTimerRef.current = setTimeout(() => setIsHovered(false), 200);
76
+ }
77
+ }, []);
78
+ useEffect(() => () => {
79
+ if (hoverTimerRef.current)
80
+ clearTimeout(hoverTimerRef.current);
81
+ }, []);
82
+ const [isInlineOpen, setIsInlineOpen] = useState(false);
83
+ const [containerRect, setContainerRect] = useState(null);
84
+ const [selectionRect, setSelectionRect] = useState(null);
85
+ const inline = useAIElementWrapper({
86
+ assistantId,
87
+ apiKey,
88
+ baseUrl,
89
+ tenantId,
90
+ tenantMetadata,
91
+ modelInterfaceTools,
92
+ onResponse: onInlineResponse,
93
+ onError,
94
+ });
95
+ // What this wrapper would show if there were no coordination.
96
+ const wantsTriggerVisible = merged.showOn === 'always' ||
97
+ (merged.showOn === 'hover' && isHovered) ||
98
+ (merged.showOn === 'click' && isInlineOpen) ||
99
+ (merged.showOn === 'select' && selectionRect !== null);
100
+ // Coordinate via the singleton registry so only one wrapper shows the
101
+ // floating trigger at a time. The most recent wrapper to want visibility
102
+ // wins; others hide until they are activated again.
103
+ const isActive = activeWrapperId === wrapperIdRef.current;
104
+ const triggerVisible = wantsTriggerVisible && (activeWrapperId === null || isActive);
105
+ useEffect(() => {
106
+ const id = wrapperIdRef.current;
107
+ if (wantsTriggerVisible) {
108
+ setActiveWrapper(id);
109
+ }
110
+ else if (getActiveWrapper() === id) {
111
+ setActiveWrapper(null);
112
+ }
113
+ }, [wantsTriggerVisible]);
114
+ // Release the registry slot if the component unmounts while active.
115
+ useEffect(() => () => {
116
+ if (getActiveWrapper() === wrapperIdRef.current) {
117
+ setActiveWrapper(null);
118
+ }
119
+ }, []);
120
+ // Track container rect (for hover/click/always trigger and tooltip anchor)
121
+ const updateContainerRect = useCallback(() => {
122
+ const el = containerRef.current;
123
+ if (!el)
124
+ return;
125
+ setContainerRect(rectFromDom(el.getBoundingClientRect()));
126
+ }, []);
127
+ useLayoutEffect(() => {
128
+ if (!triggerVisible && !isInlineOpen)
129
+ return;
130
+ updateContainerRect();
131
+ const onScrollOrResize = () => updateContainerRect();
132
+ window.addEventListener('scroll', onScrollOrResize, true);
133
+ window.addEventListener('resize', onScrollOrResize);
134
+ return () => {
135
+ window.removeEventListener('scroll', onScrollOrResize, true);
136
+ window.removeEventListener('resize', onScrollOrResize);
137
+ };
138
+ }, [triggerVisible, isInlineOpen, updateContainerRect]);
139
+ // selectionchange listener for showOn='select'
140
+ useEffect(() => {
141
+ if (merged.showOn !== 'select') {
142
+ setSelectionRect(null);
143
+ return;
144
+ }
145
+ const isInside = (node, cont) => {
146
+ if (!node)
147
+ return false;
148
+ if (node === cont)
149
+ return true;
150
+ return cont.contains(node);
151
+ };
152
+ const recompute = () => {
153
+ const sel = window.getSelection();
154
+ const cont = containerRef.current;
155
+ if (!sel || sel.isCollapsed || !cont || sel.rangeCount === 0) {
156
+ setSelectionRect(null);
157
+ return;
158
+ }
159
+ // Don't react to selections happening elsewhere — but tolerate the
160
+ // case where anchor or focus are inside our container.
161
+ if (!isInside(sel.anchorNode, cont) && !isInside(sel.focusNode, cont)) {
162
+ setSelectionRect(null);
163
+ return;
164
+ }
165
+ const range = sel.getRangeAt(0);
166
+ const rect = range.getBoundingClientRect();
167
+ if (rect.width === 0 && rect.height === 0) {
168
+ setSelectionRect(null);
169
+ return;
170
+ }
171
+ setSelectionRect(rectFromDom(rect));
172
+ };
173
+ const onMouseUp = () => {
174
+ // Run on next tick so the browser commits the final selection state.
175
+ setTimeout(recompute, 0);
176
+ };
177
+ document.addEventListener('selectionchange', recompute);
178
+ document.addEventListener('mouseup', onMouseUp);
179
+ return () => {
180
+ document.removeEventListener('selectionchange', recompute);
181
+ document.removeEventListener('mouseup', onMouseUp);
182
+ };
183
+ }, [merged.showOn]);
184
+ const buildPrompt = useCallback(() => {
185
+ if (getPrompt)
186
+ return getPrompt({ data, label });
187
+ if (merged.defaultInlinePrompt)
188
+ return merged.defaultInlinePrompt;
189
+ // Use selected text if available, otherwise fall back to label
190
+ if (merged.showOn === 'select') {
191
+ const sel = window.getSelection();
192
+ const txt = sel?.toString().trim();
193
+ if (txt)
194
+ return `Cuéntame más sobre: "${txt}"`;
195
+ }
196
+ return `Cuéntame más sobre: ${label}`;
197
+ }, [getPrompt, data, label, merged.defaultInlinePrompt, merged.showOn]);
198
+ const handleActivate = useCallback(() => {
199
+ onActivate?.();
200
+ if (behavior === 'inline') {
201
+ if (!assistantId) {
202
+ const err = new Error('assistantId is required for behavior="inline"');
203
+ onError?.(err);
204
+ // eslint-disable-next-line no-console
205
+ console.warn('[AIElementWrapper]', err.message);
206
+ return;
207
+ }
208
+ setIsInlineOpen(true);
209
+ inline.reset();
210
+ inline.sendInlinePrompt(buildPrompt());
211
+ return;
212
+ }
213
+ // drawer behavior
214
+ if (!context) {
215
+ // eslint-disable-next-line no-console
216
+ console.warn('[AIElementWrapper] behavior="drawer" requires a DevicProvider ancestor.');
217
+ return;
218
+ }
219
+ // For 'select' showOn, prefer selected text as label content fallback
220
+ let finalLabel = label;
221
+ if (merged.showOn === 'select') {
222
+ const txt = window.getSelection()?.toString().trim();
223
+ if (txt)
224
+ finalLabel = txt;
225
+ }
226
+ context.addReference({ label: finalLabel, content: referenceContent, data });
227
+ context.openDrawer();
228
+ }, [
229
+ onActivate,
230
+ behavior,
231
+ assistantId,
232
+ onError,
233
+ inline,
234
+ buildPrompt,
235
+ context,
236
+ label,
237
+ referenceContent,
238
+ data,
239
+ merged.showOn,
240
+ ]);
241
+ const closeInline = useCallback(() => {
242
+ setIsInlineOpen(false);
243
+ inline.reset();
244
+ }, [inline]);
245
+ useImperativeHandle(ref, () => ({
246
+ activate: handleActivate,
247
+ close: closeInline,
248
+ }), [handleActivate, closeInline]);
249
+ // Click outside to close inline tooltip
250
+ useEffect(() => {
251
+ if (!isInlineOpen)
252
+ return;
253
+ const handler = (e) => {
254
+ const t = tooltipRef.current;
255
+ const c = containerRef.current;
256
+ const tr = triggerRef.current;
257
+ const target = e.target;
258
+ if (t && !t.contains(target) &&
259
+ c && !c.contains(target) &&
260
+ (!tr || !tr.contains(target))) {
261
+ closeInline();
262
+ }
263
+ };
264
+ document.addEventListener('mousedown', handler);
265
+ return () => document.removeEventListener('mousedown', handler);
266
+ }, [isInlineOpen, closeInline]);
267
+ // Anchor for trigger: selection rect (when showOn='select') else container rect
268
+ const triggerAnchor = merged.showOn === 'select' ? selectionRect : containerRect;
269
+ // Tooltip anchor: container rect (or selection if select mode)
270
+ const tooltipAnchor = merged.showOn === 'select' && selectionRect ? selectionRect : containerRect;
271
+ const triggerStyle = useMemo(() => {
272
+ if (!triggerAnchor)
273
+ return { display: 'none' };
274
+ return {
275
+ ...placementStyle(merged.triggerPlacement, triggerAnchor),
276
+ zIndex: merged.zIndex + 1,
277
+ pointerEvents: 'auto',
278
+ };
279
+ }, [triggerAnchor, merged.triggerPlacement, merged.zIndex]);
280
+ const tooltipStyle = useMemo(() => {
281
+ const w = typeof merged.tooltipWidth === 'number' ? `${merged.tooltipWidth}px` : merged.tooltipWidth;
282
+ if (!tooltipAnchor)
283
+ return { display: 'none' };
284
+ return {
285
+ ...placementStyle(merged.tooltipPlacement, tooltipAnchor),
286
+ width: w,
287
+ zIndex: merged.zIndex,
288
+ };
289
+ }, [tooltipAnchor, merged.tooltipPlacement, merged.tooltipWidth, merged.zIndex]);
290
+ const renderInlineContent = () => {
291
+ if (inline.error) {
292
+ return jsx("div", { className: "devic-aiwrap-error", children: inline.error.message });
293
+ }
294
+ if (inline.isProcessing) {
295
+ return (jsxs("div", { className: "devic-aiwrap-processing", children: [jsx("span", { className: "devic-aiwrap-spinner", "aria-hidden": "true" }), jsx("span", { children: "Pensando\u2026" })] }));
296
+ }
297
+ if (inline.response) {
298
+ if (inlineRenderer)
299
+ return inlineRenderer(inline.response);
300
+ const text = typeof inline.response.content === 'string'
301
+ ? inline.response.content
302
+ : inline.response.content?.message || '';
303
+ return jsx("div", { className: "devic-aiwrap-answer", children: text });
304
+ }
305
+ return null;
306
+ };
307
+ const triggerNode = trigger ?? (jsxs("button", { type: "button", className: "devic-aiwrap-trigger", style: {
308
+ borderRadius: typeof merged.triggerBorderRadius === 'number'
309
+ ? `${merged.triggerBorderRadius}px`
310
+ : merged.triggerBorderRadius,
311
+ ...(merged.color ? { ['--devic-aiwrap-color']: merged.color } : {}),
312
+ }, children: [jsx("span", { className: "devic-aiwrap-trigger-icon", "aria-hidden": "true", children: jsx(SparklesIcon, {}) }), jsx("span", { className: "devic-aiwrap-trigger-label", children: merged.triggerLabel })] }));
313
+ const portalTarget = typeof document !== 'undefined' ? document.body : null;
314
+ return (jsxs("span", { ref: containerRef, className: `devic-aiwrap-container ${className || ''}`, style: { position: 'relative', display: 'inline-block', ...style }, "data-highlight": merged.highlightOnInteract && (isHovered || isInlineOpen) ? 'true' : 'false', onMouseEnter: () => setHoveredImmediately(true), onMouseLeave: () => setHoveredImmediately(false), children: [jsx("span", { className: "devic-aiwrap-content", children: children }), portalTarget && triggerVisible &&
315
+ createPortal(jsx("div", { ref: triggerRef, className: "devic-aiwrap-trigger-wrapper", style: triggerStyle, onMouseEnter: () => setHoveredImmediately(true), onMouseLeave: () => setHoveredImmediately(false), onMouseDown: (e) => {
316
+ // Prevent losing the text selection when interacting with trigger
317
+ e.preventDefault();
318
+ }, onClick: (e) => {
319
+ e.stopPropagation();
320
+ handleActivate();
321
+ }, children: triggerNode }), portalTarget), portalTarget && behavior === 'inline' && isInlineOpen &&
322
+ createPortal(jsxs("div", { ref: tooltipRef, className: "devic-aiwrap-tooltip", style: tooltipStyle, "data-placement": merged.tooltipPlacement, children: [jsxs("div", { className: "devic-aiwrap-tooltip-header", children: [jsx("span", { className: "devic-aiwrap-tooltip-label", children: label }), jsx("button", { type: "button", className: "devic-aiwrap-tooltip-close", onClick: closeInline, "aria-label": "Cerrar", children: jsx(CloseIcon, {}) })] }), jsx("div", { className: "devic-aiwrap-tooltip-body", children: renderInlineContent() })] }), portalTarget)] }));
323
+ });
324
+ function SparklesIcon() {
325
+ return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M10 2L11.5 8.5L18 10L11.5 11.5L10 18L8.5 11.5L2 10L8.5 8.5L10 2Z", opacity: "0.95" }), jsx("path", { d: "M16 3L16.5 5L18.5 5.5L16.5 6L16 8L15.5 6L13.5 5.5L15.5 5L16 3Z", opacity: "0.6" })] }));
326
+ }
327
+ function CloseIcon() {
328
+ return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
329
+ }
330
+
331
+ export { AIElementWrapper };
332
+ //# sourceMappingURL=AIElementWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIElementWrapper.js","sources":["../../../../src/components/AIElementWrapper/AIElementWrapper.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { useOptionalDevicContext } from '../../provider';\nimport { useAIElementWrapper } from './useAIElementWrapper';\nimport {\n getActiveWrapper,\n setActiveWrapper,\n subscribeActiveWrapper,\n} from './activeWrapperRegistry';\nimport { generateId } from '../../utils';\nimport type {\n AIElementWrapperHandle,\n AIElementWrapperOptions,\n AIElementWrapperPlacement,\n AIElementWrapperProps,\n} from './AIElementWrapper.types';\nimport './AIElementWrapper.css';\n\nconst DEFAULT_OPTIONS: Required<\n Omit<AIElementWrapperOptions, 'color' | 'drawerPromptPrefix' | 'defaultInlinePrompt'>\n> & {\n color?: string;\n drawerPromptPrefix?: AIElementWrapperOptions['drawerPromptPrefix'];\n defaultInlinePrompt?: string;\n} = {\n showOn: 'hover',\n triggerPlacement: 'bottom',\n tooltipPlacement: 'bottom',\n tooltipWidth: 360,\n triggerLabel: 'Preguntar a IA',\n highlightOnInteract: true,\n zIndex: 2147483000,\n triggerBorderRadius: 999,\n color: undefined,\n drawerPromptPrefix: undefined,\n defaultInlinePrompt: undefined,\n};\n\ninterface AnchorRect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\nfunction rectFromDom(rect: DOMRect): AnchorRect {\n return { top: rect.top, left: rect.left, width: rect.width, height: rect.height };\n}\n\nfunction placementStyle(\n placement: AIElementWrapperPlacement,\n anchor: AnchorRect,\n offset = 8\n): React.CSSProperties {\n const cx = anchor.left + anchor.width / 2;\n const cy = anchor.top + anchor.height / 2;\n switch (placement) {\n case 'top':\n return { position: 'fixed', top: anchor.top - offset, left: cx, transform: 'translate(-50%, -100%)' };\n case 'bottom':\n return { position: 'fixed', top: anchor.top + anchor.height + offset, left: cx, transform: 'translateX(-50%)' };\n case 'left':\n return { position: 'fixed', top: cy, left: anchor.left - offset, transform: 'translate(-100%, -50%)' };\n case 'right':\n return { position: 'fixed', top: cy, left: anchor.left + anchor.width + offset, transform: 'translateY(-50%)' };\n }\n}\n\n/**\n * AIElementWrapper wraps an arbitrary React node and exposes an AI trigger\n * that can either show an inline floating tooltip with the assistant's\n * answer (`behavior='inline'`) or push a reference to the registered\n * ChatDrawer (`behavior='drawer'`).\n *\n * The trigger and inline tooltip are rendered through a React portal anchored\n * to the wrapped element, so they always sit above other UI (including the\n * ChatDrawer) regardless of stacking context.\n */\nexport const AIElementWrapper = forwardRef<AIElementWrapperHandle, AIElementWrapperProps>(\n function AIElementWrapper(props, ref) {\n const {\n label,\n data,\n referenceContent,\n behavior = 'inline',\n trigger,\n options = {},\n assistantId,\n getPrompt,\n apiKey,\n baseUrl,\n tenantId,\n tenantMetadata,\n modelInterfaceTools,\n inlineRenderer,\n onActivate,\n onInlineResponse,\n onError,\n className,\n style,\n children,\n } = props;\n\n const merged = useMemo(() => ({ ...DEFAULT_OPTIONS, ...options }), [options]);\n\n // Stable instance ID used by the active-wrapper registry (singleton)\n const wrapperIdRef = useRef<string>('');\n if (!wrapperIdRef.current) wrapperIdRef.current = generateId();\n\n const [activeWrapperId, setActiveWrapperLocal] = useState<string | null>(getActiveWrapper());\n useEffect(() => subscribeActiveWrapper(setActiveWrapperLocal), []);\n\n const context = useOptionalDevicContext();\n const containerRef = useRef<HTMLSpanElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLDivElement>(null);\n\n const [isHovered, setIsHovered] = useState(false);\n const hoverTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const setHoveredImmediately = useCallback((v: boolean) => {\n if (hoverTimerRef.current) {\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = null;\n }\n if (v) {\n setIsHovered(true);\n } else {\n // Grace period so the cursor can travel from the wrapper to the\n // portal-rendered trigger without the trigger disappearing mid-flight.\n hoverTimerRef.current = setTimeout(() => setIsHovered(false), 200);\n }\n }, []);\n useEffect(() => () => {\n if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);\n }, []);\n const [isInlineOpen, setIsInlineOpen] = useState(false);\n const [containerRect, setContainerRect] = useState<AnchorRect | null>(null);\n const [selectionRect, setSelectionRect] = useState<AnchorRect | null>(null);\n\n const inline = useAIElementWrapper({\n assistantId,\n apiKey,\n baseUrl,\n tenantId,\n tenantMetadata,\n modelInterfaceTools,\n onResponse: onInlineResponse,\n onError,\n });\n\n // What this wrapper would show if there were no coordination.\n const wantsTriggerVisible =\n merged.showOn === 'always' ||\n (merged.showOn === 'hover' && isHovered) ||\n (merged.showOn === 'click' && isInlineOpen) ||\n (merged.showOn === 'select' && selectionRect !== null);\n\n // Coordinate via the singleton registry so only one wrapper shows the\n // floating trigger at a time. The most recent wrapper to want visibility\n // wins; others hide until they are activated again.\n const isActive = activeWrapperId === wrapperIdRef.current;\n const triggerVisible = wantsTriggerVisible && (activeWrapperId === null || isActive);\n\n useEffect(() => {\n const id = wrapperIdRef.current;\n if (wantsTriggerVisible) {\n setActiveWrapper(id);\n } else if (getActiveWrapper() === id) {\n setActiveWrapper(null);\n }\n }, [wantsTriggerVisible]);\n\n // Release the registry slot if the component unmounts while active.\n useEffect(\n () => () => {\n if (getActiveWrapper() === wrapperIdRef.current) {\n setActiveWrapper(null);\n }\n },\n []\n );\n\n // Track container rect (for hover/click/always trigger and tooltip anchor)\n const updateContainerRect = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n setContainerRect(rectFromDom(el.getBoundingClientRect()));\n }, []);\n\n useLayoutEffect(() => {\n if (!triggerVisible && !isInlineOpen) return;\n updateContainerRect();\n const onScrollOrResize = () => updateContainerRect();\n window.addEventListener('scroll', onScrollOrResize, true);\n window.addEventListener('resize', onScrollOrResize);\n return () => {\n window.removeEventListener('scroll', onScrollOrResize, true);\n window.removeEventListener('resize', onScrollOrResize);\n };\n }, [triggerVisible, isInlineOpen, updateContainerRect]);\n\n // selectionchange listener for showOn='select'\n useEffect(() => {\n if (merged.showOn !== 'select') {\n setSelectionRect(null);\n return;\n }\n const isInside = (node: Node | null, cont: HTMLElement) => {\n if (!node) return false;\n if (node === cont) return true;\n return cont.contains(node);\n };\n const recompute = () => {\n const sel = window.getSelection();\n const cont = containerRef.current;\n if (!sel || sel.isCollapsed || !cont || sel.rangeCount === 0) {\n setSelectionRect(null);\n return;\n }\n // Don't react to selections happening elsewhere — but tolerate the\n // case where anchor or focus are inside our container.\n if (!isInside(sel.anchorNode, cont) && !isInside(sel.focusNode, cont)) {\n setSelectionRect(null);\n return;\n }\n const range = sel.getRangeAt(0);\n const rect = range.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) {\n setSelectionRect(null);\n return;\n }\n setSelectionRect(rectFromDom(rect));\n };\n const onMouseUp = () => {\n // Run on next tick so the browser commits the final selection state.\n setTimeout(recompute, 0);\n };\n document.addEventListener('selectionchange', recompute);\n document.addEventListener('mouseup', onMouseUp);\n return () => {\n document.removeEventListener('selectionchange', recompute);\n document.removeEventListener('mouseup', onMouseUp);\n };\n }, [merged.showOn]);\n\n const buildPrompt = useCallback((): string => {\n if (getPrompt) return getPrompt({ data, label });\n if (merged.defaultInlinePrompt) return merged.defaultInlinePrompt;\n // Use selected text if available, otherwise fall back to label\n if (merged.showOn === 'select') {\n const sel = window.getSelection();\n const txt = sel?.toString().trim();\n if (txt) return `Cuéntame más sobre: \"${txt}\"`;\n }\n return `Cuéntame más sobre: ${label}`;\n }, [getPrompt, data, label, merged.defaultInlinePrompt, merged.showOn]);\n\n const handleActivate = useCallback(() => {\n onActivate?.();\n\n if (behavior === 'inline') {\n if (!assistantId) {\n const err = new Error('assistantId is required for behavior=\"inline\"');\n onError?.(err);\n // eslint-disable-next-line no-console\n console.warn('[AIElementWrapper]', err.message);\n return;\n }\n setIsInlineOpen(true);\n inline.reset();\n inline.sendInlinePrompt(buildPrompt());\n return;\n }\n\n // drawer behavior\n if (!context) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AIElementWrapper] behavior=\"drawer\" requires a DevicProvider ancestor.'\n );\n return;\n }\n // For 'select' showOn, prefer selected text as label content fallback\n let finalLabel = label;\n if (merged.showOn === 'select') {\n const txt = window.getSelection()?.toString().trim();\n if (txt) finalLabel = txt;\n }\n context.addReference({ label: finalLabel, content: referenceContent, data });\n context.openDrawer();\n }, [\n onActivate,\n behavior,\n assistantId,\n onError,\n inline,\n buildPrompt,\n context,\n label,\n referenceContent,\n data,\n merged.showOn,\n ]);\n\n const closeInline = useCallback(() => {\n setIsInlineOpen(false);\n inline.reset();\n }, [inline]);\n\n useImperativeHandle(\n ref,\n () => ({\n activate: handleActivate,\n close: closeInline,\n }),\n [handleActivate, closeInline]\n );\n\n // Click outside to close inline tooltip\n useEffect(() => {\n if (!isInlineOpen) return;\n const handler = (e: MouseEvent) => {\n const t = tooltipRef.current;\n const c = containerRef.current;\n const tr = triggerRef.current;\n const target = e.target as Node;\n if (\n t && !t.contains(target) &&\n c && !c.contains(target) &&\n (!tr || !tr.contains(target))\n ) {\n closeInline();\n }\n };\n document.addEventListener('mousedown', handler);\n return () => document.removeEventListener('mousedown', handler);\n }, [isInlineOpen, closeInline]);\n\n // Anchor for trigger: selection rect (when showOn='select') else container rect\n const triggerAnchor = merged.showOn === 'select' ? selectionRect : containerRect;\n // Tooltip anchor: container rect (or selection if select mode)\n const tooltipAnchor = merged.showOn === 'select' && selectionRect ? selectionRect : containerRect;\n\n const triggerStyle = useMemo<React.CSSProperties>(() => {\n if (!triggerAnchor) return { display: 'none' };\n return {\n ...placementStyle(merged.triggerPlacement, triggerAnchor),\n zIndex: merged.zIndex + 1,\n pointerEvents: 'auto',\n };\n }, [triggerAnchor, merged.triggerPlacement, merged.zIndex]);\n\n const tooltipStyle = useMemo<React.CSSProperties>(() => {\n const w = typeof merged.tooltipWidth === 'number' ? `${merged.tooltipWidth}px` : merged.tooltipWidth;\n if (!tooltipAnchor) return { display: 'none' };\n return {\n ...placementStyle(merged.tooltipPlacement, tooltipAnchor),\n width: w,\n zIndex: merged.zIndex,\n };\n }, [tooltipAnchor, merged.tooltipPlacement, merged.tooltipWidth, merged.zIndex]);\n\n const renderInlineContent = () => {\n if (inline.error) {\n return <div className=\"devic-aiwrap-error\">{inline.error.message}</div>;\n }\n if (inline.isProcessing) {\n return (\n <div className=\"devic-aiwrap-processing\">\n <span className=\"devic-aiwrap-spinner\" aria-hidden=\"true\" />\n <span>Pensando…</span>\n </div>\n );\n }\n if (inline.response) {\n if (inlineRenderer) return inlineRenderer(inline.response);\n const text =\n typeof inline.response.content === 'string'\n ? inline.response.content\n : (inline.response.content as any)?.message || '';\n return <div className=\"devic-aiwrap-answer\">{text}</div>;\n }\n return null;\n };\n\n const triggerNode = trigger ?? (\n <button\n type=\"button\"\n className=\"devic-aiwrap-trigger\"\n style={{\n borderRadius:\n typeof merged.triggerBorderRadius === 'number'\n ? `${merged.triggerBorderRadius}px`\n : merged.triggerBorderRadius,\n ...(merged.color ? { ['--devic-aiwrap-color' as any]: merged.color } : {}),\n }}\n >\n <span className=\"devic-aiwrap-trigger-icon\" aria-hidden=\"true\">\n <SparklesIcon />\n </span>\n <span className=\"devic-aiwrap-trigger-label\">{merged.triggerLabel}</span>\n </button>\n );\n\n const portalTarget = typeof document !== 'undefined' ? document.body : null;\n\n return (\n <span\n ref={containerRef}\n className={`devic-aiwrap-container ${className || ''}`}\n style={{ position: 'relative', display: 'inline-block', ...style }}\n data-highlight={merged.highlightOnInteract && (isHovered || isInlineOpen) ? 'true' : 'false'}\n onMouseEnter={() => setHoveredImmediately(true)}\n onMouseLeave={() => setHoveredImmediately(false)}\n >\n <span className=\"devic-aiwrap-content\">{children}</span>\n\n {portalTarget && triggerVisible &&\n createPortal(\n <div\n ref={triggerRef}\n className=\"devic-aiwrap-trigger-wrapper\"\n style={triggerStyle}\n onMouseEnter={() => setHoveredImmediately(true)}\n onMouseLeave={() => setHoveredImmediately(false)}\n onMouseDown={(e) => {\n // Prevent losing the text selection when interacting with trigger\n e.preventDefault();\n }}\n onClick={(e) => {\n e.stopPropagation();\n handleActivate();\n }}\n >\n {triggerNode}\n </div>,\n portalTarget\n )}\n\n {portalTarget && behavior === 'inline' && isInlineOpen &&\n createPortal(\n <div\n ref={tooltipRef}\n className=\"devic-aiwrap-tooltip\"\n style={tooltipStyle}\n data-placement={merged.tooltipPlacement}\n >\n <div className=\"devic-aiwrap-tooltip-header\">\n <span className=\"devic-aiwrap-tooltip-label\">{label}</span>\n <button\n type=\"button\"\n className=\"devic-aiwrap-tooltip-close\"\n onClick={closeInline}\n aria-label=\"Cerrar\"\n >\n <CloseIcon />\n </button>\n </div>\n <div className=\"devic-aiwrap-tooltip-body\">{renderInlineContent()}</div>\n </div>,\n portalTarget\n )}\n </span>\n );\n }\n);\n\nfunction SparklesIcon(): JSX.Element {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10 2L11.5 8.5L18 10L11.5 11.5L10 18L8.5 11.5L2 10L8.5 8.5L10 2Z\" opacity=\"0.95\" />\n <path d=\"M16 3L16.5 5L18.5 5.5L16.5 6L16 8L15.5 6L13.5 5.5L15.5 5L16 3Z\" opacity=\"0.6\" />\n </svg>\n );\n}\n\nfunction CloseIcon(): JSX.Element {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ import type { ChatMessage, ModelInterfaceTool } from '../../api/types';
2
+ /**
3
+ * Behavior when the trigger is activated.
4
+ * - 'inline': open a floating tooltip with an inline AI response
5
+ * - 'drawer': add the element as a reference and open the registered ChatDrawer
6
+ */
7
+ export type AIElementWrapperBehavior = 'inline' | 'drawer';
8
+ /**
9
+ * When to show the trigger.
10
+ * - 'hover': show while the wrapper is hovered
11
+ * - 'click': show while the inline tooltip is open (otherwise hidden)
12
+ * - 'always': always visible
13
+ * - 'select': show only while the user has selected text inside the wrapper
14
+ */
15
+ export type AIElementWrapperShowOn = 'hover' | 'click' | 'always' | 'select';
16
+ /**
17
+ * Tooltip placement (also used for trigger placement).
18
+ */
19
+ export type AIElementWrapperPlacement = 'top' | 'bottom' | 'left' | 'right';
20
+ /**
21
+ * Options for AIElementWrapper.
22
+ */
23
+ export interface AIElementWrapperOptions {
24
+ /**
25
+ * When the trigger pill becomes visible.
26
+ * @default 'hover'
27
+ */
28
+ showOn?: AIElementWrapperShowOn;
29
+ /**
30
+ * Position of the trigger pill relative to the wrapped element.
31
+ * @default 'bottom'
32
+ */
33
+ triggerPlacement?: AIElementWrapperPlacement;
34
+ /**
35
+ * Position of the inline response tooltip.
36
+ * @default 'bottom'
37
+ */
38
+ tooltipPlacement?: AIElementWrapperPlacement;
39
+ /**
40
+ * Tooltip width when behavior='inline'.
41
+ * @default 360
42
+ */
43
+ tooltipWidth?: number | string;
44
+ /**
45
+ * Label shown inside the default trigger pill.
46
+ * @default 'Preguntar a IA'
47
+ */
48
+ triggerLabel?: string;
49
+ /**
50
+ * Whether to highlight the wrapped content while interacting.
51
+ * @default true
52
+ */
53
+ highlightOnInteract?: boolean;
54
+ /**
55
+ * Z-index for trigger and tooltip overlays.
56
+ * @default 9999
57
+ */
58
+ zIndex?: number;
59
+ /**
60
+ * Primary color used for the trigger gradient and tooltip accents.
61
+ */
62
+ color?: string;
63
+ /**
64
+ * Border radius for the trigger pill and tooltip.
65
+ * @default 999
66
+ */
67
+ triggerBorderRadius?: number | string;
68
+ /**
69
+ * Prefix prepended to the user message when behavior='drawer'.
70
+ * Receives the list of reference labels.
71
+ * Default: "Elemento referenciado: <labels>"
72
+ */
73
+ drawerPromptPrefix?: (labels: string[]) => string;
74
+ /**
75
+ * Default prompt used when behavior='inline' and no getPrompt is provided.
76
+ */
77
+ defaultInlinePrompt?: string;
78
+ }
79
+ /**
80
+ * Props for AIElementWrapper.
81
+ */
82
+ export interface AIElementWrapperProps {
83
+ /**
84
+ * Short label that identifies the element (used as chip text in drawer mode
85
+ * and as default prompt fallback).
86
+ */
87
+ label: string;
88
+ /**
89
+ * Optional structured data describing the element.
90
+ * Passed to getPrompt() and stored on the reference.
91
+ */
92
+ data?: Record<string, any>;
93
+ /**
94
+ * Optional rich content describing the element. Stored on the reference
95
+ * so the chat can render it later.
96
+ */
97
+ referenceContent?: React.ReactNode;
98
+ /**
99
+ * Behavior when the trigger is clicked.
100
+ * @default 'inline'
101
+ */
102
+ behavior?: AIElementWrapperBehavior;
103
+ /**
104
+ * Custom trigger node. When provided, replaces the default pill.
105
+ * Click handler is attached automatically.
106
+ */
107
+ trigger?: React.ReactNode;
108
+ /**
109
+ * Display options.
110
+ */
111
+ options?: AIElementWrapperOptions;
112
+ /**
113
+ * Assistant ID used when behavior='inline'. Required in that mode.
114
+ */
115
+ assistantId?: string;
116
+ /**
117
+ * Builds the prompt sent to the inline assistant from the data prop.
118
+ * Receives `data` and `label` as inputs.
119
+ */
120
+ getPrompt?: (args: {
121
+ data?: Record<string, any>;
122
+ label: string;
123
+ }) => string;
124
+ /**
125
+ * API key (overrides DevicProvider).
126
+ */
127
+ apiKey?: string;
128
+ /**
129
+ * Base URL (overrides DevicProvider).
130
+ */
131
+ baseUrl?: string;
132
+ /**
133
+ * Tenant ID (overrides DevicProvider).
134
+ */
135
+ tenantId?: string;
136
+ /**
137
+ * Tenant metadata (merged with DevicProvider metadata).
138
+ */
139
+ tenantMetadata?: Record<string, any>;
140
+ /**
141
+ * Client-side tools for model interface protocol (inline mode only).
142
+ */
143
+ modelInterfaceTools?: ModelInterfaceTool[];
144
+ /**
145
+ * Custom renderer for the inline response. Defaults to plain text.
146
+ */
147
+ inlineRenderer?: (message: ChatMessage) => React.ReactNode;
148
+ /**
149
+ * Called when the trigger is activated.
150
+ */
151
+ onActivate?: () => void;
152
+ /**
153
+ * Called when the inline response completes.
154
+ */
155
+ onInlineResponse?: (message: ChatMessage) => void;
156
+ /**
157
+ * Called when an error occurs during inline generation.
158
+ */
159
+ onError?: (error: Error) => void;
160
+ /**
161
+ * Wrapper className.
162
+ */
163
+ className?: string;
164
+ /**
165
+ * Inline style merged into the wrapper container.
166
+ */
167
+ style?: React.CSSProperties;
168
+ /**
169
+ * The element to wrap.
170
+ */
171
+ children: React.ReactNode;
172
+ }
173
+ /**
174
+ * Imperative handle for AIElementWrapper.
175
+ */
176
+ export interface AIElementWrapperHandle {
177
+ /**
178
+ * Programmatically activate the trigger.
179
+ */
180
+ activate: () => void;
181
+ /**
182
+ * Close the inline tooltip (no-op for drawer mode).
183
+ */
184
+ close: () => void;
185
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Module-level singleton that coordinates which AIElementWrapper instance
3
+ * currently owns the floating trigger. Ensures only one wrapper shows its
4
+ * trigger at any given time across the page.
5
+ */
6
+ type Listener = (activeId: string | null) => void;
7
+ export declare function getActiveWrapper(): string | null;
8
+ export declare function setActiveWrapper(id: string | null): void;
9
+ export declare function subscribeActiveWrapper(listener: Listener): () => void;
10
+ export {};
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Module-level singleton that coordinates which AIElementWrapper instance
3
+ * currently owns the floating trigger. Ensures only one wrapper shows its
4
+ * trigger at any given time across the page.
5
+ */
6
+ let activeId = null;
7
+ const listeners = new Set();
8
+ function getActiveWrapper() {
9
+ return activeId;
10
+ }
11
+ function setActiveWrapper(id) {
12
+ if (activeId === id)
13
+ return;
14
+ activeId = id;
15
+ for (const l of listeners)
16
+ l(activeId);
17
+ }
18
+ function subscribeActiveWrapper(listener) {
19
+ listeners.add(listener);
20
+ return () => {
21
+ listeners.delete(listener);
22
+ };
23
+ }
24
+
25
+ export { getActiveWrapper, setActiveWrapper, subscribeActiveWrapper };
26
+ //# sourceMappingURL=activeWrapperRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activeWrapperRegistry.js","sources":["../../../../src/components/AIElementWrapper/activeWrapperRegistry.ts"],"sourcesContent":["/**\n * Module-level singleton that coordinates which AIElementWrapper instance\n * currently owns the floating trigger. Ensures only one wrapper shows its\n * trigger at any given time across the page.\n */\n\ntype Listener = (activeId: string | null) => void;\n\nlet activeId: string | null = null;\nconst listeners = new Set<Listener>();\n\nexport function getActiveWrapper(): string | null {\n return activeId;\n}\n\nexport function setActiveWrapper(id: string | null): void {\n if (activeId === id) return;\n activeId = id;\n for (const l of listeners) l(activeId);\n}\n\nexport function subscribeActiveWrapper(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n"],"names":[],"mappings":"AAAA;;;;AAIG;AAIH,IAAI,QAAQ,GAAkB,IAAI;AAClC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY;SAErB,gBAAgB,GAAA;AAC9B,IAAA,OAAO,QAAQ;AACjB;AAEM,SAAU,gBAAgB,CAAC,EAAiB,EAAA;IAChD,IAAI,QAAQ,KAAK,EAAE;QAAE;IACrB,QAAQ,GAAG,EAAE;IACb,KAAK,MAAM,CAAC,IAAI,SAAS;QAAE,CAAC,CAAC,QAAQ,CAAC;AACxC;AAEM,SAAU,sBAAsB,CAAC,QAAkB,EAAA;AACvD,IAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;AACvB,IAAA,OAAO,MAAK;AACV,QAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;AAC5B,IAAA,CAAC;AACH;;;;"}
@@ -0,0 +1,3 @@
1
+ export { AIElementWrapper } from './AIElementWrapper';
2
+ export { useAIElementWrapper } from './useAIElementWrapper';
3
+ export type { AIElementWrapperProps, AIElementWrapperOptions, AIElementWrapperHandle, AIElementWrapperBehavior, AIElementWrapperShowOn, AIElementWrapperPlacement, } from './AIElementWrapper.types';
@@ -0,0 +1,26 @@
1
+ import type { ChatMessage, ModelInterfaceTool } from '../../api/types';
2
+ export interface UseAIElementWrapperOptions {
3
+ assistantId?: string;
4
+ apiKey?: string;
5
+ baseUrl?: string;
6
+ tenantId?: string;
7
+ tenantMetadata?: Record<string, any>;
8
+ modelInterfaceTools?: ModelInterfaceTool[];
9
+ onResponse?: (message: ChatMessage) => void;
10
+ onError?: (error: Error) => void;
11
+ }
12
+ export interface UseAIElementWrapperResult {
13
+ isProcessing: boolean;
14
+ response: ChatMessage | null;
15
+ error: Error | null;
16
+ /** Send a prompt to the inline assistant. */
17
+ sendInlinePrompt: (prompt: string) => Promise<void>;
18
+ /** Reset response/error state. */
19
+ reset: () => void;
20
+ }
21
+ /**
22
+ * Hook that manages the inline AI generation flow for AIElementWrapper.
23
+ * Reuses sendMessageAsync + polling, similar to useAIGenerationButton but
24
+ * without the modal/tooltip UI orchestration.
25
+ */
26
+ export declare function useAIElementWrapper(options: UseAIElementWrapperOptions): UseAIElementWrapperResult;