2026-04-05 22:44:45 -05:00
|
|
|
import {
|
|
|
|
|
useCallback,
|
|
|
|
|
useEffect,
|
|
|
|
|
useRef,
|
|
|
|
|
useState,
|
|
|
|
|
type KeyboardEvent,
|
|
|
|
|
} from "react";
|
|
|
|
|
import {
|
|
|
|
|
MessageSquareText,
|
|
|
|
|
Send,
|
|
|
|
|
Trash2,
|
|
|
|
|
Brain,
|
|
|
|
|
Eye,
|
|
|
|
|
CheckCircle,
|
|
|
|
|
ChevronDown,
|
|
|
|
|
ChevronRight,
|
|
|
|
|
Loader2,
|
fix: comprehensive QA — resolve 13 bugs, add UX improvements across all services
Client SDK: add .catch() to graphRagStreaming/documentRagStreaming (silent timeout),
null-guard JSON.parse in getPrompts/getSystemPrompt/getPrompt.
Backend: implement "getvalues" config operation for token costs, null-check
createTerm() in FalkorDB triples query, add knowledge-cores service entrypoint
and Docker entry, return proper HTTP 400/404 for gateway error responses.
Workbench: cancel button + elapsed timer for chat, clear agent spinner on error,
flow dialog inline validation, responsive header wrapping, knowledge cores
loading timeout, sidebar/page naming consistency, theme toggle indicator.
Infrastructure: enable Grafana Explore for viewers, add gateway Prometheus
scrape target, fix RAG pipeline dashboard layout (6 panels visible),
filter Service Health to configured targets only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 05:20:10 -05:00
|
|
|
X,
|
2026-04-10 07:48:01 -05:00
|
|
|
AlertTriangle,
|
2026-04-05 22:44:45 -05:00
|
|
|
} from "lucide-react";
|
|
|
|
|
import Markdown from "react-markdown";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import { useConversation, type ChatMessage } from "@/hooks/use-conversation";
|
|
|
|
|
import { useChat } from "@/hooks/use-chat";
|
|
|
|
|
import { useSettings } from "@/providers/settings-provider";
|
|
|
|
|
import { useProgressStore } from "@/hooks/use-progress-store";
|
|
|
|
|
import { AutoTextarea } from "@/components/ui/textarea";
|
2026-04-12 02:55:46 -05:00
|
|
|
import { MessageActions } from "@/components/chat/message-actions";
|
|
|
|
|
import { ExplainGraph } from "@/components/chat/explain-graph";
|
2026-04-05 22:44:45 -05:00
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Constants
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const MODES = [
|
|
|
|
|
{ value: "graph-rag" as const, label: "Graph RAG" },
|
|
|
|
|
{ value: "document-rag" as const, label: "Doc RAG" },
|
|
|
|
|
{ value: "agent" as const, label: "Agent" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Agent phase section (collapsible)
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function AgentPhaseBlock({
|
|
|
|
|
phase,
|
|
|
|
|
icon,
|
|
|
|
|
label,
|
|
|
|
|
content,
|
|
|
|
|
isActive,
|
|
|
|
|
}: {
|
|
|
|
|
phase: string;
|
|
|
|
|
icon: React.ReactNode;
|
|
|
|
|
label: string;
|
|
|
|
|
content: string;
|
|
|
|
|
isActive: boolean;
|
|
|
|
|
}) {
|
|
|
|
|
const [expanded, setExpanded] = useState(false);
|
|
|
|
|
|
|
|
|
|
if (!content && !isActive) return null;
|
|
|
|
|
|
|
|
|
|
const phaseColors: Record<string, string> = {
|
|
|
|
|
think: "border-amber-500/30 bg-amber-500/5",
|
|
|
|
|
observe: "border-sky-500/30 bg-sky-500/5",
|
|
|
|
|
answer: "border-emerald-500/30 bg-emerald-500/5",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const badgeColors: Record<string, string> = {
|
|
|
|
|
think: "bg-amber-500/20 text-amber-400",
|
|
|
|
|
observe: "bg-sky-500/20 text-sky-400",
|
|
|
|
|
answer: "bg-emerald-500/20 text-emerald-400",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"rounded-md border",
|
|
|
|
|
phaseColors[phase] ?? "border-border bg-surface-100",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setExpanded((p) => !p)}
|
2026-04-12 00:59:20 -05:00
|
|
|
aria-expanded={expanded}
|
2026-04-05 22:44:45 -05:00
|
|
|
className="flex w-full items-center gap-2 px-3 py-2 text-left text-xs font-medium text-fg-muted"
|
|
|
|
|
>
|
|
|
|
|
{expanded ? (
|
|
|
|
|
<ChevronDown className="h-3 w-3 shrink-0" />
|
|
|
|
|
) : (
|
|
|
|
|
<ChevronRight className="h-3 w-3 shrink-0" />
|
|
|
|
|
)}
|
|
|
|
|
{icon}
|
|
|
|
|
<span
|
|
|
|
|
className={cn(
|
|
|
|
|
"rounded px-1.5 py-0.5",
|
|
|
|
|
badgeColors[phase] ?? "bg-surface-200 text-fg-muted",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{label}
|
|
|
|
|
</span>
|
|
|
|
|
{isActive && (
|
|
|
|
|
<Loader2 className="ml-auto h-3 w-3 animate-spin text-fg-subtle" />
|
|
|
|
|
)}
|
|
|
|
|
</button>
|
|
|
|
|
{expanded && content && (
|
|
|
|
|
<div className="border-t border-border/50 px-3 py-2 text-xs leading-relaxed text-fg-muted">
|
|
|
|
|
<p className="whitespace-pre-wrap">{content}</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Single message bubble
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
2026-04-12 02:55:46 -05:00
|
|
|
function MessageBubble({ msg, collection }: { msg: ChatMessage; collection: string }) {
|
2026-04-05 22:44:45 -05:00
|
|
|
const isUser = msg.role === "user";
|
|
|
|
|
const hasAgentPhases = msg.agentPhases != null;
|
2026-04-10 07:48:01 -05:00
|
|
|
const isError = !isUser && msg.content.startsWith("Error:");
|
2026-04-05 22:44:45 -05:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"rounded-lg px-4 py-3 text-sm leading-relaxed",
|
|
|
|
|
isUser
|
|
|
|
|
? "ml-auto max-w-[80%] bg-brand-700/30 text-fg"
|
2026-04-10 07:48:01 -05:00
|
|
|
: isError
|
|
|
|
|
? "mr-auto max-w-[80%] border border-error/30 bg-error/10 text-error"
|
|
|
|
|
: "mr-auto max-w-[80%] bg-surface-100 text-fg",
|
2026-04-05 22:44:45 -05:00
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{/* Agent phase blocks (only for agent messages) */}
|
|
|
|
|
{hasAgentPhases && msg.agentPhases && (
|
|
|
|
|
<div className="mb-2 space-y-1.5">
|
|
|
|
|
<AgentPhaseBlock
|
|
|
|
|
phase="think"
|
|
|
|
|
icon={<Brain className="h-3 w-3" />}
|
|
|
|
|
label="Thinking"
|
|
|
|
|
content={msg.agentPhases.think}
|
|
|
|
|
isActive={msg.activePhase === "think"}
|
|
|
|
|
/>
|
|
|
|
|
<AgentPhaseBlock
|
|
|
|
|
phase="observe"
|
|
|
|
|
icon={<Eye className="h-3 w-3" />}
|
|
|
|
|
label="Observing"
|
|
|
|
|
content={msg.agentPhases.observe}
|
|
|
|
|
isActive={msg.activePhase === "observe"}
|
|
|
|
|
/>
|
|
|
|
|
{msg.agentPhases.answer && (
|
|
|
|
|
<div className="flex items-center gap-1.5 px-1 pt-1 text-xs text-emerald-400">
|
|
|
|
|
<CheckCircle className="h-3 w-3" />
|
|
|
|
|
<span className="font-medium">Answer</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Main content (markdown for assistant, plain for user) */}
|
|
|
|
|
{isUser ? (
|
|
|
|
|
<p className="whitespace-pre-wrap">{msg.content}</p>
|
2026-04-10 07:48:01 -05:00
|
|
|
) : isError ? (
|
|
|
|
|
<div className="flex items-start gap-2">
|
|
|
|
|
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
|
|
|
|
|
<p className="whitespace-pre-wrap">{msg.content}</p>
|
|
|
|
|
</div>
|
2026-04-05 22:44:45 -05:00
|
|
|
) : (
|
fix: comprehensive QA audit — light mode, accessibility, error handling, code quality
- Fix light mode: theme-aware graph node labels, remove prose-invert for
theme-safe markdown, add brand/semantic color overrides for light backgrounds
- Add 404 catch-all route redirecting unknown paths to /chat
- FalkorDB: add .catch() to connectPromise, add ensureConnected() to all
store methods (createLiteral, relateNode, relateLiteral, deleteCollection)
- Accessibility: dialog role/aria-modal, toast aria-live, dismiss/zoom/search
button aria-labels, close panel aria-label
- Lazy-load ForceGraph2D (splits 189KB into separate chunk, main bundle -26%)
- Cap conversation localStorage at 200 messages to prevent quota overflow
- Fix pnpm test: add --passWithNoTests to cli/mcp packages
- Add upload error notification instead of silent catch
- Remove unused class-variance-authority dep and dead tabs.tsx component
- Add @types/node to flow package devDependencies
- Remove stale FIXME comment in messages.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:15:59 -05:00
|
|
|
<div className="prose prose-sm max-w-none text-fg prose-headings:text-fg prose-strong:text-fg prose-p:my-1 prose-a:text-brand-400 prose-pre:bg-surface-200 prose-pre:text-fg prose-code:text-brand-300">
|
2026-04-05 22:44:45 -05:00
|
|
|
<Markdown>{msg.content || (msg.isStreaming ? "" : "(empty)")}</Markdown>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Streaming indicator */}
|
|
|
|
|
{msg.isStreaming && (
|
|
|
|
|
<span className="mt-1 inline-block h-2 w-2 animate-pulse rounded-full bg-brand-400" />
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Token metadata */}
|
|
|
|
|
{msg.metadata && (
|
|
|
|
|
<div className="mt-2 flex items-center gap-3 text-[10px] text-fg-subtle">
|
|
|
|
|
{msg.metadata.model && <span>{msg.metadata.model}</span>}
|
|
|
|
|
{msg.metadata.inTokens != null && (
|
|
|
|
|
<span>in: {msg.metadata.inTokens}</span>
|
|
|
|
|
)}
|
|
|
|
|
{msg.metadata.outTokens != null && (
|
|
|
|
|
<span>out: {msg.metadata.outTokens}</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-12 02:55:46 -05:00
|
|
|
|
|
|
|
|
{/* Explainability graph */}
|
|
|
|
|
{!isUser && !isError && !msg.isStreaming && msg.explainEvents && msg.explainEvents.length > 0 && (
|
|
|
|
|
<ExplainGraph explainEvents={msg.explainEvents} collection={collection} />
|
|
|
|
|
)}
|
2026-04-05 22:44:45 -05:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Chat page
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
export default function ChatPage() {
|
|
|
|
|
const messages = useConversation((s) => s.messages);
|
|
|
|
|
const input = useConversation((s) => s.input);
|
|
|
|
|
const chatMode = useConversation((s) => s.chatMode);
|
|
|
|
|
const setInput = useConversation((s) => s.setInput);
|
|
|
|
|
const setChatMode = useConversation((s) => s.setChatMode);
|
|
|
|
|
const clearMessages = useConversation((s) => s.clearMessages);
|
2026-04-12 02:55:46 -05:00
|
|
|
const { submitMessage, cancelRequest, regenerateLastMessage } = useChat();
|
|
|
|
|
const deleteMessage = useConversation((s) => s.deleteMessage);
|
2026-04-05 22:44:45 -05:00
|
|
|
const collection = useSettings((s) => s.settings.collection);
|
|
|
|
|
const isLoading = useProgressStore((s) => s.isLoading);
|
|
|
|
|
|
|
|
|
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
fix: comprehensive QA — resolve 13 bugs, add UX improvements across all services
Client SDK: add .catch() to graphRagStreaming/documentRagStreaming (silent timeout),
null-guard JSON.parse in getPrompts/getSystemPrompt/getPrompt.
Backend: implement "getvalues" config operation for token costs, null-check
createTerm() in FalkorDB triples query, add knowledge-cores service entrypoint
and Docker entry, return proper HTTP 400/404 for gateway error responses.
Workbench: cancel button + elapsed timer for chat, clear agent spinner on error,
flow dialog inline validation, responsive header wrapping, knowledge cores
loading timeout, sidebar/page naming consistency, theme toggle indicator.
Infrastructure: enable Grafana Explore for viewers, add gateway Prometheus
scrape target, fix RAG pipeline dashboard layout (6 panels visible),
filter Service Health to configured targets only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 05:20:10 -05:00
|
|
|
// Elapsed time counter while loading
|
|
|
|
|
const [elapsed, setElapsed] = useState(0);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!isLoading) {
|
|
|
|
|
setElapsed(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const interval = setInterval(() => setElapsed((e) => e + 1), 1000);
|
|
|
|
|
return () => clearInterval(interval);
|
|
|
|
|
}, [isLoading]);
|
|
|
|
|
|
2026-04-05 22:44:45 -05:00
|
|
|
// Auto-scroll to bottom when messages change
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
|
|
|
}, [messages]);
|
|
|
|
|
|
|
|
|
|
const handleSubmit = useCallback(() => {
|
|
|
|
|
if (input.trim()) {
|
|
|
|
|
submitMessage({ input });
|
|
|
|
|
}
|
|
|
|
|
}, [input, submitMessage]);
|
|
|
|
|
|
|
|
|
|
const handleKeyDown = useCallback(
|
|
|
|
|
(e: KeyboardEvent<HTMLTextAreaElement>) => {
|
|
|
|
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleSubmit();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[handleSubmit],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex h-full flex-col">
|
|
|
|
|
{/* Header */}
|
fix: comprehensive QA — resolve 13 bugs, add UX improvements across all services
Client SDK: add .catch() to graphRagStreaming/documentRagStreaming (silent timeout),
null-guard JSON.parse in getPrompts/getSystemPrompt/getPrompt.
Backend: implement "getvalues" config operation for token costs, null-check
createTerm() in FalkorDB triples query, add knowledge-cores service entrypoint
and Docker entry, return proper HTTP 400/404 for gateway error responses.
Workbench: cancel button + elapsed timer for chat, clear agent spinner on error,
flow dialog inline validation, responsive header wrapping, knowledge cores
loading timeout, sidebar/page naming consistency, theme toggle indicator.
Infrastructure: enable Grafana Explore for viewers, add gateway Prometheus
scrape target, fix RAG pipeline dashboard layout (6 panels visible),
filter Service Health to configured targets only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 05:20:10 -05:00
|
|
|
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
|
2026-04-05 22:44:45 -05:00
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<MessageSquareText className="h-6 w-6 text-brand-400" />
|
|
|
|
|
<h1 className="text-2xl font-bold text-fg">Chat</h1>
|
2026-04-10 07:48:01 -05:00
|
|
|
<span className="ml-2 rounded bg-surface-200 px-2 py-0.5 text-xs text-fg-muted">
|
2026-04-05 22:44:45 -05:00
|
|
|
{collection}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
fix: comprehensive QA — resolve 13 bugs, add UX improvements across all services
Client SDK: add .catch() to graphRagStreaming/documentRagStreaming (silent timeout),
null-guard JSON.parse in getPrompts/getSystemPrompt/getPrompt.
Backend: implement "getvalues" config operation for token costs, null-check
createTerm() in FalkorDB triples query, add knowledge-cores service entrypoint
and Docker entry, return proper HTTP 400/404 for gateway error responses.
Workbench: cancel button + elapsed timer for chat, clear agent spinner on error,
flow dialog inline validation, responsive header wrapping, knowledge cores
loading timeout, sidebar/page naming consistency, theme toggle indicator.
Infrastructure: enable Grafana Explore for viewers, add gateway Prometheus
scrape target, fix RAG pipeline dashboard layout (6 panels visible),
filter Service Health to configured targets only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 05:20:10 -05:00
|
|
|
<div className="flex flex-wrap items-center gap-2">
|
2026-04-05 22:44:45 -05:00
|
|
|
{/* Mode selector */}
|
fix: comprehensive a11y and contrast QA pass across workbench
Automated QA loop (6 parallel browser agents, 2 rounds) found and fixed
15 accessibility, contrast, and responsive issues across all 8 pages:
- WCAG contrast: light-mode warning (#854d0e), error (#b91c1c), toggle
off-state (surface-400), connection badge (fg-muted)
- ARIA: mode selector group+pressed, tab pattern ids+labelledby, nav
and aside labels, dialog focus-return, alert roles on banners
- Responsive: library header flex-wrap, search/button aria-labels
- Focus: NavLink visible ring, dialog close button ring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:26:28 -05:00
|
|
|
<div role="group" aria-label="Chat mode" className="flex rounded-lg border border-border bg-surface-100 p-0.5">
|
2026-04-05 22:44:45 -05:00
|
|
|
{MODES.map((mode) => (
|
|
|
|
|
<button
|
2026-04-12 02:55:46 -05:00
|
|
|
type="button"
|
2026-04-05 22:44:45 -05:00
|
|
|
key={mode.value}
|
|
|
|
|
onClick={() => setChatMode(mode.value)}
|
fix: comprehensive a11y and contrast QA pass across workbench
Automated QA loop (6 parallel browser agents, 2 rounds) found and fixed
15 accessibility, contrast, and responsive issues across all 8 pages:
- WCAG contrast: light-mode warning (#854d0e), error (#b91c1c), toggle
off-state (surface-400), connection badge (fg-muted)
- ARIA: mode selector group+pressed, tab pattern ids+labelledby, nav
and aside labels, dialog focus-return, alert roles on banners
- Responsive: library header flex-wrap, search/button aria-labels
- Focus: NavLink visible ring, dialog close button ring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:26:28 -05:00
|
|
|
aria-pressed={chatMode === mode.value}
|
2026-04-05 22:44:45 -05:00
|
|
|
className={cn(
|
|
|
|
|
"rounded-md px-3 py-1 text-xs font-medium transition-colors",
|
|
|
|
|
chatMode === mode.value
|
|
|
|
|
? "bg-brand-600 text-white"
|
|
|
|
|
: "text-fg-muted hover:text-fg",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{mode.label}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button
|
2026-04-07 06:33:22 -05:00
|
|
|
onClick={() => { cancelRequest(); clearMessages(); }}
|
2026-04-05 22:44:45 -05:00
|
|
|
className="rounded-lg p-2 text-fg-subtle hover:bg-surface-200 hover:text-fg"
|
|
|
|
|
title="Clear messages"
|
2026-04-07 06:33:22 -05:00
|
|
|
aria-label="Clear messages"
|
2026-04-05 22:44:45 -05:00
|
|
|
>
|
|
|
|
|
<Trash2 className="h-4 w-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Messages */}
|
2026-04-12 02:55:46 -05:00
|
|
|
<div className="flex-1 space-y-4 overflow-y-auto pb-4 pt-10">
|
2026-04-05 22:44:45 -05:00
|
|
|
{messages.length === 0 && (
|
|
|
|
|
<div className="flex flex-col items-center justify-center py-20 text-fg-subtle">
|
|
|
|
|
<MessageSquareText className="mb-3 h-10 w-10 opacity-30" />
|
|
|
|
|
<p>Send a message to start a conversation.</p>
|
|
|
|
|
<p className="mt-1 text-xs">
|
2026-04-12 02:55:46 -05:00
|
|
|
Mode: <span className="text-fg-muted">{MODES.find((m) => m.value === chatMode)?.label ?? chatMode}</span>
|
2026-04-05 22:44:45 -05:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-04-12 02:55:46 -05:00
|
|
|
{messages.map((msg, idx) => {
|
|
|
|
|
const isLastAssistant =
|
|
|
|
|
msg.role === "assistant" &&
|
|
|
|
|
idx === messages.length - 1;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div key={msg.id} className="group relative">
|
|
|
|
|
{!msg.isStreaming && (
|
|
|
|
|
<MessageActions
|
|
|
|
|
content={msg.content}
|
|
|
|
|
isLastAssistant={isLastAssistant}
|
|
|
|
|
onDelete={() => deleteMessage(msg.id)}
|
|
|
|
|
onRegenerate={isLastAssistant ? regenerateLastMessage : undefined}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<MessageBubble msg={msg} collection={collection} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
2026-04-05 22:44:45 -05:00
|
|
|
<div ref={scrollRef} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Loading indicator */}
|
|
|
|
|
{isLoading && (
|
|
|
|
|
<div className="flex items-center gap-2 pb-2 text-xs text-fg-subtle">
|
|
|
|
|
<Loader2 className="h-3 w-3 animate-spin" />
|
fix: comprehensive QA — resolve 13 bugs, add UX improvements across all services
Client SDK: add .catch() to graphRagStreaming/documentRagStreaming (silent timeout),
null-guard JSON.parse in getPrompts/getSystemPrompt/getPrompt.
Backend: implement "getvalues" config operation for token costs, null-check
createTerm() in FalkorDB triples query, add knowledge-cores service entrypoint
and Docker entry, return proper HTTP 400/404 for gateway error responses.
Workbench: cancel button + elapsed timer for chat, clear agent spinner on error,
flow dialog inline validation, responsive header wrapping, knowledge cores
loading timeout, sidebar/page naming consistency, theme toggle indicator.
Infrastructure: enable Grafana Explore for viewers, add gateway Prometheus
scrape target, fix RAG pipeline dashboard layout (6 panels visible),
filter Service Health to configured targets only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 05:20:10 -05:00
|
|
|
<span>Processing... {elapsed}s</span>
|
|
|
|
|
<button
|
|
|
|
|
onClick={cancelRequest}
|
|
|
|
|
className="flex items-center gap-1 rounded-lg px-3 py-1 text-xs text-red-400 hover:bg-surface-200"
|
|
|
|
|
>
|
|
|
|
|
<X className="h-3 w-3" />
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
2026-04-05 22:44:45 -05:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Input area */}
|
|
|
|
|
<div className="flex items-end gap-2 border-t border-border pt-4">
|
|
|
|
|
<AutoTextarea
|
|
|
|
|
value={input}
|
|
|
|
|
onChange={(e) => setInput(e.target.value)}
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
placeholder="Type your message... (Enter to send, Shift+Enter for new line)"
|
2026-04-10 07:48:01 -05:00
|
|
|
aria-label="Chat message"
|
2026-04-05 22:44:45 -05:00
|
|
|
maxRows={6}
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleSubmit}
|
|
|
|
|
disabled={!input.trim() || isLoading}
|
2026-04-07 06:33:22 -05:00
|
|
|
aria-label="Send message"
|
2026-04-05 22:44:45 -05:00
|
|
|
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-brand-600 text-white transition-colors hover:bg-brand-500 disabled:opacity-40"
|
|
|
|
|
>
|
|
|
|
|
<Send className="h-4 w-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|