| import type { ExpandedQuery } from '../types'; | |
| import { InfoTooltip } from './PipelineView'; | |
| interface ExpansionColumnState { | |
| status: 'idle' | 'running' | 'done' | 'error'; | |
| data?: ExpandedQuery; | |
| error?: string; | |
| } | |
| interface ExpansionColumnProps { | |
| state: ExpansionColumnState; | |
| accent: string; | |
| info: string; | |
| } | |
| function Spinner({ color }: { color: string }) { | |
| return ( | |
| <span style={{ | |
| display: 'inline-block', | |
| width: '14px', | |
| height: '14px', | |
| border: '2px solid var(--border)', | |
| borderTopColor: color, | |
| borderRadius: '50%', | |
| animation: 'spin 0.7s linear infinite', | |
| }} /> | |
| ); | |
| } | |
| function CompactCard({ label, content }: { label: string; content: string | string[] }) { | |
| const text = Array.isArray(content) ? content.join('\n') : content; | |
| return ( | |
| <div style={{ | |
| background: 'var(--bg-card)', | |
| border: '1px solid var(--border)', | |
| borderRadius: '5px', | |
| padding: '0.4rem 0.6rem', | |
| marginBottom: '0.3rem', | |
| }}> | |
| <div style={{ | |
| fontSize: '0.65rem', | |
| fontWeight: 700, | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| color: 'var(--text-muted)', | |
| textTransform: 'uppercase', | |
| letterSpacing: '0.06em', | |
| marginBottom: '0.2rem', | |
| }}> | |
| {label} | |
| </div> | |
| <div style={{ | |
| fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace", | |
| fontSize: '0.68rem', | |
| color: 'var(--text)', | |
| lineHeight: 1.5, | |
| whiteSpace: 'pre-wrap', | |
| wordBreak: 'break-word', | |
| }}> | |
| {text} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function VariantSummary({ data }: { data: ExpandedQuery }) { | |
| const count = | |
| (data.lex.trim() ? 1 : 0) + | |
| data.vec.length + | |
| (data.hyde.trim() ? 1 : 0); | |
| if (count === 0) return null; | |
| return ( | |
| <div style={{ | |
| fontSize: '0.72rem', | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| color: 'var(--text-secondary)', | |
| marginBottom: '0.4rem', | |
| padding: '0.3rem 0.5rem', | |
| background: 'var(--bg-card)', | |
| border: '1px solid var(--border)', | |
| borderRadius: '5px', | |
| }}> | |
| Expanded into <strong style={{ color: 'var(--text)' }}>{count}</strong> search variant{count !== 1 ? 's' : ''} | |
| </div> | |
| ); | |
| } | |
| export default function ExpansionColumn({ state, accent, info }: ExpansionColumnProps) { | |
| const isIdle = state.status === 'idle'; | |
| const isRunning = state.status === 'running'; | |
| const isDone = state.status === 'done'; | |
| const isError = state.status === 'error'; | |
| return ( | |
| <div style={{ opacity: isIdle ? 0.45 : 1, transition: 'opacity 0.3s' }}> | |
| <div style={{ | |
| display: 'flex', | |
| alignItems: 'center', | |
| gap: '0.4rem', | |
| marginBottom: '0.75rem', | |
| paddingBottom: '0.5rem', | |
| borderBottom: '1px solid var(--stage-divider)', | |
| }}> | |
| <span style={{ | |
| width: '3px', | |
| height: '14px', | |
| borderRadius: '2px', | |
| background: accent, | |
| flexShrink: 0, | |
| }} /> | |
| <h3 style={{ | |
| margin: 0, | |
| fontSize: '0.78rem', | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| fontWeight: 700, | |
| color: accent, | |
| textTransform: 'uppercase', | |
| letterSpacing: '0.05em', | |
| }}> | |
| Query Expansion | |
| </h3> | |
| {isRunning && <Spinner color={accent} />} | |
| <InfoTooltip text={info} /> | |
| </div> | |
| {isIdle && ( | |
| <p style={{ | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| fontSize: '0.75rem', | |
| color: 'var(--text-muted)', | |
| margin: 0, | |
| }}> | |
| Awaiting query... | |
| </p> | |
| )} | |
| {isRunning && ( | |
| <p style={{ | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| fontSize: '0.75rem', | |
| color: 'var(--text-secondary)', | |
| margin: 0, | |
| fontStyle: 'italic', | |
| }}> | |
| Generating... | |
| </p> | |
| )} | |
| {isError && ( | |
| <div style={{ | |
| padding: '0.45rem 0.6rem', | |
| background: 'var(--error-bg)', | |
| border: '1px solid var(--error-border)', | |
| borderRadius: '5px', | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| fontSize: '0.75rem', | |
| color: '#c62828', | |
| }}> | |
| {state.error} | |
| </div> | |
| )} | |
| {isDone && state.data && ( | |
| <> | |
| {state.data.note && ( | |
| <div style={{ | |
| padding: '0.35rem 0.5rem', | |
| marginBottom: '0.3rem', | |
| background: 'var(--bg-card)', | |
| border: '1px solid var(--border)', | |
| borderRadius: '5px', | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| fontSize: '0.72rem', | |
| color: 'var(--text-secondary)', | |
| lineHeight: 1.45, | |
| }}> | |
| {state.data.note} | |
| </div> | |
| )} | |
| <VariantSummary data={state.data} /> | |
| {state.data.lex.trim() && ( | |
| <CompactCard label="Lex" content={state.data.lex} /> | |
| )} | |
| {state.data.vec.length > 0 && ( | |
| <CompactCard label="Vec" content={state.data.vec} /> | |
| )} | |
| {state.data.hyde.trim() && ( | |
| <CompactCard label="HyDE" content={state.data.hyde} /> | |
| )} | |
| </> | |
| )} | |
| </div> | |
| ); | |
| } | |