@conduit-d365/ai-chat 0.1.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.
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { AIChatConfig, UseAIChatReturn, UsePageContextReturn } from './types';
3
+ export interface AIChatProviderProps extends AIChatConfig {
4
+ children: ReactNode;
5
+ /**
6
+ * Optional async function that returns a Bearer token.
7
+ * Typically pass getAccessToken from @conduit-d365/auth's useAuth().
8
+ * If omitted, requests are sent without an Authorization header.
9
+ */
10
+ getAccessToken?: () => Promise<string>;
11
+ }
12
+ export declare function AIChatProvider({ children, appName, endpoint, placeholder: _placeholder, maxHistory, getAccessToken, }: AIChatProviderProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function useAIChat(): UseAIChatReturn;
14
+ export declare function usePageContext(): UsePageContextReturn;
15
+ //# sourceMappingURL=AIChatProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIChatProvider.d.ts","sourceRoot":"","sources":["../src/AIChatProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,EACV,YAAY,EAIZ,eAAe,EACf,oBAAoB,EACrB,MAAM,SAAS,CAAA;AAUhB,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACvC;AAOD,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,OAAO,EACP,QAAyB,EACzB,WAAW,EAAE,YAAY,EACzB,UAAe,EACf,cAAc,GACf,EAAE,mBAAmB,2CAgMrB;AAID,wBAAgB,SAAS,IAAI,eAAe,CAM3C;AAED,wBAAgB,cAAc,IAAI,oBAAoB,CAMrD"}
@@ -0,0 +1,183 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useRef, useState, } from 'react';
3
+ // ── Contexts ──────────────────────────────────────────────────────────
4
+ const AIChatContext = createContext(null);
5
+ const PageContextContext = createContext(null);
6
+ const GetTokenContext = createContext(null);
7
+ let nextId = 0;
8
+ function generateId() {
9
+ return `msg_${Date.now()}_${nextId++}`;
10
+ }
11
+ export function AIChatProvider({ children, appName, endpoint = '/api/ai/chat', placeholder: _placeholder, maxHistory = 50, getAccessToken, }) {
12
+ const [messages, setMessages] = useState([]);
13
+ const [isStreaming, setIsStreaming] = useState(false);
14
+ const [isOpen, setIsOpen] = useState(false);
15
+ const [error, setError] = useState(null);
16
+ const [pageContext, setPageContext] = useState(null);
17
+ // Ref to abort in-flight requests
18
+ const abortRef = useRef(null);
19
+ const sendMessage = useCallback(async (content) => {
20
+ if (!content.trim() || isStreaming)
21
+ return;
22
+ const userMessage = {
23
+ id: generateId(),
24
+ role: 'user',
25
+ content: content.trim(),
26
+ timestamp: Date.now(),
27
+ };
28
+ setMessages((prev) => [...prev, userMessage]);
29
+ setError(null);
30
+ setIsStreaming(true);
31
+ // Build the assistant placeholder
32
+ const assistantId = generateId();
33
+ const assistantMessage = {
34
+ id: assistantId,
35
+ role: 'assistant',
36
+ content: '',
37
+ timestamp: Date.now(),
38
+ };
39
+ setMessages((prev) => [...prev, assistantMessage]);
40
+ try {
41
+ abortRef.current = new AbortController();
42
+ // Build history (trim to maxHistory)
43
+ const history = [...messages, userMessage].slice(-maxHistory);
44
+ const body = {
45
+ message: content.trim(),
46
+ history,
47
+ pageContext,
48
+ appName,
49
+ };
50
+ const headers = {
51
+ 'Content-Type': 'application/json',
52
+ };
53
+ if (getAccessToken) {
54
+ try {
55
+ const token = await getAccessToken();
56
+ headers['Authorization'] = `Bearer ${token}`;
57
+ }
58
+ catch {
59
+ // If token acquisition fails, proceed without auth
60
+ // (backend will reject with 401 if auth is required)
61
+ }
62
+ }
63
+ const response = await fetch(endpoint, {
64
+ method: 'POST',
65
+ headers,
66
+ body: JSON.stringify(body),
67
+ signal: abortRef.current.signal,
68
+ });
69
+ if (!response.ok) {
70
+ const errorText = await response.text().catch(() => response.statusText);
71
+ throw new Error(response.status === 401
72
+ ? 'Authentication required. Please sign in.'
73
+ : response.status === 429
74
+ ? 'Rate limit exceeded. Please wait a moment.'
75
+ : `Chat request failed: ${errorText}`);
76
+ }
77
+ // Stream SSE response
78
+ const reader = response.body?.getReader();
79
+ if (!reader)
80
+ throw new Error('No response body');
81
+ const decoder = new TextDecoder();
82
+ let buffer = '';
83
+ while (true) {
84
+ const { done, value } = await reader.read();
85
+ if (done)
86
+ break;
87
+ buffer += decoder.decode(value, { stream: true });
88
+ // Process SSE lines
89
+ const lines = buffer.split('\n');
90
+ buffer = lines.pop() ?? '';
91
+ for (const line of lines) {
92
+ if (line.startsWith('data: ')) {
93
+ const data = line.slice(6);
94
+ if (data === '[DONE]')
95
+ break;
96
+ try {
97
+ const parsed = JSON.parse(data);
98
+ if (parsed.error) {
99
+ throw new Error(parsed.error);
100
+ }
101
+ if (parsed.content) {
102
+ setMessages((prev) => prev.map((m) => m.id === assistantId
103
+ ? { ...m, content: m.content + parsed.content }
104
+ : m));
105
+ }
106
+ }
107
+ catch (e) {
108
+ if (e instanceof SyntaxError) {
109
+ // Not JSON — treat raw data as content chunk
110
+ setMessages((prev) => prev.map((m) => m.id === assistantId
111
+ ? { ...m, content: m.content + data }
112
+ : m));
113
+ }
114
+ else {
115
+ throw e;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ catch (err) {
123
+ if (err.name === 'AbortError')
124
+ return;
125
+ const errorMessage = err instanceof Error ? err.message : 'An error occurred';
126
+ setError(errorMessage);
127
+ // Remove the empty assistant message on error
128
+ setMessages((prev) => {
129
+ const last = prev[prev.length - 1];
130
+ if (last?.id === assistantId && !last.content) {
131
+ return prev.slice(0, -1);
132
+ }
133
+ return prev;
134
+ });
135
+ }
136
+ finally {
137
+ setIsStreaming(false);
138
+ abortRef.current = null;
139
+ }
140
+ }, [isStreaming, messages, pageContext, appName, endpoint, maxHistory, getAccessToken]);
141
+ const clearHistory = useCallback(() => {
142
+ if (abortRef.current)
143
+ abortRef.current.abort();
144
+ setMessages([]);
145
+ setError(null);
146
+ setIsStreaming(false);
147
+ }, []);
148
+ const toggle = useCallback(() => setIsOpen((v) => !v), []);
149
+ const open = useCallback(() => setIsOpen(true), []);
150
+ const close = useCallback(() => setIsOpen(false), []);
151
+ const chatValue = {
152
+ messages,
153
+ isStreaming,
154
+ isOpen,
155
+ error,
156
+ sendMessage,
157
+ clearHistory,
158
+ toggle,
159
+ open,
160
+ close,
161
+ };
162
+ const pageContextValue = {
163
+ pageContext,
164
+ setPageContext,
165
+ };
166
+ return (_jsx(GetTokenContext.Provider, { value: getAccessToken ?? null, children: _jsx(PageContextContext.Provider, { value: pageContextValue, children: _jsx(AIChatContext.Provider, { value: chatValue, children: children }) }) }));
167
+ }
168
+ // ── Hooks ─────────────────────────────────────────────────────────────
169
+ export function useAIChat() {
170
+ const context = useContext(AIChatContext);
171
+ if (!context) {
172
+ throw new Error('useAIChat must be used within an AIChatProvider');
173
+ }
174
+ return context;
175
+ }
176
+ export function usePageContext() {
177
+ const context = useContext(PageContextContext);
178
+ if (!context) {
179
+ throw new Error('usePageContext must be used within an AIChatProvider');
180
+ }
181
+ return context;
182
+ }
183
+ //# sourceMappingURL=AIChatProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIChatProvider.js","sourceRoot":"","sources":["../src/AIChatProvider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface ChatSidebarProps {
2
+ /** Width of the sidebar in pixels. Defaults to 400. */
3
+ width?: number;
4
+ /** Which side to render on. Defaults to "right". */
5
+ position?: 'left' | 'right';
6
+ /** Title displayed in the sidebar header. Defaults to "Conduit AI". */
7
+ title?: string;
8
+ }
9
+ export declare function ChatSidebar({ width, position, title, }: ChatSidebarProps): import("react/jsx-runtime").JSX.Element | null;
10
+ //# sourceMappingURL=ChatSidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatSidebar.d.ts","sourceRoot":"","sources":["../src/ChatSidebar.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAmDD,wBAAgB,WAAW,CAAC,EAC1B,KAAW,EACX,QAAkB,EAClB,KAAoB,GACrB,EAAE,gBAAgB,kDAmSlB"}
@@ -0,0 +1,187 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { useAIChat, usePageContext } from './AIChatProvider';
4
+ import { MessageBubble } from './components/MessageBubble';
5
+ import { ContextBadge } from './components/ContextBadge';
6
+ /**
7
+ * CSS keyframes injected once into the document head.
8
+ * Uses a data-attribute guard to avoid duplicate injection.
9
+ */
10
+ const STYLE_ID = 'conduit-ai-chat-styles';
11
+ function ensureStyles() {
12
+ if (typeof document === 'undefined')
13
+ return;
14
+ if (document.getElementById(STYLE_ID))
15
+ return;
16
+ const style = document.createElement('style');
17
+ style.id = STYLE_ID;
18
+ style.textContent = `
19
+ @keyframes conduit-ai-dot {
20
+ 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
21
+ 40% { opacity: 1; transform: scale(1); }
22
+ }
23
+ @keyframes conduit-ai-blink {
24
+ 0%, 100% { opacity: 1; }
25
+ 50% { opacity: 0; }
26
+ }
27
+ @keyframes conduit-ai-slide-in-right {
28
+ from { transform: translateX(100%); }
29
+ to { transform: translateX(0); }
30
+ }
31
+ @keyframes conduit-ai-slide-in-left {
32
+ from { transform: translateX(-100%); }
33
+ to { transform: translateX(0); }
34
+ }
35
+ .conduit-ai-sidebar-enter-right {
36
+ animation: conduit-ai-slide-in-right 0.2s ease-out;
37
+ }
38
+ .conduit-ai-sidebar-enter-left {
39
+ animation: conduit-ai-slide-in-left 0.2s ease-out;
40
+ }
41
+ .conduit-ai-input:focus {
42
+ outline: 2px solid #FF831D;
43
+ outline-offset: -2px;
44
+ }
45
+ .conduit-ai-send-btn:hover:not(:disabled) {
46
+ background-color: #e6720f !important;
47
+ }
48
+ .conduit-ai-clear-btn:hover {
49
+ background-color: var(--conduit-bg-muted, #f1f5f9) !important;
50
+ }
51
+ `;
52
+ document.head.appendChild(style);
53
+ }
54
+ export function ChatSidebar({ width = 400, position = 'right', title = 'Conduit AI', }) {
55
+ const { messages, isStreaming, isOpen, error, sendMessage, clearHistory, close } = useAIChat();
56
+ const { pageContext } = usePageContext();
57
+ const [input, setInput] = useState('');
58
+ const messagesEndRef = useRef(null);
59
+ const inputRef = useRef(null);
60
+ useEffect(() => ensureStyles(), []);
61
+ // Auto-scroll to bottom on new messages
62
+ useEffect(() => {
63
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
64
+ }, [messages]);
65
+ // Focus input when sidebar opens
66
+ useEffect(() => {
67
+ if (isOpen) {
68
+ setTimeout(() => inputRef.current?.focus(), 200);
69
+ }
70
+ }, [isOpen]);
71
+ const handleSubmit = useCallback(() => {
72
+ if (!input.trim() || isStreaming)
73
+ return;
74
+ sendMessage(input);
75
+ setInput('');
76
+ }, [input, isStreaming, sendMessage]);
77
+ const handleKeyDown = useCallback((e) => {
78
+ if (e.key === 'Enter' && !e.shiftKey) {
79
+ e.preventDefault();
80
+ handleSubmit();
81
+ }
82
+ }, [handleSubmit]);
83
+ if (!isOpen)
84
+ return null;
85
+ const isLeft = position === 'left';
86
+ return (_jsxs("div", { className: isLeft ? 'conduit-ai-sidebar-enter-left' : 'conduit-ai-sidebar-enter-right', style: {
87
+ position: 'fixed',
88
+ top: 0,
89
+ [position]: 0,
90
+ width: `${width}px`,
91
+ maxWidth: '100vw',
92
+ height: '100vh',
93
+ display: 'flex',
94
+ flexDirection: 'column',
95
+ backgroundColor: 'var(--conduit-ai-sidebar-bg, #ffffff)',
96
+ borderLeft: isLeft ? 'none' : '1px solid var(--conduit-border, #e2e8f0)',
97
+ borderRight: isLeft ? '1px solid var(--conduit-border, #e2e8f0)' : 'none',
98
+ zIndex: 1000,
99
+ fontFamily: "'Inter', system-ui, -apple-system, sans-serif",
100
+ color: 'var(--conduit-fg, #334155)',
101
+ }, children: [_jsxs("div", { style: {
102
+ display: 'flex',
103
+ alignItems: 'center',
104
+ justifyContent: 'space-between',
105
+ padding: '12px 16px',
106
+ borderBottom: '1px solid var(--conduit-border, #e2e8f0)',
107
+ flexShrink: 0,
108
+ }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: [_jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z", fill: "#02018C" }), _jsx("path", { d: "M8 10.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM16 10.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z", fill: "#FFFFFF" }), _jsx("path", { d: "M12 17c-2.21 0-4-1.34-4-3h8c0 1.66-1.79 3-4 3z", fill: "#FF831D" })] }), _jsx("span", { style: { fontWeight: 600, fontSize: '15px' }, children: title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '4px' }, children: [messages.length > 0 && (_jsx("button", { onClick: clearHistory, className: "conduit-ai-clear-btn", title: "Clear chat", style: {
109
+ background: 'none',
110
+ border: 'none',
111
+ cursor: 'pointer',
112
+ padding: '4px 8px',
113
+ borderRadius: '6px',
114
+ fontSize: '12px',
115
+ color: 'var(--conduit-fg, #64748b)',
116
+ }, children: "Clear" })), _jsx("button", { onClick: close, title: "Close", style: {
117
+ background: 'none',
118
+ border: 'none',
119
+ cursor: 'pointer',
120
+ padding: '4px',
121
+ borderRadius: '6px',
122
+ color: 'var(--conduit-fg, #64748b)',
123
+ fontSize: '18px',
124
+ lineHeight: 1,
125
+ }, children: "\u00D7" })] })] }), _jsx(ContextBadge, { context: pageContext }), _jsxs("div", { style: {
126
+ flex: 1,
127
+ overflowY: 'auto',
128
+ padding: '16px',
129
+ }, children: [messages.length === 0 && (_jsxs("div", { style: {
130
+ textAlign: 'center',
131
+ padding: '48px 24px',
132
+ color: 'var(--conduit-fg, #94a3b8)',
133
+ fontSize: '14px',
134
+ }, children: [_jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", style: { margin: '0 auto 12px', opacity: 0.5 }, children: _jsx("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z", fill: "currentColor" }) }), _jsx("p", { style: { margin: '0 0 4px', fontWeight: 500 }, children: "Ask me anything" }), _jsx("p", { style: { margin: 0, fontSize: '13px' }, children: "I can help with D365 entity mapping, metadata queries, and transformation scripts." })] })), messages.map((message, index) => (_jsx(MessageBubble, { message: message, isStreaming: isStreaming && index === messages.length - 1 && message.role === 'assistant' }, message.id))), error && (_jsx("div", { style: {
135
+ margin: '8px 0',
136
+ padding: '8px 12px',
137
+ borderRadius: '8px',
138
+ fontSize: '13px',
139
+ backgroundColor: '#fef2f2',
140
+ color: '#b91c1c',
141
+ border: '1px solid #fecaca',
142
+ }, children: error })), _jsx("div", { ref: messagesEndRef })] }), _jsxs("div", { style: {
143
+ padding: '12px 16px',
144
+ borderTop: '1px solid var(--conduit-border, #e2e8f0)',
145
+ flexShrink: 0,
146
+ }, children: [_jsxs("div", { style: {
147
+ display: 'flex',
148
+ gap: '8px',
149
+ alignItems: 'flex-end',
150
+ }, children: [_jsx("textarea", { ref: inputRef, className: "conduit-ai-input", value: input, onChange: (e) => setInput(e.target.value), onKeyDown: handleKeyDown, placeholder: "Ask about D365 entities, mappings...", disabled: isStreaming, rows: 1, style: {
151
+ flex: 1,
152
+ resize: 'none',
153
+ border: '1px solid var(--conduit-border, #e2e8f0)',
154
+ borderRadius: '8px',
155
+ padding: '8px 12px',
156
+ fontSize: '14px',
157
+ lineHeight: '1.4',
158
+ fontFamily: 'inherit',
159
+ backgroundColor: 'var(--conduit-ai-input-bg, #ffffff)',
160
+ color: 'var(--conduit-fg, #334155)',
161
+ maxHeight: '120px',
162
+ overflowY: 'auto',
163
+ }, onInput: (e) => {
164
+ const el = e.currentTarget;
165
+ el.style.height = 'auto';
166
+ el.style.height = Math.min(el.scrollHeight, 120) + 'px';
167
+ } }), _jsx("button", { className: "conduit-ai-send-btn", onClick: handleSubmit, disabled: !input.trim() || isStreaming, title: "Send message", style: {
168
+ flexShrink: 0,
169
+ width: '36px',
170
+ height: '36px',
171
+ borderRadius: '8px',
172
+ border: 'none',
173
+ cursor: input.trim() && !isStreaming ? 'pointer' : 'default',
174
+ backgroundColor: input.trim() && !isStreaming ? '#FF831D' : '#cbd5e1',
175
+ color: '#ffffff',
176
+ display: 'flex',
177
+ alignItems: 'center',
178
+ justifyContent: 'center',
179
+ transition: 'background-color 0.15s',
180
+ }, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M2.5 8H13.5M13.5 8L9 3.5M13.5 8L9 12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), _jsx("div", { style: {
181
+ marginTop: '6px',
182
+ fontSize: '11px',
183
+ color: 'var(--conduit-fg, #94a3b8)',
184
+ opacity: 0.6,
185
+ }, children: "Shift+Enter for new line" })] })] }));
186
+ }
187
+ //# sourceMappingURL=ChatSidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatSidebar.js","sourceRoot":"","sources":["../src/ChatSidebar.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface ChatToggleProps {
2
+ /** Which corner to place the toggle button. Defaults to "bottom-right". */
3
+ position?: 'bottom-right' | 'bottom-left';
4
+ /** Distance from the edge in pixels. Defaults to 24. */
5
+ offset?: number;
6
+ }
7
+ export declare function ChatToggle({ position, offset, }: ChatToggleProps): import("react/jsx-runtime").JSX.Element | null;
8
+ //# sourceMappingURL=ChatToggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatToggle.d.ts","sourceRoot":"","sources":["../src/ChatToggle.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,CAAA;IACzC,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAsBD,wBAAgB,UAAU,CAAC,EACzB,QAAyB,EACzB,MAAW,GACZ,EAAE,eAAe,kDA8CjB"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect } from 'react';
3
+ import { useAIChat } from './AIChatProvider';
4
+ const TOGGLE_STYLE_ID = 'conduit-ai-toggle-styles';
5
+ function ensureToggleStyles() {
6
+ if (typeof document === 'undefined')
7
+ return;
8
+ if (document.getElementById(TOGGLE_STYLE_ID))
9
+ return;
10
+ const style = document.createElement('style');
11
+ style.id = TOGGLE_STYLE_ID;
12
+ style.textContent = `
13
+ .conduit-ai-toggle:hover {
14
+ transform: scale(1.08);
15
+ box-shadow: 0 6px 20px rgba(255, 131, 29, 0.4) !important;
16
+ }
17
+ .conduit-ai-toggle:active {
18
+ transform: scale(0.95);
19
+ }
20
+ `;
21
+ document.head.appendChild(style);
22
+ }
23
+ export function ChatToggle({ position = 'bottom-right', offset = 24, }) {
24
+ const { toggle, isOpen } = useAIChat();
25
+ useEffect(() => ensureToggleStyles(), []);
26
+ // Hide the toggle when the sidebar is open
27
+ if (isOpen)
28
+ return null;
29
+ const isRight = position === 'bottom-right';
30
+ return (_jsx("button", { className: "conduit-ai-toggle", onClick: toggle, title: "Open AI chat", style: {
31
+ position: 'fixed',
32
+ bottom: `${offset}px`,
33
+ [isRight ? 'right' : 'left']: `${offset}px`,
34
+ width: '52px',
35
+ height: '52px',
36
+ borderRadius: '50%',
37
+ border: 'none',
38
+ cursor: 'pointer',
39
+ backgroundColor: '#FF831D',
40
+ color: '#ffffff',
41
+ boxShadow: '0 4px 12px rgba(255, 131, 29, 0.3)',
42
+ display: 'flex',
43
+ alignItems: 'center',
44
+ justifyContent: 'center',
45
+ zIndex: 999,
46
+ transition: 'transform 0.15s, box-shadow 0.15s',
47
+ }, children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2z", fill: "currentColor" }), _jsx("circle", { cx: "8", cy: "10", r: "1", fill: "#02018C" }), _jsx("circle", { cx: "12", cy: "10", r: "1", fill: "#02018C" }), _jsx("circle", { cx: "16", cy: "10", r: "1", fill: "#02018C" })] }) }));
48
+ }
49
+ //# sourceMappingURL=ChatToggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatToggle.js","sourceRoot":"","sources":["../src/ChatToggle.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAS5C,MAAM,eAAe,GAAG,0BAA0B,CAAA;AAElD,SAAS,kBAAkB;IACzB,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAM;IAC3C,IAAI,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC;QAAE,OAAM;IAEpD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IAC7C,KAAK,CAAC,EAAE,GAAG,eAAe,CAAA;IAC1B,KAAK,CAAC,WAAW,GAAG;;;;;;;;GAQnB,CAAA;IACD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,GAAG,cAAc,EACzB,MAAM,GAAG,EAAE,GACK;IAChB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAA;IAEtC,SAAS,CAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,EAAE,EAAE,CAAC,CAAA;IAEzC,2CAA2C;IAC3C,IAAI,MAAM;QAAE,OAAO,IAAI,CAAA;IAEvB,MAAM,OAAO,GAAG,QAAQ,KAAK,cAAc,CAAA;IAE3C,OAAO,CACL,iBACE,SAAS,EAAC,mBAAmB,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAC,cAAc,EACpB,KAAK,EAAE;YACL,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,GAAG,MAAM,IAAI;YACrB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,IAAI;YAC3C,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,MAAM;YACd,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,SAAS;YACjB,eAAe,EAAE,SAAS;YAC1B,KAAK,EAAE,SAAS;YAChB,SAAS,EAAE,oCAAoC;YAC/C,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,mCAAmC;SAChD,YAGD,eAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,aACzD,eACE,CAAC,EAAC,mEAAmE,EACrE,IAAI,EAAC,cAAc,GACnB,EACF,iBAAQ,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,EAAC,IAAI,EAAC,SAAS,GAAG,EAC9C,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,EAAC,IAAI,EAAC,SAAS,GAAG,EAC/C,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,EAAC,IAAI,EAAC,SAAS,GAAG,IAC3C,GACC,CACV,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { PageContext } from '../types';
2
+ export interface ContextBadgeProps {
3
+ context: PageContext | null;
4
+ }
5
+ export declare function ContextBadge({ context }: ContextBadgeProps): import("react/jsx-runtime").JSX.Element | null;
6
+ //# sourceMappingURL=ContextBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextBadge.d.ts","sourceRoot":"","sources":["../../src/components/ContextBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;CAC5B;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,iBAAiB,kDAmD1D"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function ContextBadge({ context }) {
3
+ if (!context)
4
+ return null;
5
+ const label = context.entity
6
+ ? `Viewing: ${context.entity}`
7
+ : context.summary
8
+ ? context.summary
9
+ : `Page: ${context.view}`;
10
+ return (_jsxs("div", { style: {
11
+ display: 'flex',
12
+ alignItems: 'center',
13
+ gap: '6px',
14
+ padding: '6px 12px',
15
+ fontSize: '12px',
16
+ color: 'var(--conduit-fg, #64748b)',
17
+ backgroundColor: 'var(--conduit-bg-muted, #f1f5f9)',
18
+ borderBottom: '1px solid var(--conduit-border, #e2e8f0)',
19
+ }, children: [_jsxs("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", style: { flexShrink: 0, opacity: 0.7 }, children: [_jsx("circle", { cx: "8", cy: "8", r: "6", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }), _jsx("circle", { cx: "8", cy: "8", r: "2", fill: "#FF831D" })] }), _jsx("span", { style: {
20
+ overflow: 'hidden',
21
+ textOverflow: 'ellipsis',
22
+ whiteSpace: 'nowrap',
23
+ }, children: label })] }));
24
+ }
25
+ //# sourceMappingURL=ContextBadge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextBadge.js","sourceRoot":"","sources":["../../src/components/ContextBadge.tsx"],"names":[],"mappings":";AAMA,MAAM,UAAU,YAAY,CAAC,EAAE,OAAO,EAAqB;IACzD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM;QAC1B,CAAC,CAAC,YAAY,OAAO,CAAC,MAAM,EAAE;QAC9B,CAAC,CAAC,OAAO,CAAC,OAAO;YACf,CAAC,CAAC,OAAO,CAAC,OAAO;YACjB,CAAC,CAAC,SAAS,OAAO,CAAC,IAAI,EAAE,CAAA;IAE7B,OAAO,CACL,eACE,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,QAAQ;YACpB,GAAG,EAAE,KAAK;YACV,OAAO,EAAE,UAAU;YACnB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,4BAA4B;YACnC,eAAe,EAAE,kCAAkC;YACnD,YAAY,EAAE,0CAA0C;SACzD,aAGD,eACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,aAEtC,iBACE,EAAE,EAAC,GAAG,EACN,EAAE,EAAC,GAAG,EACN,CAAC,EAAC,GAAG,EACL,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,KAAK,EACjB,IAAI,EAAC,MAAM,GACX,EACF,iBAAQ,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,CAAC,EAAC,GAAG,EAAC,IAAI,EAAC,SAAS,GAAG,IACzC,EACN,eACE,KAAK,EAAE;oBACL,QAAQ,EAAE,QAAQ;oBAClB,YAAY,EAAE,UAAU;oBACxB,UAAU,EAAE,QAAQ;iBACrB,YAEA,KAAK,GACD,IACH,CACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface MarkdownRendererProps {
2
+ content: string;
3
+ }
4
+ export declare function MarkdownRenderer({ content }: MarkdownRendererProps): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=MarkdownRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownRenderer.d.ts","sourceRoot":"","sources":["../../src/components/MarkdownRenderer.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAA;CAChB;AA4JD,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,EAAE,qBAAqB,2CAQlE"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useCallback } from 'react';
3
+ import ReactMarkdown from 'react-markdown';
4
+ import remarkGfm from 'remark-gfm';
5
+ function CodeBlock({ children, className }) {
6
+ const [copied, setCopied] = useState(false);
7
+ const language = className?.replace('language-', '') ?? '';
8
+ const handleCopy = useCallback(() => {
9
+ navigator.clipboard.writeText(children).then(() => {
10
+ setCopied(true);
11
+ setTimeout(() => setCopied(false), 2000);
12
+ });
13
+ }, [children]);
14
+ return (_jsxs("div", { style: {
15
+ position: 'relative',
16
+ margin: '8px 0',
17
+ borderRadius: '8px',
18
+ overflow: 'hidden',
19
+ border: '1px solid var(--conduit-border, #e2e8f0)',
20
+ }, children: [language && (_jsxs("div", { style: {
21
+ display: 'flex',
22
+ justifyContent: 'space-between',
23
+ alignItems: 'center',
24
+ padding: '4px 12px',
25
+ fontSize: '11px',
26
+ fontWeight: 500,
27
+ color: 'var(--conduit-fg, #64748b)',
28
+ backgroundColor: 'var(--conduit-bg-muted, #f1f5f9)',
29
+ borderBottom: '1px solid var(--conduit-border, #e2e8f0)',
30
+ }, children: [_jsx("span", { children: language }), _jsx("button", { onClick: handleCopy, style: {
31
+ background: 'none',
32
+ border: 'none',
33
+ cursor: 'pointer',
34
+ fontSize: '11px',
35
+ color: 'var(--conduit-fg, #64748b)',
36
+ padding: '2px 6px',
37
+ borderRadius: '4px',
38
+ }, children: copied ? 'Copied!' : 'Copy' })] })), _jsx("pre", { style: {
39
+ margin: 0,
40
+ padding: '12px',
41
+ overflow: 'auto',
42
+ fontSize: '13px',
43
+ lineHeight: '1.5',
44
+ backgroundColor: 'var(--conduit-bg-muted, #f1f5f9)',
45
+ color: 'var(--conduit-fg, #334155)',
46
+ }, children: _jsx("code", { children: children }) })] }));
47
+ }
48
+ const components = {
49
+ code({ children, className, ...props }) {
50
+ // Block code (inside <pre>) vs inline code
51
+ const isInline = !className;
52
+ if (isInline) {
53
+ return (_jsx("code", { style: {
54
+ padding: '2px 5px',
55
+ borderRadius: '4px',
56
+ fontSize: '13px',
57
+ backgroundColor: 'var(--conduit-bg-muted, #f1f5f9)',
58
+ border: '1px solid var(--conduit-border, #e2e8f0)',
59
+ }, ...props, children: children }));
60
+ }
61
+ return _jsx(CodeBlock, { className: className, children: String(children).replace(/\n$/, '') });
62
+ },
63
+ pre({ children }) {
64
+ // react-markdown wraps code blocks in <pre><code>. We handle
65
+ // rendering in the code component above, so just pass through.
66
+ return _jsx(_Fragment, { children: children });
67
+ },
68
+ table({ children }) {
69
+ return (_jsx("div", { style: { overflowX: 'auto', margin: '8px 0' }, children: _jsx("table", { style: {
70
+ borderCollapse: 'collapse',
71
+ width: '100%',
72
+ fontSize: '13px',
73
+ }, children: children }) }));
74
+ },
75
+ th({ children }) {
76
+ return (_jsx("th", { style: {
77
+ padding: '6px 10px',
78
+ textAlign: 'left',
79
+ borderBottom: '2px solid var(--conduit-border, #e2e8f0)',
80
+ fontWeight: 600,
81
+ }, children: children }));
82
+ },
83
+ td({ children }) {
84
+ return (_jsx("td", { style: {
85
+ padding: '6px 10px',
86
+ borderBottom: '1px solid var(--conduit-border, #e2e8f0)',
87
+ }, children: children }));
88
+ },
89
+ p({ children }) {
90
+ return _jsx("p", { style: { margin: '6px 0' }, children: children });
91
+ },
92
+ ul({ children }) {
93
+ return _jsx("ul", { style: { margin: '6px 0', paddingLeft: '20px' }, children: children });
94
+ },
95
+ ol({ children }) {
96
+ return _jsx("ol", { style: { margin: '6px 0', paddingLeft: '20px' }, children: children });
97
+ },
98
+ li({ children }) {
99
+ return _jsx("li", { style: { margin: '2px 0' }, children: children });
100
+ },
101
+ strong({ children }) {
102
+ return _jsx("strong", { style: { fontWeight: 600 }, children: children });
103
+ },
104
+ };
105
+ export function MarkdownRenderer({ content }) {
106
+ return (_jsx("div", { style: { overflowWrap: 'break-word', wordBreak: 'break-word' }, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: components, children: content }) }));
107
+ }
108
+ //# sourceMappingURL=MarkdownRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownRenderer.js","sourceRoot":"","sources":["../../src/components/MarkdownRenderer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ChatMessage } from '../types';
2
+ export interface MessageBubbleProps {
3
+ message: ChatMessage;
4
+ isStreaming?: boolean;
5
+ }
6
+ export declare function MessageBubble({ message, isStreaming }: MessageBubbleProps): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=MessageBubble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../src/components/MessageBubble.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,kBAAkB,2CAiDzE"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MarkdownRenderer } from './MarkdownRenderer';
3
+ export function MessageBubble({ message, isStreaming }) {
4
+ const isUser = message.role === 'user';
5
+ return (_jsx("div", { style: {
6
+ display: 'flex',
7
+ justifyContent: isUser ? 'flex-end' : 'flex-start',
8
+ marginBottom: '12px',
9
+ paddingLeft: isUser ? '32px' : '0',
10
+ paddingRight: isUser ? '0' : '32px',
11
+ }, children: _jsx("div", { style: {
12
+ maxWidth: '100%',
13
+ padding: '10px 14px',
14
+ borderRadius: '12px',
15
+ fontSize: '14px',
16
+ lineHeight: '1.5',
17
+ ...(isUser
18
+ ? {
19
+ backgroundColor: 'var(--conduit-ai-user-bg, #eff6ff)',
20
+ color: 'var(--conduit-ai-user-fg, #1e40af)',
21
+ borderBottomRightRadius: '4px',
22
+ }
23
+ : {
24
+ backgroundColor: 'var(--conduit-ai-assistant-bg, #f8fafc)',
25
+ color: 'var(--conduit-fg, #334155)',
26
+ borderBottomLeftRadius: '4px',
27
+ border: '1px solid var(--conduit-border, #e2e8f0)',
28
+ }),
29
+ }, children: isUser ? (_jsx("span", { style: { whiteSpace: 'pre-wrap' }, children: message.content })) : (_jsxs(_Fragment, { children: [message.content ? (_jsx(MarkdownRenderer, { content: message.content })) : isStreaming ? (_jsx(StreamingDots, {})) : null, isStreaming && message.content && _jsx(StreamingCursor, {})] })) }) }));
30
+ }
31
+ function StreamingDots() {
32
+ return (_jsx("div", { style: { display: 'flex', gap: '4px', padding: '4px 0' }, children: [0, 1, 2].map((i) => (_jsx("div", { style: {
33
+ width: '6px',
34
+ height: '6px',
35
+ borderRadius: '50%',
36
+ backgroundColor: '#FF831D',
37
+ animation: `conduit-ai-dot 1.2s ${i * 0.2}s ease-in-out infinite`,
38
+ } }, i))) }));
39
+ }
40
+ function StreamingCursor() {
41
+ return (_jsx("span", { style: {
42
+ display: 'inline-block',
43
+ width: '2px',
44
+ height: '14px',
45
+ backgroundColor: '#FF831D',
46
+ marginLeft: '2px',
47
+ verticalAlign: 'text-bottom',
48
+ animation: 'conduit-ai-blink 0.8s step-end infinite',
49
+ } }));
50
+ }
51
+ //# sourceMappingURL=MessageBubble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBubble.js","sourceRoot":"","sources":["../../src/components/MessageBubble.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAOrD,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAE,WAAW,EAAsB;IACxE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,CAAA;IAEtC,OAAO,CACL,cACE,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY;YAClD,YAAY,EAAE,MAAM;YACpB,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG;YAClC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM;SACpC,YAED,cACE,KAAK,EAAE;gBACL,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,WAAW;gBACpB,YAAY,EAAE,MAAM;gBACpB,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,KAAK;gBACjB,GAAG,CAAC,MAAM;oBACR,CAAC,CAAC;wBACE,eAAe,EAAE,oCAAoC;wBACrD,KAAK,EAAE,oCAAoC;wBAC3C,uBAAuB,EAAE,KAAK;qBAC/B;oBACH,CAAC,CAAC;wBACE,eAAe,EAAE,yCAAyC;wBAC1D,KAAK,EAAE,4BAA4B;wBACnC,sBAAsB,EAAE,KAAK;wBAC7B,MAAM,EAAE,0CAA0C;qBACnD,CAAC;aACP,YAEA,MAAM,CAAC,CAAC,CAAC,CACR,eAAM,KAAK,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,YAAG,OAAO,CAAC,OAAO,GAAQ,CAClE,CAAC,CAAC,CAAC,CACF,8BACG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CACjB,KAAC,gBAAgB,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAI,CAC/C,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAChB,KAAC,aAAa,KAAG,CAClB,CAAC,CAAC,CAAC,IAAI,EACP,WAAW,IAAI,OAAO,CAAC,OAAO,IAAI,KAAC,eAAe,KAAG,IACrD,CACJ,GACG,GACF,CACP,CAAA;AACH,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,CACL,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAC1D,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACpB,cAEE,KAAK,EAAE;gBACL,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,KAAK;gBACb,YAAY,EAAE,KAAK;gBACnB,eAAe,EAAE,SAAS;gBAC1B,SAAS,EAAE,uBAAuB,CAAC,GAAG,GAAG,wBAAwB;aAClE,IAPI,CAAC,CAQN,CACH,CAAC,GACE,CACP,CAAA;AACH,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,CACL,eACE,KAAK,EAAE;YACL,OAAO,EAAE,cAAc;YACvB,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,MAAM;YACd,eAAe,EAAE,SAAS;YAC1B,UAAU,EAAE,KAAK;YACjB,aAAa,EAAE,aAAa;YAC5B,SAAS,EAAE,yCAAyC;SACrD,GACD,CACH,CAAA;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ export { AIChatProvider, useAIChat, usePageContext } from './AIChatProvider';
2
+ export type { AIChatProviderProps } from './AIChatProvider';
3
+ export { ChatSidebar } from './ChatSidebar';
4
+ export type { ChatSidebarProps } from './ChatSidebar';
5
+ export { ChatToggle } from './ChatToggle';
6
+ export type { ChatToggleProps } from './ChatToggle';
7
+ export { MessageBubble } from './components/MessageBubble';
8
+ export type { MessageBubbleProps } from './components/MessageBubble';
9
+ export { MarkdownRenderer } from './components/MarkdownRenderer';
10
+ export type { MarkdownRendererProps } from './components/MarkdownRenderer';
11
+ export { ContextBadge } from './components/ContextBadge';
12
+ export type { ContextBadgeProps } from './components/ContextBadge';
13
+ export type { ChatMessage, MessageRole, PageContext, AIChatConfig, UseAIChatReturn, UsePageContextReturn, ChatRequest, } from './types';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAG3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAEpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAGlE,YAAY,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,WAAW,GACZ,MAAM,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // Provider & hooks
2
+ export { AIChatProvider, useAIChat, usePageContext } from './AIChatProvider';
3
+ // Components
4
+ export { ChatSidebar } from './ChatSidebar';
5
+ export { ChatToggle } from './ChatToggle';
6
+ // Sub-components (for custom layouts)
7
+ export { MessageBubble } from './components/MessageBubble';
8
+ export { MarkdownRenderer } from './components/MarkdownRenderer';
9
+ export { ContextBadge } from './components/ContextBadge';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAG5E,aAAa;AACb,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG3C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAGzC,sCAAsC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAGhE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA"}
@@ -0,0 +1,59 @@
1
+ /** Role of a chat message sender. */
2
+ export type MessageRole = 'user' | 'assistant';
3
+ /** A single message in the chat history. */
4
+ export interface ChatMessage {
5
+ id: string;
6
+ role: MessageRole;
7
+ content: string;
8
+ timestamp: number;
9
+ }
10
+ /**
11
+ * Page context registered by consumer apps via usePageContext().
12
+ * Sent with every chat message so the AI knows what the user is viewing.
13
+ */
14
+ export interface PageContext {
15
+ /** Current view/page identifier (e.g. "field-mapping", "entity-detail") */
16
+ view: string;
17
+ /** Primary entity or object the user is viewing */
18
+ entity?: string;
19
+ /** Human-readable summary of what's on screen */
20
+ summary?: string;
21
+ /** Arbitrary structured data relevant to the current view */
22
+ data?: Record<string, unknown>;
23
+ }
24
+ /** Configuration for the AIChatProvider. */
25
+ export interface AIChatConfig {
26
+ /** Name of the consuming app (e.g. "MigrationMapper", "Scripter") */
27
+ appName: string;
28
+ /** API endpoint for chat requests. Defaults to "/api/ai/chat" */
29
+ endpoint?: string;
30
+ /** Placeholder text for the input field */
31
+ placeholder?: string;
32
+ /** Maximum number of messages to retain in history (sent to backend). Defaults to 50. */
33
+ maxHistory?: number;
34
+ }
35
+ /** Shape of the value returned by useAIChat(). */
36
+ export interface UseAIChatReturn {
37
+ messages: ChatMessage[];
38
+ isStreaming: boolean;
39
+ isOpen: boolean;
40
+ error: string | null;
41
+ sendMessage: (content: string) => void;
42
+ clearHistory: () => void;
43
+ toggle: () => void;
44
+ open: () => void;
45
+ close: () => void;
46
+ }
47
+ /** Shape of the value returned by usePageContext(). */
48
+ export interface UsePageContextReturn {
49
+ pageContext: PageContext | null;
50
+ setPageContext: (context: PageContext | null) => void;
51
+ }
52
+ /** Request body sent to the AI backend. */
53
+ export interface ChatRequest {
54
+ message: string;
55
+ history: ChatMessage[];
56
+ pageContext: PageContext | null;
57
+ appName: string;
58
+ }
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAA;AAE9C,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAA;IACZ,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,4CAA4C;AAC5C,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAA;IACf,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,WAAW,EAAE,OAAO,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;IAC/B,cAAc,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;CACtD;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;IAC/B,OAAO,EAAE,MAAM,CAAA;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@conduit-d365/ai-chat",
3
+ "version": "0.1.0",
4
+ "description": "Shared AI chat sidebar for the Conduit suite",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "assets"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "peerDependencies": {
20
+ "react": "^18.0 || ^19.0",
21
+ "react-dom": "^18.0 || ^19.0"
22
+ },
23
+ "dependencies": {
24
+ "react-markdown": "^9.0",
25
+ "remark-gfm": "^4.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^19.0",
29
+ "react": "^19.0",
30
+ "react-dom": "^19.0",
31
+ "typescript": "^5.5"
32
+ }
33
+ }