From c905b984d275512af1c357860be30b3b5404a97c Mon Sep 17 00:00:00 2001 From: Ruchi Anand Date: Wed, 22 Jul 2026 12:09:06 +0530 Subject: [PATCH 1/3] feedback loop changes --- src/components/FloatingAssistant/api.ts | 16 +++++++++++++ src/components/FloatingAssistant/constants.ts | 3 ++- src/components/FloatingAssistant/index.tsx | 23 +++++++++++++++---- src/components/FloatingAssistant/types.ts | 3 +++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/components/FloatingAssistant/api.ts b/src/components/FloatingAssistant/api.ts index 0ee661d5b..2bb452195 100644 --- a/src/components/FloatingAssistant/api.ts +++ b/src/components/FloatingAssistant/api.ts @@ -28,3 +28,19 @@ export async function* streamAgentResponse( yield* parseSseStream(response); } + +export async function sendFeedback( + traceId: string, + observationId: string | undefined, + value: 'up' | 'down', +): Promise { + const response = await fetch(`${CLOUDFLARE_URL}${API_PATHS.FEEDBACK}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ traceId, observationId, value }), + }); + + if (!response.ok) { + throw new Error(`Feedback API error: ${response.status}`); + } +} diff --git a/src/components/FloatingAssistant/constants.ts b/src/components/FloatingAssistant/constants.ts index ea32343a2..c202490c4 100644 --- a/src/components/FloatingAssistant/constants.ts +++ b/src/components/FloatingAssistant/constants.ts @@ -1,4 +1,4 @@ -export const CLOUDFLARE_URL = 'https://spotter-code-popular-questions.thoughtspot-485.workers.dev'; +export const CLOUDFLARE_URL = 'http://localhost:8001'; export const LOADING_PHASES = [ 'Processing your request...', @@ -17,6 +17,7 @@ export const LOADING_PHASE_DELAYS = [0, 1200, 2800, 4800, 7000]; export const API_PATHS = { SUGGESTED_QUESTIONS: '/suggested-questions', AGENT: '/agent/embed-assistant', + FEEDBACK: '/agent/feedback', } as const; export const ERROR_MESSAGES = { diff --git a/src/components/FloatingAssistant/index.tsx b/src/components/FloatingAssistant/index.tsx index 80218022f..04087055e 100644 --- a/src/components/FloatingAssistant/index.tsx +++ b/src/components/FloatingAssistant/index.tsx @@ -10,7 +10,7 @@ import SpotterCodeLogo from './SpotterCodeLogo'; import { LOADING_PHASES, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH, PANEL_DEFAULT_WIDTH, LOADING_PHASE_DELAYS, ERROR_MESSAGES } from './constants'; import { Message } from './types'; import { renderMarkdown, formatTimestamp, formatDuration, getPageId, stripMarkdown } from './helpers'; -import { fetchSuggestedQuestions, streamAgentResponse } from './api'; +import { fetchSuggestedQuestions, streamAgentResponse, sendFeedback } from './api'; const SparkleIcon = () => ( @@ -72,7 +72,7 @@ const FloatingAssistant: React.FC = () => { }, 250); }; - const giveFeedback = (idx: number, type: 'up' | 'down') => { + const giveFeedback = (idx: number, type: 'up' | 'down', message: Message) => { const isUnfill = feedbackGiven[idx] === type; setFeedbackGiven((prev: Record) => { const next = { ...prev }; @@ -86,6 +86,12 @@ const FloatingAssistant: React.FC = () => { setToastExiting(false); setShowFeedbackToast(true); feedbackToastTimer.current = setTimeout(hideToast, 2500); + + if (message.traceId) { + sendFeedback(message.traceId, message.observationId, type).catch(() => { + // Feedback is best-effort — a failed submission shouldn't disrupt the chat UI. + }); + } } }; const [editingIndex, setEditingIndex] = useState(null); @@ -318,10 +324,15 @@ const FloatingAssistant: React.FC = () => { let collectedSteps: string[] = []; let finalContent: string = ERROR_MESSAGES.DEFAULT; let aborted = false; + let traceId: string | undefined; + let observationId: string | undefined; try { for await (const event of streamAgentResponse(updatedMessages, pageId, abortRef.current.signal)) { - if (event.type === 'text') { + if (event.type === 'trace') { + traceId = event.traceId; + observationId = event.observationId ?? event.generationId; + } else if (event.type === 'text') { accumulated += event.content; setStreamingText(accumulated); } else if (event.type === 'tool-start') { @@ -347,6 +358,8 @@ const FloatingAssistant: React.FC = () => { content: finalContent, toolSteps: collectedSteps.length > 0 ? collectedSteps : undefined, durationMs: Date.now() - startTime, + traceId, + observationId, }]); } setStreamingText(''); @@ -571,14 +584,14 @@ const FloatingAssistant: React.FC = () => { diff --git a/src/components/FloatingAssistant/types.ts b/src/components/FloatingAssistant/types.ts index 8454ed5d7..0fb70f74e 100644 --- a/src/components/FloatingAssistant/types.ts +++ b/src/components/FloatingAssistant/types.ts @@ -5,9 +5,12 @@ export type Message = { toolSteps?: string[]; durationMs?: number; sentAt?: number; + traceId?: string; + observationId?: string; }; export type SseEvent = + | { type: 'trace'; traceId: string; generationId?: string; observationId?: string } | { type: 'text'; content: string } | { type: 'tool-start'; toolName: string; content: string; input: unknown } | { type: 'tool-result'; toolName: string; content: string; output: unknown } From cc450a45eee9ac35595f9bb1076373e83332ac31 Mon Sep 17 00:00:00 2001 From: Ruchi Anand Date: Fri, 24 Jul 2026 13:54:22 +0530 Subject: [PATCH 2/3] fix --- src/components/Document/index.tsx | 1 + src/components/FloatingAssistant/constants.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Document/index.tsx b/src/components/Document/index.tsx index 56165a6ef..6e3ab5942 100644 --- a/src/components/Document/index.tsx +++ b/src/components/Document/index.tsx @@ -31,6 +31,7 @@ const Document = (props: { const handleMouseUp = (e: MouseEvent) => { const target = e.target as HTMLElement; if (target.closest('.selection-cta-button')) return; + if (target.closest('.floating-assistant__panel, .floating-assistant__chip-ring')) return; // If mouse didn't move (plain click, not a drag-select), don't re-show const moved = Math.abs(e.clientX - mouseDownX) > 3 || Math.abs(e.clientY - mouseDownY) > 3; diff --git a/src/components/FloatingAssistant/constants.ts b/src/components/FloatingAssistant/constants.ts index c202490c4..c706024b0 100644 --- a/src/components/FloatingAssistant/constants.ts +++ b/src/components/FloatingAssistant/constants.ts @@ -17,7 +17,7 @@ export const LOADING_PHASE_DELAYS = [0, 1200, 2800, 4800, 7000]; export const API_PATHS = { SUGGESTED_QUESTIONS: '/suggested-questions', AGENT: '/agent/embed-assistant', - FEEDBACK: '/agent/feedback', + FEEDBACK: '/agent/embed-assistant/feedback', } as const; export const ERROR_MESSAGES = { From da9e773afeaa5e46f7e9ade970e249e1e5bbc086 Mon Sep 17 00:00:00 2001 From: Ruchi Anand Date: Thu, 30 Jul 2026 10:45:43 +0530 Subject: [PATCH 3/3] changed footer --- src/components/DevDocTemplate/index.tsx | 12 +++++--- src/components/FloatingAssistant/index.scss | 18 ++++++++++- src/components/FloatingAssistant/index.tsx | 34 ++++++++++++++++----- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/components/DevDocTemplate/index.tsx b/src/components/DevDocTemplate/index.tsx index 563608c44..c18e2435b 100644 --- a/src/components/DevDocTemplate/index.tsx +++ b/src/components/DevDocTemplate/index.tsx @@ -98,6 +98,10 @@ const DevDocTemplate: FC = (props) => { // breadcrumsData is derived after processedNavMap is built (see useMemo below) const [activeCategory, setActiveCategory] = useState('guides'); const [showSearch, setShowSearch] = useState(false); + + useEffect(() => { + window.dispatchEvent(new CustomEvent('spotter-code-suspend', { detail: { suspended: showSearch } })); + }, [showSearch]); const [leftNavOpen, setLeftNavOpen] = useState(false); const [keyword, updateKeyword] = useState(''); const [isPublicSiteOpen, setIsPublicSiteOpen] = useState(() => { @@ -453,18 +457,16 @@ const isVersionedIframe = VERSION_DROPDOWN.some( const customStyles = { overlay: { background: 'rgba(50,57,70, 0.9)', - zIndex: 10, + zIndex: 1100, }, content: { top: '50px', - left: 'auro', + left: 'auto', right: 'auto', bottom: 'auto', width: isMaxMobileResolution ? '40%' : '100%', margin: 'auto', - transform: `translate(${ - isMaxMobileResolution ? '80%' : '0' - }, 70px)`, + transform: 'translate(0, 70px)', border: 'none', height: isMaxMobileResolution ? '400px' : '300px', boxShadow: 'none', diff --git a/src/components/FloatingAssistant/index.scss b/src/components/FloatingAssistant/index.scss index e612f34cd..abe8094ec 100644 --- a/src/components/FloatingAssistant/index.scss +++ b/src/components/FloatingAssistant/index.scss @@ -76,6 +76,11 @@ } } +.floating-assistant__chip-ring--suspended { + pointer-events: none; + opacity: 0.5; +} + .floating-assistant__panel { position: fixed; top: 108px; @@ -90,7 +95,6 @@ rgba(100, 160, 255, 0.3) 90% ), #fff; - border-left: 1px solid #e3e6eb; display: flex; flex-direction: column; overflow: hidden; @@ -109,6 +113,18 @@ &--embedded { top: 0; // no header or secondary nav in embedded mode — panel takes full height } + + &--suspended { + pointer-events: none; + + &::after { + content: ''; + position: absolute; + inset: 0; + background: rgba(50, 57, 70, 0.4); + z-index: 1; + } + } } // ── Header ──────────────────────────────────────────────────────────────────── diff --git a/src/components/FloatingAssistant/index.tsx b/src/components/FloatingAssistant/index.tsx index 04087055e..ce598ab48 100644 --- a/src/components/FloatingAssistant/index.tsx +++ b/src/components/FloatingAssistant/index.tsx @@ -187,6 +187,22 @@ const FloatingAssistant: React.FC = () => { const abortRef = useRef(null); const userScrolledRef = useRef(false); + const [isSuspended, setIsSuspended] = useState(false); + const panelRef = useRef(null); + useEffect(() => { + const handler = (e: CustomEvent<{ suspended: boolean }>) => { + setIsSuspended(e.detail.suspended); + if (e.detail.suspended) { + const active = document.activeElement as HTMLElement | null; + if (active && panelRef.current?.contains(active)) { + active.blur(); + } + } + }; + window.addEventListener('spotter-code-suspend', handler as EventListener); + return () => window.removeEventListener('spotter-code-suspend', handler as EventListener); + }, []); + useEffect(() => { setIsOpen(true); }, []); @@ -223,10 +239,10 @@ const FloatingAssistant: React.FC = () => { }; useEffect(() => { - if (isOpen && inputRef.current) { + if (isOpen && !isSuspended && inputRef.current) { inputRef.current.focus(); } - }, [isOpen]); + }, [isOpen, isSuspended]); useEffect(() => { const el = inputRef.current; @@ -236,10 +252,10 @@ const FloatingAssistant: React.FC = () => { }, [input]); useEffect(() => { - if (quotedText) { + if (quotedText && !isSuspended) { setTimeout(() => inputRef.current?.focus(), 100); } - }, [quotedText]); + }, [quotedText, isSuspended]); useEffect(() => { const handler = (e: CustomEvent<{ location: Location }>) => { @@ -416,11 +432,14 @@ const FloatingAssistant: React.FC = () => { return ( <> {!isOpen && !isClosing && ( -
+
@@ -429,7 +448,8 @@ const FloatingAssistant: React.FC = () => { {(isOpen || isClosing) && (
@@ -692,7 +712,7 @@ const FloatingAssistant: React.FC = () => {
- SpotterCode responses should be reviewed.{' '} + You are interacting with an AI system. Responses should be reviewed.{' '} Learn more