Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
983966d
context
ruchI9897 Apr 27, 2026
8b944f8
fix
ruchI9897 Apr 27, 2026
de99591
Merge branch 'main' into context
ruchI9897 Apr 27, 2026
bda90be
fix
ruchI9897 Apr 27, 2026
cd93b03
fix
ruchI9897 Apr 27, 2026
3d1fa23
fix
ruchI9897 Apr 28, 2026
003ea07
popular questions
ruchI9897 Jun 10, 2026
8440e66
sync
ruchI9897 Jun 11, 2026
a3f5b45
ui fixes
ruchI9897 Jun 12, 2026
0d6253e
fix
ruchI9897 Jun 12, 2026
54f9587
fix
ruchI9897 Jun 12, 2026
b4f6a7d
fix
ruchI9897 Jun 12, 2026
b2416a0
added UI changes
ruchI9897 Jun 17, 2026
e5b305a
fixes ui
ruchI9897 Jul 6, 2026
7d84412
added context changes
ruchI9897 Jul 8, 2026
011b659
styles changes
ruchI9897 Jul 9, 2026
8f500f2
styles changes
ruchI9897 Jul 9, 2026
b7674a9
feedback fixes
ruchI9897 Jul 14, 2026
af93fc7
fix
ruchI9897 Jul 14, 2026
5c2baf8
fix
ruchI9897 Jul 14, 2026
490cd95
fixed all issues
ruchI9897 Jul 14, 2026
7fda70c
icon fix
ruchI9897 Jul 14, 2026
ea7fc28
fix
ruchI9897 Jul 14, 2026
06ce824
cleanup
ruchI9897 Jul 15, 2026
446e95e
cleanup: remove dead code and fix SSR/credential issues
ruchI9897 Jul 16, 2026
2cffacc
feat: make FloatingAssistant panel horizontally resizable
ruchI9897 Jul 16, 2026
e4f429c
fix: prevent SSR build crash from useFloatingAssistant outside provider
ruchI9897 Jul 16, 2026
62fc56a
fix: set panel resize minimum width to 360px
ruchI9897 Jul 16, 2026
7b508aa
fixes
ruchI9897 Jul 16, 2026
7f8a748
fixes
ruchI9897 Jul 16, 2026
9c65d13
cleanup: remove dead code and fix SSR/credential issues
ruchI9897 Jul 16, 2026
1233123
fix: add missing arrow-up icon to inline SVG sprite
ruchI9897 Jul 16, 2026
a3fb633
fix: set stop button icon color to white
ruchI9897 Jul 16, 2026
3808af0
fix: multiple FloatingAssistant UI fixes
ruchI9897 Jul 16, 2026
3f16793
fix: thumb icon colors and gen-summary cursor
ruchI9897 Jul 16, 2026
f0b8d08
fix
ruchI9897 Jul 16, 2026
39834aa
fix
ruchI9897 Jul 16, 2026
785ec03
hydration fix
ruchI9897 Jul 17, 2026
634d569
fix: add wrapRootElement to gatsby-ssr.js to match gatsby-browser.js
ruchI9897 Jul 17, 2026
0887f57
fix: mount FloatingAssistant as standalone React root to fix hydration
ruchI9897 Jul 17, 2026
a1a7fb0
fix: use dynamic imports in gatsby-browser.js to prevent SSR crash
ruchI9897 Jul 17, 2026
0247373
chore: cleanup for PR review
ruchI9897 Jul 17, 2026
c750a17
cleanup
ruchI9897 Jul 17, 2026
4601248
npm token
ruchI9897 Jul 17, 2026
d041c43
css fix
ruchI9897 Jul 17, 2026
b80c3d9
fixed
ruchI9897 Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
registry = 'https://registry.npmjs.org/'
@thoughtspot:registry=https://packagecloud.io/modeanalytics/tse/npm/
//packagecloud.io/modeanalytics/tse/npm/:_authToken=${PACKAGECLOUD_TOKEN}
31 changes: 31 additions & 0 deletions gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
exports.onClientEntry = () => {
// Dynamic imports so @thoughtspot/radiant-react (which reads `window` at
// module-load time) is never evaluated during Gatsby's SSR build.
Promise.all([
import('./src/contexts/FloatingAssistantContext'),
import('./src/components/FloatingAssistant'),
import('react'),
import('react-dom/client'),
]).then(([
{ FloatingAssistantProvider },
{ default: FloatingAssistant },
React,
ReactDOM,
]) => {
const container = document.createElement('div');
container.id = 'floating-assistant-root';
document.body.appendChild(container);

ReactDOM.createRoot(container).render(
React.createElement(
FloatingAssistantProvider,
null,
React.createElement(FloatingAssistant)
)
);
});
};

exports.onRouteUpdate = ({ location }) => {
window.dispatchEvent(new CustomEvent('gatsby-route-update', { detail: { location } }));
};
11 changes: 11 additions & 0 deletions gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const React = require('react');
const RADIANT_SPRITE = require('./src/components/FloatingAssistant/radiantSprite');

exports.onRenderBody = ({ setPreBodyComponents }) => {
setPreBodyComponents([
React.createElement('div', {
key: 'radiant-sprite',
dangerouslySetInnerHTML: { __html: RADIANT_SPRITE },
}),
]);
};
5,798 changes: 4,289 additions & 1,509 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@
"react-dom": "> 16.8.0"
},
"dependencies": {
"@thoughtspot/radiant-react": "^1.0.0-beta",
"@types/dompurify": "^3.0.5",
"@vercel/analytics": "^1.0.2",
"algoliasearch": "^4.10.5",
"cheerio": "^1.2.0",
"classnames": "^2.3.1",
"dompurify": "^3.3.3",
"eventemitter3": "^4.0.7",
"gatsby-plugin-vercel": "^1.0.3",
"gatsby-source-git": "^1.1.0",
"html-react-parser": "^1.4.12",
"lodash": "^4.17.21",
"marked": "^18.0.0",
"mixpanel-browser": "^2.45.0",
"react-helmet": "^6.1.0",
"turndown": "^7.2.4",
Expand Down
19 changes: 17 additions & 2 deletions src/components/Document/helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,20 @@ export const customizeDocContent = () => {
langSpan.innerText = rawLang;
header.appendChild(langSpan);

/* Copy button (right) */
/* Right group: CTA + copy button */
const rightGroup = document.createElement('div');
rightGroup.classList.add('code-block-header-actions');

const ctaLink = document.createElement('button');
ctaLink.classList.add('ctaButton');
ctaLink.innerText = 'Ask SpotterCode';
ctaLink.addEventListener('click', () => {
const code = copySource.innerText.trim();
window.dispatchEvent(new CustomEvent('spotter-code-ask', { detail: { quotedText: code } }));
});
rightGroup.appendChild(ctaLink);

/* Copy button — icon style */
const buttonElement = document.createElement('button');
buttonElement.setAttribute('class', 'copyButton');
buttonElement.setAttribute('aria-label', t('CODE_COPY_BTN_HOVER_TEXT'));
Expand All @@ -90,7 +103,9 @@ export const customizeDocContent = () => {
buttonElement.appendChild(imageElement);

enableCopyToClipboard(buttonElement, copySource);
header.appendChild(buttonElement);
rightGroup.appendChild(buttonElement);

header.appendChild(rightGroup);

/* ── Wrap pre in code-block-wrapper ── */
const wrapper = document.createElement('div');
Expand Down
156 changes: 122 additions & 34 deletions src/components/Document/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,97 @@
@import '../../assets/styles/admonition.scss';
@import '../../assets/styles/grid.scss';

@keyframes selection-cta-border-spin {
from { --selection-cta-angle: 0deg; }
to { --selection-cta-angle: 360deg; }
}

@property --selection-cta-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}

.selection-cta-button {
position: fixed;
z-index: 1000;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
font-size: 13px;
font-weight: 500;
color: #1d232f !important;
background: #fff;
border-radius: 100px;
text-decoration: none;
white-space: nowrap;
pointer-events: all;
box-shadow:
0 4px 16px 0 rgba(25, 35, 49, 0.14),
0 1px 4px 0 rgba(25, 35, 49, 0.06);
transition: box-shadow 0.15s ease;
font-family: 'Optimo-Plain', sans-serif;
border: 1px solid transparent;
isolation: isolate;

// Sparkle icon
&::before {
content: '';
display: inline-block;
width: 16px;
height: 16px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 27 27' fill='%232770EF' stroke='none'%3E%3Cpath fill='%232770EF' stroke='none' d='M8.25809 7.21109C8.83155 5.59342 11.1191 5.59349 11.6927 7.21109L13.4163 12.0753C13.4919 12.2885 13.6602 12.4568 13.8733 12.5324L18.7376 14.256C20.3555 14.8294 20.3555 17.1172 18.7376 17.6906L13.8733 19.4142C13.6601 19.4898 13.4918 19.658 13.4163 19.8712L11.6927 24.7355C11.1191 26.353 8.83157 26.3531 8.25809 24.7355L6.53445 19.8712C6.4589 19.658 6.29064 19.4898 6.07742 19.4142L1.21316 17.6906C-0.404397 17.1171 -0.404379 14.8295 1.21316 14.256L6.07742 12.5324C6.29058 12.4568 6.45883 12.2885 6.53445 12.0753L8.25809 7.21109ZM20.2805 0.49136C20.6395 -0.163697 21.6064 -0.163877 21.9651 0.49136L22.0315 0.642727L22.888 3.05972L25.3059 3.91616C26.1625 4.21966 26.1622 5.43079 25.3059 5.73452L22.888 6.59097L22.0315 9.00894C21.7279 9.86544 20.5167 9.86552 20.2132 9.00894L19.3567 6.59097L16.9397 5.73452C16.0831 5.43098 16.0831 4.21969 16.9397 3.91616L19.3567 3.05972L20.2132 0.642727L20.2805 0.49136Z'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
flex-shrink: 0;
position: relative;
z-index: 1;
}

// Animated gradient border via mask — shows only the 2px ring
&::after {
content: '';
position: absolute;
inset: -2px;
border-radius: inherit;
background: conic-gradient(
from var(--selection-cta-angle),
#8C62F5,
#48D1E0,
#2770EF,
#8C62F5
);
// Cut out the inner area so only the border ring is visible
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
padding: 2px;
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
}

&:hover {
color: #1d232f !important;
text-decoration: none;
box-shadow:
0 4px 16px 0 rgba(25, 35, 49, 0.14),
0 1px 4px 0 rgba(25, 35, 49, 0.06);

&::after {
opacity: 1;
animation: selection-cta-border-spin 2s linear infinite;
}
}
}

.documentWrapper {
width: calc(98%);
color: var(--primary-color);
Expand Down Expand Up @@ -102,52 +193,49 @@
font-family: $font-family-code;
}

.code-block-header-actions {
display: flex;
align-items: center;
gap: 8px;
}

.ctaButton {
display: inline-flex;
align-items: center;
padding: 4px 10px;
font-size: 12px;
font-weight: 500;
color: #2770ef;
background: transparent;
border: none;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
font-family: $font-family-doc;
transition: opacity 0.15s ease;
&:hover { opacity: 0.75; }
}

.copyButton {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
padding: 0 8px;
height: 28px;
padding: 0;
font-size: 13px;
font-weight: 500;
color: #fff;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 5px;
cursor: pointer;
white-space: nowrap;
font-family: $font-family-doc;
transition: background 0.15s ease, color 0.15s ease;
position: relative;

&:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}

.copyIcon {
height: 14px;
width: 14px;
display: block;
flex-shrink: 0;
}

/* Tooltip floats below — absolutely positioned so it doesn't shift layout */
.tooltip {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 10;
pointer-events: none;

.tooltiptext {
display: block;
background: #1d232f;
color: #fff;
font-size: 11px;
padding: 3px 8px;
border-radius: 4px;
white-space: nowrap;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}
}
&:hover { background: rgba(255, 255, 255, 0.1); }
&.copied { color: #0d9f6e; border-color: #0d9f6e; }
}
}

Expand Down
66 changes: 65 additions & 1 deletion src/components/Document/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import './index.scss';
import { customizeDocContent, addScrollListener } from './helper';
import Footer from '../Footer';
Expand All @@ -18,6 +18,57 @@ const Document = (props: {
breadcrumsData: any;
markdownBody?: string;
}) => {
const openAssistantWithQuote = (text: string) => {
window.dispatchEvent(new CustomEvent('spotter-code-ask', { detail: { quotedText: text } }));
};
const [selectionPos, setSelectionPos] = useState<{ top: number; left: number } | null>(null);
const selectionRef = useRef<string>('');

useEffect(() => {
let mouseDownX = 0;
let mouseDownY = 0;

const handleMouseUp = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (target.closest('.selection-cta-button')) 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;
if (!moved) return;

const selection = window.getSelection();
const text = selection?.toString().trim() || '';
if (!text) {
setSelectionPos(null);
return;
}
const range = selection!.getRangeAt(0);
const rect = range.getBoundingClientRect();
selectionRef.current = text;
const HEADER_HEIGHT = 108; // main header (60) + secondary header (48)
const BUTTON_HEIGHT = 36;
const rawTop = rect.top - BUTTON_HEIGHT - 6;
setSelectionPos({
top: Math.max(HEADER_HEIGHT + 4, rawTop),
left: Math.max(8, e.clientX - 80),
});
};

const handleMouseDown = (e: MouseEvent) => {
mouseDownX = e.clientX;
mouseDownY = e.clientY;
if ((e.target as HTMLElement).closest('.selection-cta-button')) return;
setSelectionPos(null);
};

document.addEventListener('mouseup', handleMouseUp);
document.addEventListener('mousedown', handleMouseDown);
return () => {
document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('mousedown', handleMouseDown);
};
}, []);

useEffect(() => {
customizeDocContent();
}, [props.docContent]);
Expand Down Expand Up @@ -131,6 +182,19 @@ const Document = (props: {
className="documentWrapper"
style={!props.shouldShowRightNav ? { width: '100%' } : undefined}
>
{selectionPos && (
<button
className="selection-cta-button"
style={{ top: selectionPos.top, left: selectionPos.left }}
onClick={() => {
const selected = selectionRef.current;
setSelectionPos(null);
openAssistantWithQuote(selected);
}}
>
Ask SpotterCode
</button>
)}
{!isHomePage && (
<Breadcrums
breadcrumsData={props.breadcrumsData}
Expand Down
Loading