| 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> |
| ); |
| } |
|
|