feat: Go backend, enhanced search, new widgets, Docker deploy
Major changes: - Add Go backend (backend/) with microservices architecture - Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler, proxy-manager, media-search, fastClassifier, language detection - New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard, UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions - Improved discover-svc with discover-db integration - Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md) - Library-svc: project_id schema migration - Remove deprecated finance-svc and travel-svc - Localization improvements across services Made-with: Cursor
This commit is contained in:
146
backend/webui/src/components/Citation.tsx
Normal file
146
backend/webui/src/components/Citation.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
'use client';
|
||||
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||
import type { Citation as CitationType } from '@/lib/types';
|
||||
|
||||
interface CitationProps {
|
||||
citation: CitationType;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export function Citation({ citation, compact }: CitationProps) {
|
||||
if (compact) {
|
||||
return (
|
||||
<a
|
||||
href={citation.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-5 h-5 text-2xs font-medium bg-cream-300/10 hover:bg-cream-300/20 text-cream-300 border border-cream-400/20 rounded transition-colors"
|
||||
>
|
||||
{citation.index}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<a
|
||||
href={citation.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-2.5 py-1.5 bg-navy-800/40 hover:bg-navy-800/60 border border-navy-700/30 hover:border-cream-400/20 rounded-lg transition-all group"
|
||||
>
|
||||
<span className="w-4 h-4 rounded bg-cream-300/10 text-cream-300 flex items-center justify-center text-2xs font-medium">
|
||||
{citation.index}
|
||||
</span>
|
||||
{citation.favicon && (
|
||||
<img
|
||||
src={citation.favicon}
|
||||
alt=""
|
||||
className="w-3.5 h-3.5 rounded"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="text-xs text-cream-400/80 group-hover:text-cream-200 max-w-[120px] truncate transition-colors">
|
||||
{citation.domain}
|
||||
</span>
|
||||
<ExternalLink className="w-2.5 h-2.5 text-cream-500/50 group-hover:text-cream-400/70 transition-colors" />
|
||||
</a>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content
|
||||
side="top"
|
||||
className="max-w-[300px] p-4 bg-navy-800/95 backdrop-blur-xl border border-navy-700/50 rounded-xl shadow-xl z-50"
|
||||
sideOffset={8}
|
||||
>
|
||||
<p className="font-medium text-sm text-cream-100 line-clamp-2 mb-2">
|
||||
{citation.title}
|
||||
</p>
|
||||
{citation.snippet && (
|
||||
<p className="text-xs text-cream-400/70 line-clamp-3 mb-3">
|
||||
{citation.snippet}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex items-center gap-2 text-2xs text-cream-500/60">
|
||||
{citation.favicon && (
|
||||
<img
|
||||
src={citation.favicon}
|
||||
alt=""
|
||||
className="w-3 h-3 rounded"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="truncate">{citation.domain}</span>
|
||||
</div>
|
||||
<Tooltip.Arrow className="fill-navy-800" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
interface CitationListProps {
|
||||
citations: CitationType[];
|
||||
maxVisible?: number;
|
||||
}
|
||||
|
||||
export function CitationList({ citations, maxVisible = 6 }: CitationListProps) {
|
||||
if (!citations || citations.length === 0) return null;
|
||||
|
||||
const visible = citations.slice(0, maxVisible);
|
||||
const remaining = citations.length - maxVisible;
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{visible.map((citation) => (
|
||||
<Citation key={citation.index} citation={citation} />
|
||||
))}
|
||||
{remaining > 0 && (
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<button className="text-xs text-cream-500/70 hover:text-cream-300 px-2.5 py-1.5 rounded-lg hover:bg-navy-800/30 transition-colors">
|
||||
+{remaining} ещё
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content
|
||||
side="top"
|
||||
className="max-w-[320px] p-3 bg-navy-800/95 backdrop-blur-xl border border-navy-700/50 rounded-xl shadow-xl z-50"
|
||||
sideOffset={8}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
{citations.slice(maxVisible).map((citation) => (
|
||||
<a
|
||||
key={citation.index}
|
||||
href={citation.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 p-2 rounded-lg hover:bg-navy-700/50 transition-colors"
|
||||
>
|
||||
<span className="w-4 h-4 rounded bg-cream-300/10 text-cream-300 flex items-center justify-center text-2xs font-medium flex-shrink-0">
|
||||
{citation.index}
|
||||
</span>
|
||||
<span className="text-xs text-cream-200 truncate">
|
||||
{citation.title}
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<Tooltip.Arrow className="fill-navy-800" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user