@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.1.1 → 0.1.2
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.
- package/dist/context/AgentAssistantContext.d.ts.map +1 -1
- package/dist/context/AgentAssistantContext.js +3 -1
- package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts +15 -0
- package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/ChatBotHeader.js +62 -0
- package/dist/legacy/chatbot/ui/ChatBotUI.d.ts.map +1 -1
- package/dist/legacy/chatbot/ui/ChatBotUI.js +3 -71
- package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts +15 -0
- package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts.map +1 -0
- package/dist/legacy/chatbot/ui/chatBotHeaderParts.js +50 -0
- package/dist/voice/useVoiceTranscriptMessages.d.ts +7 -5
- package/dist/voice/useVoiceTranscriptMessages.d.ts.map +1 -1
- package/dist/voice/useVoiceTranscriptMessages.js +79 -76
- package/dist/voice/voiceUserBubble.d.ts +10 -0
- package/dist/voice/voiceUserBubble.d.ts.map +1 -0
- package/dist/voice/voiceUserBubble.js +52 -0
- package/package.json +5 -5
- package/src/context/AgentAssistantContext.tsx +4 -2
- package/src/legacy/chatbot/ui/ChatBotHeader.tsx +143 -0
- package/src/legacy/chatbot/ui/ChatBotUI.tsx +13 -144
- package/src/legacy/chatbot/ui/chatBotHeaderParts.tsx +115 -0
- package/src/voice/useVoiceTranscriptMessages.ts +87 -72
- package/src/voice/voiceUserBubble.ts +71 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentAssistantContext.d.ts","sourceRoot":"","sources":["../../src/context/AgentAssistantContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"AgentAssistantContext.d.ts","sourceRoot":"","sources":["../../src/context/AgentAssistantContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,8CAA8C,CAAC;AACzF,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC3B,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAK1F,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAE/C,MAAM,MAAM,0BAA0B,GAAG,qBAAqB,GAC5D,qBAAqB,GAAG;IACxB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,YAAY,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD,CAAC;AAIF,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,SAAS,CAAC;IACpB,EAAE,EAAE,IAAI,CACN,qBAAqB,EACnB,OAAO,GACP,aAAa,GACb,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,OAAO,GACP,mBAAmB,GACnB,wBAAwB,CAC3B,CAAC;CACH,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,EAAE,GACH,EAAE,2BAA2B,GAAG,KAAK,CAAC,YAAY,CA8LlD;AAED,wBAAgB,iBAAiB,IAAI,0BAA0B,CAQ9D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAU5D"}
|
|
@@ -24,7 +24,9 @@ export function AgentAssistantProvider({ children, ui, }) {
|
|
|
24
24
|
});
|
|
25
25
|
const voiceTranscriptApi = useMemo(() => ({
|
|
26
26
|
getMessages: () => agent.agent.getSnapshot().messages,
|
|
27
|
-
|
|
27
|
+
updateMessages: (updater) => {
|
|
28
|
+
agent.setMessages(updater(agent.agent.getSnapshot().messages));
|
|
29
|
+
},
|
|
28
30
|
syncVoiceTurnHistory: agent.syncVoiceTurnHistory,
|
|
29
31
|
}), [agent.agent, agent.setMessages, agent.syncVoiceTurnHistory]);
|
|
30
32
|
const { handleTranscript, handleDone, clearVoiceStream } = useVoiceTranscriptMessages(voiceTranscriptApi, interactionMode, voice.voiceSessionId);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type ChatBotHeaderProps = {
|
|
3
|
+
mobileLayout: boolean;
|
|
4
|
+
isDragging: boolean;
|
|
5
|
+
onDragStart: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
6
|
+
isVoiceMode: boolean;
|
|
7
|
+
isVoiceConnecting: boolean;
|
|
8
|
+
onVoiceToggle: () => void;
|
|
9
|
+
piiDisplayMode: 'plain' | 'redacted';
|
|
10
|
+
isAITLoading: boolean;
|
|
11
|
+
onSettingsClick: () => void;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
};
|
|
14
|
+
export declare const ChatBotHeader: React.FC<ChatBotHeaderProps>;
|
|
15
|
+
//# sourceMappingURL=ChatBotHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatBotHeader.d.ts","sourceRoot":"","sources":["../../../../src/legacy/chatbot/ui/ChatBotHeader.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IACjE,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAqHtD,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { css } from '@emotion/react';
|
|
4
|
+
import { ModelSelector } from './ModelSelector';
|
|
5
|
+
import { DRAG_CORNER_EXCLUSION_PX, HeaderActions, HeaderLoadingIndicator, PiiBadge, } from './chatBotHeaderParts';
|
|
6
|
+
import { chatHeader, headerTitle } from './styles/isolatedStyles';
|
|
7
|
+
export const ChatBotHeader = ({ mobileLayout, isDragging, onDragStart, isVoiceMode, isVoiceConnecting, onVoiceToggle, piiDisplayMode, isAITLoading, onSettingsClick, onClose, }) => {
|
|
8
|
+
const actions = (_jsx(HeaderActions, { isVoiceMode: isVoiceMode, isVoiceConnecting: isVoiceConnecting, onVoiceToggle: onVoiceToggle, onSettingsClick: onSettingsClick, onClose: onClose }));
|
|
9
|
+
const metaChips = !isVoiceMode ? (_jsxs(_Fragment, { children: [_jsx("div", { css: css `position: relative !important; pointer-events: auto !important;`, children: _jsx(ModelSelector, {}) }), piiDisplayMode === 'redacted' && _jsx(PiiBadge, {}), isAITLoading && _jsx(HeaderLoadingIndicator, {})] })) : null;
|
|
10
|
+
const dragOverlay = (_jsx("div", { css: css `
|
|
11
|
+
position: absolute !important;
|
|
12
|
+
left: ${DRAG_CORNER_EXCLUSION_PX}px !important;
|
|
13
|
+
right: ${DRAG_CORNER_EXCLUSION_PX}px !important;
|
|
14
|
+
top: 0 !important;
|
|
15
|
+
bottom: 0 !important;
|
|
16
|
+
z-index: 1 !important;
|
|
17
|
+
cursor: ${isDragging ? 'grabbing' : 'grab'} !important;
|
|
18
|
+
user-select: none !important;
|
|
19
|
+
`, onPointerDown: onDragStart, title: "Drag to move", "aria-hidden": true }));
|
|
20
|
+
return (_jsxs("div", { css: [chatHeader, css `
|
|
21
|
+
position: relative !important;
|
|
22
|
+
${mobileLayout ? css `
|
|
23
|
+
padding: 10px 12px !important;
|
|
24
|
+
flex-direction: column !important;
|
|
25
|
+
align-items: stretch !important;
|
|
26
|
+
gap: 8px !important;
|
|
27
|
+
` : ''}
|
|
28
|
+
`], children: [dragOverlay, mobileLayout ? (_jsxs("div", { css: css `
|
|
29
|
+
position: relative !important;
|
|
30
|
+
z-index: 2 !important;
|
|
31
|
+
display: flex !important;
|
|
32
|
+
flex-direction: column !important;
|
|
33
|
+
gap: 8px !important;
|
|
34
|
+
width: 100% !important;
|
|
35
|
+
`, children: [_jsxs("div", { css: css `
|
|
36
|
+
display: flex !important;
|
|
37
|
+
justify-content: space-between !important;
|
|
38
|
+
align-items: center !important;
|
|
39
|
+
gap: 8px !important;
|
|
40
|
+
pointer-events: none !important;
|
|
41
|
+
`, children: [_jsx("h3", { css: [headerTitle, css `pointer-events: none !important;`], children: "AI Agent" }), actions] }), metaChips && (_jsx("div", { css: css `
|
|
42
|
+
display: flex !important;
|
|
43
|
+
align-items: center !important;
|
|
44
|
+
flex-wrap: wrap !important;
|
|
45
|
+
gap: 8px !important;
|
|
46
|
+
pointer-events: none !important;
|
|
47
|
+
`, children: metaChips }))] })) : (_jsxs("div", { css: css `
|
|
48
|
+
position: relative !important;
|
|
49
|
+
z-index: 2 !important;
|
|
50
|
+
display: flex !important;
|
|
51
|
+
justify-content: space-between !important;
|
|
52
|
+
align-items: center !important;
|
|
53
|
+
width: 100% !important;
|
|
54
|
+
pointer-events: none !important;
|
|
55
|
+
`, children: [_jsxs("div", { css: css `
|
|
56
|
+
display: flex !important;
|
|
57
|
+
align-items: center !important;
|
|
58
|
+
gap: 10px !important;
|
|
59
|
+
min-width: 0 !important;
|
|
60
|
+
pointer-events: none !important;
|
|
61
|
+
`, children: [_jsx("h3", { css: headerTitle, children: "AI Agent" }), metaChips] }), actions] }))] }));
|
|
62
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatBotUI.d.ts","sourceRoot":"","sources":["../../../../src/legacy/chatbot/ui/ChatBotUI.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatBotUI.d.ts","sourceRoot":"","sources":["../../../../src/legacy/chatbot/ui/ChatBotUI.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA+F/B,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAgtB7B,CAAC"}
|
|
@@ -4,16 +4,13 @@ import { css } from '@emotion/react';
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { useDraggable, useLocalStorage, useResizable, walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
|
|
6
6
|
import { useChatBot } from '../context/ChatBotContext';
|
|
7
|
-
import
|
|
7
|
+
import { ChatBotHeader } from './ChatBotHeader';
|
|
8
8
|
import { MessageInput } from './MessageInput';
|
|
9
9
|
import { MessageList } from './MessageList';
|
|
10
|
-
import { ModelSelector } from './ModelSelector';
|
|
11
10
|
import { PermissionForm } from './PermissionForm';
|
|
12
11
|
import { PresetMessages } from './PresetMessages';
|
|
13
12
|
import { VoiceModePanel } from './VoiceModePanel';
|
|
14
|
-
import {
|
|
15
|
-
/** Header drag band inset so it does not overlap corner resize hit areas */
|
|
16
|
-
const DRAG_CORNER_EXCLUSION_PX = 20;
|
|
13
|
+
import { chatWindow, errorToast, floatingButton, idvBanner, idvBannerText, idvBannerTitle, idvDismissButton, idvVerifyButton, infoToast, modalOverlay, resizeHandleNE, resizeHandleNW, resizeHandleSE, resizeHandleSW, sdkContainer, successToast, toastCloseButton, warningToast } from './styles/isolatedStyles';
|
|
17
14
|
const MOBILE_BREAKPOINT = 768;
|
|
18
15
|
const MOBILE_EDGE_MARGIN = 12;
|
|
19
16
|
const MOBILE_FAB_POSITION = { right: MOBILE_EDGE_MARGIN, bottom: MOBILE_EDGE_MARGIN };
|
|
@@ -526,72 +523,7 @@ export const ChatBotUI = () => {
|
|
|
526
523
|
left: ${MOBILE_EDGE_MARGIN}px !important;
|
|
527
524
|
top: ${MOBILE_EDGE_MARGIN}px !important;
|
|
528
525
|
` : ''}
|
|
529
|
-
`], children: [!mobileLayout && (_jsxs(_Fragment, { children: [_jsx("div", { css: resizeHandleNW, onPointerDown: handleResizeStart('nw'), title: "Resize", "aria-label": "Resize chat window from top-left" }), _jsx("div", { css: resizeHandleNE, onPointerDown: handleResizeStart('ne'), title: "Resize", "aria-label": "Resize chat window from top-right" }), _jsx("div", { css: resizeHandleSW, onPointerDown: handleResizeStart('sw'), title: "Resize", "aria-label": "Resize chat window from bottom-left" }), _jsx("div", { css: resizeHandleSE, onPointerDown: handleResizeStart('se'), title: "Resize", "aria-label": "Resize chat window from bottom-right" })] })), _jsxs("div", { css: [
|
|
530
|
-
position: relative !important;
|
|
531
|
-
`], children: [_jsx("div", { css: css `
|
|
532
|
-
position: absolute !important;
|
|
533
|
-
left: ${DRAG_CORNER_EXCLUSION_PX}px !important;
|
|
534
|
-
right: ${DRAG_CORNER_EXCLUSION_PX}px !important;
|
|
535
|
-
top: 0 !important;
|
|
536
|
-
bottom: 0 !important;
|
|
537
|
-
z-index: 1 !important;
|
|
538
|
-
cursor: ${isDragging ? 'grabbing' : 'grab'} !important;
|
|
539
|
-
user-select: none !important;
|
|
540
|
-
`, onPointerDown: handleDragStart, title: "Drag to move", "aria-hidden": true }), _jsxs("div", { css: css `
|
|
541
|
-
position: relative !important;
|
|
542
|
-
z-index: 2 !important;
|
|
543
|
-
display: flex !important;
|
|
544
|
-
justify-content: space-between !important;
|
|
545
|
-
align-items: center !important;
|
|
546
|
-
width: 100% !important;
|
|
547
|
-
pointer-events: none !important;
|
|
548
|
-
`, children: [_jsxs("div", { css: css `
|
|
549
|
-
display: flex !important;
|
|
550
|
-
align-items: center !important;
|
|
551
|
-
gap: 10px !important;
|
|
552
|
-
pointer-events: none !important;
|
|
553
|
-
`, children: [_jsx("h3", { css: headerTitle, children: "AI Agent" }), !isVoiceMode && (_jsx("div", { css: css `
|
|
554
|
-
position: relative !important;
|
|
555
|
-
pointer-events: auto !important;
|
|
556
|
-
`, children: _jsx(ModelSelector, {}) })), !isVoiceMode && piiDisplayMode === 'redacted' && (_jsxs("div", { css: css `
|
|
557
|
-
display: inline-flex !important;
|
|
558
|
-
align-items: center !important;
|
|
559
|
-
gap: 4px !important;
|
|
560
|
-
padding: 2px 8px !important;
|
|
561
|
-
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
562
|
-
border: 1px solid rgba(255, 255, 255, 0.4) !important;
|
|
563
|
-
border-radius: 10px !important;
|
|
564
|
-
font-size: 10px !important;
|
|
565
|
-
font-weight: 600 !important;
|
|
566
|
-
color: #ffffff !important;
|
|
567
|
-
white-space: nowrap !important;
|
|
568
|
-
line-height: 1.4 !important;
|
|
569
|
-
user-select: none !important;
|
|
570
|
-
`, title: "PII Protection is active \u2014 sensitive data is automatically anonymized before sending to AI", children: [_jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "#ffffff", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z" }) }), "PII Protected"] })), isAITLoading && (_jsxs("div", { css: css `
|
|
571
|
-
display: flex !important;
|
|
572
|
-
align-items: center !important;
|
|
573
|
-
gap: 5px !important;
|
|
574
|
-
font-size: 12px !important;
|
|
575
|
-
opacity: 0.8 !important;
|
|
576
|
-
`, children: [_jsx("div", { css: loadingSpinner }), "Loading..."] }))] }), _jsxs("div", { css: css `
|
|
577
|
-
display: flex !important;
|
|
578
|
-
align-items: center !important;
|
|
579
|
-
gap: 10px !important;
|
|
580
|
-
pointer-events: auto !important;
|
|
581
|
-
`, children: [_jsxs("button", { onClick: () => void ((isVoiceMode || isVoiceConnecting) ? exitVoiceMode() : enterVoiceMode()), css: [headerButton, css `
|
|
582
|
-
width: auto !important;
|
|
583
|
-
min-width: 82px !important;
|
|
584
|
-
padding: 0 10px !important;
|
|
585
|
-
gap: 6px !important;
|
|
586
|
-
font-size: 12px !important;
|
|
587
|
-
font-weight: 600 !important;
|
|
588
|
-
line-height: 1 !important;
|
|
589
|
-
white-space: nowrap !important;
|
|
590
|
-
`], title: isVoiceConnecting
|
|
591
|
-
? 'Cancel voice connection and return to text mode'
|
|
592
|
-
: isVoiceMode
|
|
593
|
-
? 'Switch to text mode'
|
|
594
|
-
: 'Switch to voice mode', onPointerDown: (e) => e.stopPropagation(), children: [_jsx(GraphicEqIcon, { css: css `font-size: 16px !important; color: #fff !important;` }), (isVoiceMode || isVoiceConnecting) ? 'Text Mode' : 'Voice Mode'] }), _jsx("button", { onClick: handleSettingsClick, css: headerButton, title: "AIT Settings", onPointerDown: (e) => e.stopPropagation(), children: "\u2699\uFE0F" }), _jsx("button", { onClick: handleClose, css: closeButton, onPointerDown: (e) => e.stopPropagation(), title: "Minimize", children: "\u2212" })] })] })] }), showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !isWalletVerifiedWithBerifyme && (_jsxs("div", { "data-idv-banner": true, css: idvBanner, children: [_jsx("div", { css: css `
|
|
526
|
+
`], children: [!mobileLayout && (_jsxs(_Fragment, { children: [_jsx("div", { css: resizeHandleNW, onPointerDown: handleResizeStart('nw'), title: "Resize", "aria-label": "Resize chat window from top-left" }), _jsx("div", { css: resizeHandleNE, onPointerDown: handleResizeStart('ne'), title: "Resize", "aria-label": "Resize chat window from top-right" }), _jsx("div", { css: resizeHandleSW, onPointerDown: handleResizeStart('sw'), title: "Resize", "aria-label": "Resize chat window from bottom-left" }), _jsx("div", { css: resizeHandleSE, onPointerDown: handleResizeStart('se'), title: "Resize", "aria-label": "Resize chat window from bottom-right" })] })), _jsx(ChatBotHeader, { mobileLayout: mobileLayout, isDragging: isDragging, onDragStart: handleDragStart, isVoiceMode: isVoiceMode, isVoiceConnecting: isVoiceConnecting, onVoiceToggle: () => void ((isVoiceMode || isVoiceConnecting) ? exitVoiceMode() : enterVoiceMode()), piiDisplayMode: piiDisplayMode, isAITLoading: isAITLoading, onSettingsClick: handleSettingsClick, onClose: handleClose }), showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !isWalletVerifiedWithBerifyme && (_jsxs("div", { "data-idv-banner": true, css: idvBanner, children: [_jsx("div", { css: css `
|
|
595
527
|
font-size: 20px !important;
|
|
596
528
|
color: #f39c12 !important;
|
|
597
529
|
`, children: "\uD83D\uDCA1" }), _jsxs("div", { css: css `flex: 1 !important;`, children: [_jsx("h5", { css: idvBannerTitle, children: walletTextUtils.getWalletText('Recommended: Verify Your Wallet', serviceId) }), _jsx("p", { css: idvBannerText, children: walletTextUtils.getWalletText('While not required, verifying your wallet with Berify.me provides additional security and trust for your AI agent interactions.', serviceId) }), _jsx("button", { onClick: () => onVerifyWallet('berifyme'), css: idvVerifyButton, children: walletTextUtils.getWalletText('Verify Wallet', serviceId) })] }), _jsx("button", { onClick: handleDismissIDV, css: idvDismissButton, title: `Hide for ${Math.floor((props.idvBannerDismissSeconds || 86400) / 3600)} hours`, children: "\u00D7" })] })), !showIDVSuggestion && dismissUntil && timeRemaining && (_jsxs("div", { css: css `
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export declare const DRAG_CORNER_EXCLUSION_PX = 20;
|
|
3
|
+
export declare const piiBadgeStyles: import("@emotion/utils").SerializedStyles;
|
|
4
|
+
export declare const PiiBadge: React.FC;
|
|
5
|
+
type HeaderActionsProps = {
|
|
6
|
+
isVoiceMode: boolean;
|
|
7
|
+
isVoiceConnecting: boolean;
|
|
8
|
+
onVoiceToggle: () => void;
|
|
9
|
+
onSettingsClick: () => void;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
};
|
|
12
|
+
export declare const HeaderActions: React.FC<HeaderActionsProps>;
|
|
13
|
+
export declare const HeaderLoadingIndicator: React.FC;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=chatBotHeaderParts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatBotHeaderParts.d.ts","sourceRoot":"","sources":["../../../../src/legacy/chatbot/ui/chatBotHeaderParts.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAa3C,eAAO,MAAM,cAAc,2CAc1B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAU5B,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA8CtD,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAW1C,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { css } from '@emotion/react';
|
|
4
|
+
import GraphicEqIcon from '@mui/icons-material/GraphicEq';
|
|
5
|
+
import { closeButton, headerButton, loadingSpinner } from './styles/isolatedStyles';
|
|
6
|
+
export const DRAG_CORNER_EXCLUSION_PX = 20;
|
|
7
|
+
const voiceButtonStyles = css `
|
|
8
|
+
width: auto !important;
|
|
9
|
+
min-width: 82px !important;
|
|
10
|
+
padding: 0 10px !important;
|
|
11
|
+
gap: 6px !important;
|
|
12
|
+
font-size: 12px !important;
|
|
13
|
+
font-weight: 600 !important;
|
|
14
|
+
line-height: 1 !important;
|
|
15
|
+
white-space: nowrap !important;
|
|
16
|
+
`;
|
|
17
|
+
export const piiBadgeStyles = css `
|
|
18
|
+
display: inline-flex !important;
|
|
19
|
+
align-items: center !important;
|
|
20
|
+
gap: 4px !important;
|
|
21
|
+
padding: 2px 8px !important;
|
|
22
|
+
background-color: rgba(255, 255, 255, 0.2) !important;
|
|
23
|
+
border: 1px solid rgba(255, 255, 255, 0.4) !important;
|
|
24
|
+
border-radius: 10px !important;
|
|
25
|
+
font-size: 10px !important;
|
|
26
|
+
font-weight: 600 !important;
|
|
27
|
+
color: #ffffff !important;
|
|
28
|
+
white-space: nowrap !important;
|
|
29
|
+
line-height: 1.4 !important;
|
|
30
|
+
user-select: none !important;
|
|
31
|
+
`;
|
|
32
|
+
export const PiiBadge = () => (_jsxs("div", { css: piiBadgeStyles, title: "PII Protection is active \u2014 sensitive data is automatically anonymized before sending to AI", children: [_jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "#ffffff", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z" }) }), "PII Protected"] }));
|
|
33
|
+
export const HeaderActions = ({ isVoiceMode, isVoiceConnecting, onVoiceToggle, onSettingsClick, onClose, }) => (_jsxs("div", { css: css `
|
|
34
|
+
display: flex !important;
|
|
35
|
+
align-items: center !important;
|
|
36
|
+
gap: 10px !important;
|
|
37
|
+
flex-shrink: 0 !important;
|
|
38
|
+
pointer-events: auto !important;
|
|
39
|
+
`, children: [_jsxs("button", { onClick: onVoiceToggle, css: [headerButton, voiceButtonStyles], title: isVoiceConnecting
|
|
40
|
+
? 'Cancel voice connection and return to text mode'
|
|
41
|
+
: isVoiceMode
|
|
42
|
+
? 'Switch to text mode'
|
|
43
|
+
: 'Switch to voice mode', onPointerDown: (e) => e.stopPropagation(), children: [_jsx(GraphicEqIcon, { css: css `font-size: 16px !important; color: #fff !important;` }), (isVoiceMode || isVoiceConnecting) ? 'Text Mode' : 'Voice Mode'] }), _jsx("button", { onClick: onSettingsClick, css: headerButton, title: "AIT Settings", onPointerDown: (e) => e.stopPropagation(), children: "\u2699\uFE0F" }), _jsx("button", { onClick: onClose, css: closeButton, onPointerDown: (e) => e.stopPropagation(), title: "Minimize", children: "\u2212" })] }));
|
|
44
|
+
export const HeaderLoadingIndicator = () => (_jsxs("div", { css: css `
|
|
45
|
+
display: flex !important;
|
|
46
|
+
align-items: center !important;
|
|
47
|
+
gap: 5px !important;
|
|
48
|
+
font-size: 12px !important;
|
|
49
|
+
opacity: 0.8 !important;
|
|
50
|
+
`, children: [_jsx("div", { css: loadingSpinner }), "Loading..."] }));
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { Message, VoiceDoneEvent, VoiceTranscriptEvent } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
-
|
|
3
|
-
type VoiceTranscriptAgentApi = {
|
|
2
|
+
type InteractionMode = 'text' | 'voice';
|
|
3
|
+
export type VoiceTranscriptAgentApi = {
|
|
4
4
|
getMessages: () => Message[];
|
|
5
|
-
|
|
5
|
+
updateMessages: (updater: (prev: Message[]) => Message[]) => void;
|
|
6
6
|
syncVoiceTurnHistory: (options?: {
|
|
7
7
|
last?: number;
|
|
8
8
|
}) => Promise<void>;
|
|
9
9
|
};
|
|
10
|
-
export declare function useVoiceTranscriptMessages(api: VoiceTranscriptAgentApi, interactionMode: InteractionMode, voiceSessionId: string | null): {
|
|
10
|
+
export declare function useVoiceTranscriptMessages(api: VoiceTranscriptAgentApi, interactionMode: InteractionMode, voiceSessionId: string | null, getPendingUserText?: () => string): {
|
|
11
11
|
handleTranscript: (event: VoiceTranscriptEvent) => void;
|
|
12
|
-
handleDone: (event: VoiceDoneEvent
|
|
12
|
+
handleDone: (event: VoiceDoneEvent, options?: {
|
|
13
|
+
pendingUserText?: string;
|
|
14
|
+
}) => void;
|
|
13
15
|
clearVoiceStream: () => void;
|
|
14
16
|
};
|
|
15
17
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoiceTranscriptMessages.d.ts","sourceRoot":"","sources":["../../src/voice/useVoiceTranscriptMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,oBAAoB,EACrB,MAAM,8CAA8C,CAAC;
|
|
1
|
+
{"version":3,"file":"useVoiceTranscriptMessages.d.ts","sourceRoot":"","sources":["../../src/voice/useVoiceTranscriptMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,oBAAoB,EACrB,MAAM,8CAA8C,CAAC;AAKtD,KAAK,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAExC,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,MAAM,OAAO,EAAE,CAAC;IAC7B,cAAc,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC;IAClE,oBAAoB,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE,CAAC;AAWF,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,uBAAuB,EAC5B,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,kBAAkB,CAAC,EAAE,MAAM,MAAM;8BAiGvB,oBAAoB;wBA4BpB,cAAc,YAAY;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE;;EAgCjE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mergeStreamingTranscript } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
2
|
import { useCallback, useRef } from 'react';
|
|
3
|
+
import { ensureUserBubbleForVoiceTurn } from './voiceUserBubble';
|
|
3
4
|
const STREAM_PREFIX = 'voice-stream-';
|
|
4
5
|
function voiceMeta(sessionId) {
|
|
5
6
|
return {
|
|
@@ -7,104 +8,105 @@ function voiceMeta(sessionId) {
|
|
|
7
8
|
voiceSessionId: sessionId ?? undefined,
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
|
-
export function useVoiceTranscriptMessages(api, interactionMode, voiceSessionId) {
|
|
11
|
+
export function useVoiceTranscriptMessages(api, interactionMode, voiceSessionId, getPendingUserText) {
|
|
11
12
|
const streamIdRef = useRef(null);
|
|
12
13
|
const sessionIdRef = useRef(voiceSessionId);
|
|
13
14
|
sessionIdRef.current = voiceSessionId;
|
|
14
15
|
const isVoiceUiActive = useCallback(() => interactionMode === 'voice' && sessionIdRef.current != null, [interactionMode]);
|
|
15
16
|
const upsertStreaming = useCallback((text) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
17
|
+
api.updateMessages((prev) => {
|
|
18
|
+
let streamId = streamIdRef.current;
|
|
19
|
+
if (!streamId) {
|
|
20
|
+
streamId = `${STREAM_PREFIX}${Date.now()}`;
|
|
21
|
+
streamIdRef.current = streamId;
|
|
22
|
+
}
|
|
23
|
+
const idx = prev.findIndex((m) => m.id === streamId);
|
|
24
|
+
const partialContent = idx >= 0
|
|
25
|
+
? mergeStreamingTranscript(prev[idx]?.partialContent ?? '', text)
|
|
26
|
+
: text;
|
|
27
|
+
const meta = voiceMeta(sessionIdRef.current);
|
|
28
|
+
if (idx >= 0) {
|
|
29
|
+
return prev.map((m, i) => i === idx
|
|
30
|
+
? { ...m, partialContent, isStreaming: true, metadata: { ...m.metadata, ...meta } }
|
|
31
|
+
: m);
|
|
32
|
+
}
|
|
33
|
+
const withUser = ensureUserBubbleForVoiceTurn(prev, getPendingUserText?.() ?? '', undefined, meta);
|
|
34
|
+
return [
|
|
35
|
+
...withUser,
|
|
36
|
+
{
|
|
37
|
+
id: streamId,
|
|
38
|
+
role: 'assistant',
|
|
39
|
+
content: '',
|
|
40
|
+
partialContent,
|
|
41
|
+
isStreaming: true,
|
|
42
|
+
timestamp: new Date().toISOString(),
|
|
43
|
+
metadata: meta,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
});
|
|
47
|
+
}, [api, getPendingUserText]);
|
|
46
48
|
const finalizeAssistant = useCallback((text, messageId) => {
|
|
47
49
|
const trimmed = text.trim();
|
|
48
50
|
streamIdRef.current = null;
|
|
49
51
|
if (!trimmed)
|
|
50
52
|
return;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
api.updateMessages((prev) => {
|
|
54
|
+
const streamIdx = prev.findIndex((m) => m.isStreaming && m.role === 'assistant');
|
|
55
|
+
if (streamIdx >= 0) {
|
|
56
|
+
return prev.map((m, i) => i === streamIdx
|
|
57
|
+
? {
|
|
58
|
+
...m,
|
|
59
|
+
id: messageId ?? m.id,
|
|
60
|
+
content: trimmed,
|
|
61
|
+
partialContent: undefined,
|
|
62
|
+
isStreaming: false,
|
|
63
|
+
metadata: { ...m.metadata, ...voiceMeta(sessionIdRef.current) },
|
|
64
|
+
}
|
|
65
|
+
: m);
|
|
66
|
+
}
|
|
67
|
+
const last = prev[prev.length - 1];
|
|
68
|
+
if (last?.role === 'assistant' && last.content === trimmed)
|
|
69
|
+
return prev;
|
|
70
|
+
return [
|
|
71
|
+
...prev,
|
|
72
|
+
{
|
|
73
|
+
id: messageId ?? `voice-asst-${Date.now()}`,
|
|
74
|
+
role: 'assistant',
|
|
58
75
|
content: trimmed,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const last = messages[messages.length - 1];
|
|
67
|
-
if (last?.role === 'assistant' && last.content === trimmed)
|
|
68
|
-
return;
|
|
69
|
-
api.setMessages([
|
|
70
|
-
...messages,
|
|
71
|
-
{
|
|
72
|
-
id: messageId ?? `voice-asst-${Date.now()}`,
|
|
73
|
-
role: 'assistant',
|
|
74
|
-
content: trimmed,
|
|
75
|
-
timestamp: new Date().toISOString(),
|
|
76
|
-
metadata: voiceMeta(sessionIdRef.current),
|
|
77
|
-
},
|
|
78
|
-
]);
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
metadata: voiceMeta(sessionIdRef.current),
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
});
|
|
79
81
|
}, [api]);
|
|
80
82
|
const handleTranscript = useCallback((event) => {
|
|
81
83
|
if (!isVoiceUiActive())
|
|
82
84
|
return;
|
|
83
85
|
const text = event.text?.trim() ?? '';
|
|
84
86
|
if (event.role === 'assistant') {
|
|
85
|
-
// Keep one streaming bubble for the whole turn; finalize only in handleDone.
|
|
86
87
|
if (text)
|
|
87
88
|
upsertStreaming(text);
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
90
91
|
if (event.role === 'user' && !event.interim && text) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
api.updateMessages((prev) => {
|
|
93
|
+
const last = prev[prev.length - 1];
|
|
94
|
+
if (last?.role === 'user' && last.content === text)
|
|
95
|
+
return prev;
|
|
96
|
+
return [
|
|
97
|
+
...prev,
|
|
98
|
+
{
|
|
99
|
+
id: `voice-user-${Date.now()}`,
|
|
100
|
+
role: 'user',
|
|
101
|
+
content: text,
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
metadata: voiceMeta(sessionIdRef.current),
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
});
|
|
105
107
|
}
|
|
106
|
-
}, [api,
|
|
107
|
-
const handleDone = useCallback((event) => {
|
|
108
|
+
}, [api, isVoiceUiActive, upsertStreaming]);
|
|
109
|
+
const handleDone = useCallback((event, options) => {
|
|
108
110
|
if (!isVoiceUiActive())
|
|
109
111
|
return;
|
|
110
112
|
if (event.guardrailsBlocked || event.billingBlocked || event.error) {
|
|
@@ -113,6 +115,7 @@ export function useVoiceTranscriptMessages(api, interactionMode, voiceSessionId)
|
|
|
113
115
|
}
|
|
114
116
|
const reply = event.replyText?.trim() ?? '';
|
|
115
117
|
if (reply) {
|
|
118
|
+
api.updateMessages((prev) => ensureUserBubbleForVoiceTurn(prev, options?.pendingUserText ?? getPendingUserText?.() ?? '', event.userMessageId, voiceMeta(sessionIdRef.current)));
|
|
116
119
|
finalizeAssistant(reply, event.assistantMessageId ?? undefined);
|
|
117
120
|
}
|
|
118
121
|
else {
|
|
@@ -121,7 +124,7 @@ export function useVoiceTranscriptMessages(api, interactionMode, voiceSessionId)
|
|
|
121
124
|
void api.syncVoiceTurnHistory({ last: 20 }).catch((err) => {
|
|
122
125
|
console.warn('[nxtlinq] syncVoiceTurnHistory after voice turn failed', err);
|
|
123
126
|
});
|
|
124
|
-
}, [api, finalizeAssistant, isVoiceUiActive]);
|
|
127
|
+
}, [api, finalizeAssistant, getPendingUserText, isVoiceUiActive]);
|
|
125
128
|
const clearVoiceStream = useCallback(() => {
|
|
126
129
|
streamIdRef.current = null;
|
|
127
130
|
}, []);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Message } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
|
+
export declare const VOICE_USER_INPUT_PLACEHOLDER = "(Voice input)";
|
|
3
|
+
type VoiceMeta = {
|
|
4
|
+
voiceRealtime: true;
|
|
5
|
+
voiceSessionId?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Ensure a user bubble exists before the in-flight assistant reply for this turn. */
|
|
8
|
+
export declare function ensureUserBubbleForVoiceTurn(messages: Message[], userText: string, userMessageId: string | null | undefined, metaForPlaceholder: VoiceMeta | undefined): Message[];
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=voiceUserBubble.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voiceUserBubble.d.ts","sourceRoot":"","sources":["../../src/voice/voiceUserBubble.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8CAA8C,CAAC;AAE5E,eAAO,MAAM,4BAA4B,kBAAkB,CAAC;AAE5D,KAAK,SAAS,GAAG;IACf,aAAa,EAAE,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,kBAAkB,EAAE,SAAS,GAAG,SAAS,GACxC,OAAO,EAAE,CAuDX"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const VOICE_USER_INPUT_PLACEHOLDER = '(Voice input)';
|
|
2
|
+
/** Ensure a user bubble exists before the in-flight assistant reply for this turn. */
|
|
3
|
+
export function ensureUserBubbleForVoiceTurn(messages, userText, userMessageId, metaForPlaceholder) {
|
|
4
|
+
const streamIdx = messages.findIndex((m) => m.isStreaming && m.role === 'assistant');
|
|
5
|
+
const insertAt = streamIdx >= 0 ? streamIdx : messages.length;
|
|
6
|
+
const before = messages.slice(0, insertAt);
|
|
7
|
+
const lastAsstIdx = (() => {
|
|
8
|
+
for (let i = before.length - 1; i >= 0; i -= 1) {
|
|
9
|
+
const m = before[i];
|
|
10
|
+
if (m.role === 'assistant' && !m.isStreaming && Boolean(m.content?.trim()))
|
|
11
|
+
return i;
|
|
12
|
+
}
|
|
13
|
+
return -1;
|
|
14
|
+
})();
|
|
15
|
+
const lastUserIdx = (() => {
|
|
16
|
+
for (let i = before.length - 1; i >= 0; i -= 1) {
|
|
17
|
+
if (before[i].role === 'user')
|
|
18
|
+
return i;
|
|
19
|
+
}
|
|
20
|
+
return -1;
|
|
21
|
+
})();
|
|
22
|
+
const hasUserForTurn = lastUserIdx >= 0 && lastUserIdx > lastAsstIdx;
|
|
23
|
+
const trimmed = userText.trim();
|
|
24
|
+
const displayText = trimmed || VOICE_USER_INPUT_PLACEHOLDER;
|
|
25
|
+
if (hasUserForTurn) {
|
|
26
|
+
const existing = before[lastUserIdx];
|
|
27
|
+
const shouldUpgrade = trimmed &&
|
|
28
|
+
existing.content !== trimmed &&
|
|
29
|
+
(existing.content === VOICE_USER_INPUT_PLACEHOLDER || !existing.content.trim());
|
|
30
|
+
if (!shouldUpgrade)
|
|
31
|
+
return messages;
|
|
32
|
+
const upgraded = before.map((m, i) => i === lastUserIdx
|
|
33
|
+
? {
|
|
34
|
+
...m,
|
|
35
|
+
content: trimmed,
|
|
36
|
+
id: userMessageId ?? m.id,
|
|
37
|
+
metadata: userMessageId ? undefined : m.metadata,
|
|
38
|
+
}
|
|
39
|
+
: m);
|
|
40
|
+
return [...upgraded, ...messages.slice(insertAt)];
|
|
41
|
+
}
|
|
42
|
+
const userMsg = {
|
|
43
|
+
id: userMessageId ?? `voice-user-${Date.now()}`,
|
|
44
|
+
role: 'user',
|
|
45
|
+
content: displayText,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
metadata: userMessageId ? undefined : metaForPlaceholder,
|
|
48
|
+
};
|
|
49
|
+
const next = [...messages];
|
|
50
|
+
next.splice(insertAt, 0, userMsg);
|
|
51
|
+
return next;
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytexbyte/nxtlinq-ai-agent-ui-react-development",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Official React Web UI for nxtlinq AI Agent — drop-in chat widget",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"react-dom": ">=18.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@bytexbyte/nxtlinq-ai-agent-core-development": "0.3.
|
|
42
|
-
"@bytexbyte/nxtlinq-ai-agent-web-development": "0.1.
|
|
41
|
+
"@bytexbyte/nxtlinq-ai-agent-core-development": "0.3.9",
|
|
42
|
+
"@bytexbyte/nxtlinq-ai-agent-web-development": "0.1.2",
|
|
43
43
|
"@emotion/react": "^11.14.0",
|
|
44
44
|
"@emotion/styled": "^11.14.1",
|
|
45
45
|
"@mui/icons-material": "^7.2.0",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"uuid": "^11.1.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@bytexbyte/nxtlinq-ai-agent-core-development": "
|
|
53
|
-
"@bytexbyte/nxtlinq-ai-agent-web-development": "
|
|
52
|
+
"@bytexbyte/nxtlinq-ai-agent-core-development": "0.3.9",
|
|
53
|
+
"@bytexbyte/nxtlinq-ai-agent-web-development": "0.1.2",
|
|
54
54
|
"@types/react": "^18.2.64",
|
|
55
55
|
"@types/react-dom": "^18.2.25",
|
|
56
56
|
"react": "^18.2.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VoiceStatus } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
1
|
+
import type { Message, VoiceStatus } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
2
2
|
import {
|
|
3
3
|
useNxtlinqAgent,
|
|
4
4
|
useNxtlinqVoice,
|
|
@@ -93,7 +93,9 @@ export function AgentAssistantProvider({
|
|
|
93
93
|
const voiceTranscriptApi = useMemo(
|
|
94
94
|
() => ({
|
|
95
95
|
getMessages: () => agent.agent.getSnapshot().messages,
|
|
96
|
-
|
|
96
|
+
updateMessages: (updater: (prev: Message[]) => Message[]) => {
|
|
97
|
+
agent.setMessages(updater(agent.agent.getSnapshot().messages));
|
|
98
|
+
},
|
|
97
99
|
syncVoiceTurnHistory: agent.syncVoiceTurnHistory,
|
|
98
100
|
}),
|
|
99
101
|
[agent.agent, agent.setMessages, agent.syncVoiceTurnHistory],
|