diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ec50b83d..7e2b2ad8 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -37,7 +37,7 @@ function MainLayout() { const [navOpen, setNavOpen] = useState(!(isMobile || isTablet)); return ( -
+
{demo.header}
{t('newChat')}
{agent.name}
{t('manageAgents')}
{t('settings.label')}
{t('pageNotFound.message')}
{`(Draft)`}
{agent.description}
{t('agents.backToAll')}
{agent.last_used_at ? t('agents.logs.lastUsedAt') + ' ' + diff --git a/frontend/src/agents/AgentPreview.tsx b/frontend/src/agents/AgentPreview.tsx index 149bc83b..b1c1cb46 100644 --- a/frontend/src/agents/AgentPreview.tsx +++ b/frontend/src/agents/AgentPreview.tsx @@ -131,7 +131,7 @@ export default function AgentPreview() { autoFocus={false} />
{t('agents.preview.testMessage')}
{t('agents.description')}
{t('agents.noSearchResults')}
{t('agents.tryDifferentSearch')}
{t(`agents.sections.${config.id}.emptyState`)}
{t(`agents.sections.${config.id}.description`)}
{currentFolderId ? t('agents.folders.empty') @@ -587,7 +587,7 @@ function AgentSection({
{t('agents.form.preview.publishedPreview')}
{t('agents.shared.notFound')}
{t('tagline')}
by {agent.shared_metadata.shared_by}
Shared on{' '} {new Date(agent.shared_metadata.shared_at).toLocaleString( 'en-US', @@ -53,14 +53,14 @@ export default function SharedAgentCard({ agent }: { agent: Agent }) { )} {agent.tool_details && agent.tool_details.length > 0 && (
Connected Tools
@@ -53,13 +53,13 @@ export default function AgentTypeModal({
@@ -71,13 +71,13 @@ export default function AgentTypeModal({ handleSelect('workflow')} - className="hover:border-violets-are-blue hover:bg-violets-are-blue/5 dark:hover:border-violets-are-blue dark:hover:bg-violets-are-blue/10 group flex items-start gap-5 rounded-xl border-2 border-gray-200 p-5 text-left transition-all dark:border-[#2E2F34]" + className="hover:border-primary hover:bg-primary/5 dark:hover:border-primary dark:hover:bg-primary/10 group flex items-start gap-5 rounded-xl border-2 border-gray-200 p-5 text-left transition-all dark:border-border" > - + - + Workflow Agent diff --git a/frontend/src/agents/workflow/WorkflowBuilder.tsx b/frontend/src/agents/workflow/WorkflowBuilder.tsx index b1b5faf6..5092efe5 100644 --- a/frontend/src/agents/workflow/WorkflowBuilder.tsx +++ b/frontend/src/agents/workflow/WorkflowBuilder.tsx @@ -1333,12 +1333,12 @@ function WorkflowBuilderInner() { return ( <> - - + + @@ -1368,7 +1368,7 @@ function WorkflowBuilderInner() { {showWorkflowSettings && ( @@ -1378,7 +1378,7 @@ function WorkflowBuilderInner() { type="text" value={workflowName} onChange={(e) => setWorkflowName(e.target.value)} - className="focus:ring-purple-30 w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="focus:ring-ring w-full rounded-lg border border-border bg-card px-3 py-2 text-sm outline-none focus:ring-2 dark:text-white" placeholder="Enter workflow name" /> @@ -1389,7 +1389,7 @@ function WorkflowBuilderInner() { setWorkflowDescription(e.target.value)} - className="focus:ring-purple-30 w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="focus:ring-ring w-full rounded-lg border border-border bg-card px-3 py-2 text-sm outline-none focus:ring-2 dark:text-white" rows={3} placeholder="Describe what this workflow does" /> @@ -1419,14 +1419,14 @@ function WorkflowBuilderInner() { uploadText={[ { text: 'Click to upload', - colorClass: 'text-violets-are-blue', + colorClass: 'text-primary', }, { text: ' or drag and drop', colorClass: 'text-gray-500', }, ]} - className="rounded-lg border-2 border-dashed border-[#E5E5E5] p-3 text-center transition-colors dark:border-[#3A3A3A] dark:bg-[#2C2C2C]" + className="rounded-lg border-2 border-dashed border-border p-3 text-center transition-colors" /> Image updates are included the next time you save. @@ -1435,7 +1435,7 @@ function WorkflowBuilderInner() { Done @@ -1446,7 +1446,7 @@ function WorkflowBuilderInner() { setShowWorkflowSettings((prev) => !prev)} - className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]" + className="flex items-center gap-2 rounded-full border border-border bg-card px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-accent dark:text-gray-200" > Details @@ -1454,7 +1454,7 @@ function WorkflowBuilderInner() { {canManageAgent && ( navigate(`/agents/logs/${effectiveAgentId}`)} - className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]" + className="flex items-center gap-2 rounded-full border border-border bg-card px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-accent dark:text-gray-200" > Logs @@ -1463,7 +1463,7 @@ function WorkflowBuilderInner() { {canManageAgent && ( setAgentDetails('ACTIVE')} - className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]" + className="flex items-center gap-2 rounded-full border border-border bg-card px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-accent dark:text-gray-200" > Access Details @@ -1473,7 +1473,7 @@ function WorkflowBuilderInner() { setDeleteConfirmation('ACTIVE')} disabled={isDeletingAgent} - className="flex items-center gap-2 rounded-full border border-red-200 bg-white px-4 py-2 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-50 dark:border-red-900/30 dark:bg-[#2C2C2C] dark:text-red-400 dark:hover:bg-red-900/10" + className="flex items-center gap-2 rounded-full border border-red-200 bg-card px-4 py-2 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-50 dark:border-red-900/30 dark:text-red-400 dark:hover:bg-red-900/10" > {isDeletingAgent ? 'Deleting...' : 'Delete'} @@ -1489,7 +1489,7 @@ function WorkflowBuilderInner() { } setShowPreview(true); }} - className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]" + className="flex items-center gap-2 rounded-full border border-border bg-card px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-accent dark:text-gray-200" > Preview @@ -1499,8 +1499,8 @@ function WorkflowBuilderInner() { disabled={isPrimaryActionDisabled} className={`relative inline-flex items-center justify-center rounded-full px-6 py-2 text-sm font-medium shadow-sm transition-colors disabled:cursor-not-allowed ${ canManageAgent && !hasSavableChanges - ? 'bg-gray-200 text-gray-500 dark:bg-[#3A3A3A] dark:text-gray-400' - : 'bg-violets-are-blue hover:bg-purple-30 text-white disabled:opacity-50' + ? 'bg-gray-200 text-gray-500 dark:bg-accent dark:text-gray-400' + : 'bg-primary hover:bg-primary/90 text-white disabled:opacity-50' }`} > - + Core Nodes handleNodeDragStart(e, 'agent')} > - + @@ -1568,7 +1568,7 @@ function WorkflowBuilderInner() { handleNodeDragStart(e, 'end')} > @@ -1580,7 +1580,7 @@ function WorkflowBuilderInner() { handleNodeDragStart(e, 'note')} > @@ -1600,7 +1600,7 @@ function WorkflowBuilderInner() { handleNodeDragStart(e, 'state')} > @@ -1617,7 +1617,7 @@ function WorkflowBuilderInner() { handleNodeDragStart(e, 'condition')} > @@ -1639,7 +1639,7 @@ function WorkflowBuilderInner() { - - + + {selectedNode.type === 'start' && 'Start Node'} {selectedNode.type === 'end' && 'End Node'} @@ -1685,7 +1685,7 @@ function WorkflowBuilderInner() { - + Node ID @@ -1714,7 +1714,7 @@ function WorkflowBuilderInner() { label: e.target.value, }) } - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white" placeholder="Enter node title" /> @@ -1808,7 +1808,7 @@ function WorkflowBuilderInner() { }, }) } - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white" rows={3} placeholder="System prompt for the agent" /> @@ -1851,7 +1851,7 @@ function WorkflowBuilderInner() { }, }); }} - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white" placeholder="Variable name for output" /> @@ -1922,7 +1922,7 @@ function WorkflowBuilderInner() { e.target.value, ) } - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 font-mono text-xs transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 font-mono text-xs transition-all outline-none focus:ring-2 dark:text-white" rows={8} placeholder={`{ "type": "object", @@ -1962,7 +1962,7 @@ function WorkflowBuilderInner() { content: e.target.value, }) } - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white" rows={4} placeholder="Enter note content" /> @@ -1987,7 +1987,7 @@ function WorkflowBuilderInner() { ) => ( @@ -2037,7 +2037,7 @@ function WorkflowBuilderInner() { }, }); }} - className="border-light-silver focus:ring-purple-30 mb-1 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white" + className="border-border focus:ring-ring mb-1 w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:bg-accent dark:text-white" rows={2} placeholder="input.foo + 1" /> @@ -2048,7 +2048,7 @@ function WorkflowBuilderInner() { href="https://cel.dev/" target="_blank" rel="noreferrer" - className="text-violets-are-blue underline" + className="text-primary underline" > Learn more @@ -2077,7 +2077,7 @@ function WorkflowBuilderInner() { }, }); }} - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:bg-accent dark:text-white" placeholder="variable_name" /> @@ -2098,7 +2098,7 @@ function WorkflowBuilderInner() { }, }); }} - className="flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-[#383838]" + className="flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-accent dark:text-gray-400" > Add @@ -2111,7 +2111,7 @@ function WorkflowBuilderInner() { Create conditions to branch your workflow - + handleUpdateNodeData({ @@ -2124,8 +2124,8 @@ function WorkflowBuilderInner() { className={`flex-1 px-3 py-1.5 text-xs font-medium transition-colors ${ (selectedNode.data.config?.mode || 'simple') === 'simple' - ? 'bg-violets-are-blue text-white' - : 'text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-[#383838]' + ? 'bg-primary text-white' + : 'text-gray-600 hover:bg-accent dark:text-gray-400' }`} > Simple @@ -2142,8 +2142,8 @@ function WorkflowBuilderInner() { className={`flex-1 px-3 py-1.5 text-xs font-medium transition-colors ${ selectedNode.data.config?.mode === 'advanced' - ? 'bg-violets-are-blue text-white' - : 'text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-[#383838]' + ? 'bg-primary text-white' + : 'text-gray-600 hover:bg-accent dark:text-gray-400' }`} > Advanced @@ -2154,7 +2154,7 @@ function WorkflowBuilderInner() { (c: ConditionCase, idx: number) => ( @@ -2219,7 +2219,7 @@ function WorkflowBuilderInner() { }, }); }} - className="border-light-silver focus:ring-purple-30 mb-2 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white" + className="border-border focus:ring-ring mb-2 w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:bg-accent dark:text-white" placeholder="Case name (optional)" /> {(selectedNode.data.config?.mode || @@ -2255,7 +2255,7 @@ function WorkflowBuilderInner() { }, }); }} - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm outline-none focus:ring-2 dark:bg-accent dark:text-white" placeholder="Variable" /> @@ -2372,7 +2372,7 @@ function WorkflowBuilderInner() { }, }); }} - className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white" + className="border-border focus:ring-ring w-full rounded-xl border bg-card px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:bg-accent dark:text-white" rows={2} placeholder="Enter condition, e.g. input == 5" /> @@ -2383,7 +2383,7 @@ function WorkflowBuilderInner() { href="https://cel.dev/" target="_blank" rel="noreferrer" - className="text-violets-are-blue underline" + className="text-primary underline" > Learn more @@ -2414,7 +2414,7 @@ function WorkflowBuilderInner() { }, }); }} - className="flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-[#383838]" + className="flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-accent dark:text-gray-400" > Add @@ -2446,7 +2446,7 @@ function WorkflowBuilderInner() { { if (el && stepRefs) stepRefs.current.set(step.nodeId, el); }} - className="rounded-xl bg-[#F5F5F5] p-3 dark:bg-[#383838]" + className="rounded-xl bg-muted p-3 dark:bg-accent" > @@ -181,7 +181,7 @@ function ExecutionDetails({ {(hasOutput || step.error || stateVars.length > 0) && ( {hasOutput && ( - + Output:{' '} @@ -205,7 +205,7 @@ function ExecutionDetails({ {stateVars.map(([key, value]) => ( {key}: @@ -487,10 +487,10 @@ export default function WorkflowPreview({ queries.length > 0 ? queries[queries.length - 1].executionSteps || [] : []; return ( - - + + - + @@ -504,7 +504,7 @@ export default function WorkflowPreview({ {status === 'loading' && ( - + Running @@ -512,7 +512,7 @@ export default function WorkflowPreview({ - + Workflow @@ -537,7 +537,7 @@ export default function WorkflowPreview({ > {queries.length === 0 ? ( - + @@ -618,7 +618,7 @@ export default function WorkflowPreview({ )} - + handleQuestionSubmission(text)} loading={status === 'loading'} diff --git a/frontend/src/agents/workflow/components/MobileBlocker.tsx b/frontend/src/agents/workflow/components/MobileBlocker.tsx index 0d941482..7ccbb00d 100644 --- a/frontend/src/agents/workflow/components/MobileBlocker.tsx +++ b/frontend/src/agents/workflow/components/MobileBlocker.tsx @@ -2,14 +2,14 @@ import { Monitor } from 'lucide-react'; export default function MobileBlocker() { return ( - - - + + + Desktop Required - + The Workflow Builder requires a larger screen for the best experience. Please open this page on a desktop or laptop computer. diff --git a/frontend/src/agents/workflow/components/PromptTextArea.tsx b/frontend/src/agents/workflow/components/PromptTextArea.tsx index 973f7a43..6dddcf94 100644 --- a/frontend/src/agents/workflow/components/PromptTextArea.tsx +++ b/frontend/src/agents/workflow/components/PromptTextArea.tsx @@ -186,7 +186,7 @@ function HighlightedOverlay({ text }: { text: string }) { <> {parts.map((part, i) => /^\{\{[^}]*\}\}$/.test(part) ? ( - + {part} ) : ( @@ -222,7 +222,7 @@ function VariableListWithSearch({ return ( - + - + {v.label} @@ -412,7 +412,7 @@ export default function PromptTextArea({ )} Add context @@ -472,7 +472,7 @@ export default function PromptTextArea({ e.preventDefault()} > 0 && ( = ({ icon, handles = { source: true, target: true }, }) => { - let bgColor = 'bg-white dark:bg-[#2C2C2C]'; - let borderColor = 'border-gray-200 dark:border-[#3A3A3A]'; + let bgColor = 'bg-card'; + let borderColor = 'border-border'; let iconBg = 'bg-gray-100 dark:bg-gray-800'; let iconColor = 'text-gray-600 dark:text-gray-400'; if (selected) { borderColor = - 'border-violets-are-blue ring-2 ring-purple-300 dark:ring-violets-are-blue'; + 'border-primary ring-2 ring-primary'; } if (type === 'start') { @@ -56,7 +56,7 @@ export const BaseNode: React.FC = ({ type="target" position={Position.Left} isConnectable={true} - className="hover:bg-violets-are-blue! -left-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!" + className="hover:bg-primary/90! -left-1! h-3! w-3! rounded-full! border-2! border-card! bg-gray-400! transition-colors!" /> )} @@ -86,7 +86,7 @@ export const BaseNode: React.FC = ({ type="source" position={Position.Right} isConnectable={true} - className="hover:bg-violets-are-blue! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!" + className="hover:bg-primary/90! -right-1! h-3! w-3! rounded-full! border-2! border-card! bg-gray-400! transition-colors!" /> )} diff --git a/frontend/src/agents/workflow/nodes/ConditionNode.tsx b/frontend/src/agents/workflow/nodes/ConditionNode.tsx index 392c86aa..a4376258 100644 --- a/frontend/src/agents/workflow/nodes/ConditionNode.tsx +++ b/frontend/src/agents/workflow/nodes/ConditionNode.tsx @@ -36,10 +36,10 @@ const ConditionNode = ({ data, selected }: NodeProps) => { return ( @@ -47,7 +47,7 @@ const ConditionNode = ({ data, selected }: NodeProps) => { type="target" position={Position.Left} isConnectable - className="hover:bg-violets-are-blue! top-1/2! -left-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!" + className="hover:bg-primary/90! top-1/2! -left-1! h-3! w-3! rounded-full! border-2! border-card! bg-gray-400! transition-colors!" /> @@ -100,7 +100,7 @@ const ConditionNode = ({ data, selected }: NodeProps) => { id={c.sourceHandle} isConnectable style={{ top: getHandleTop(i, totalOutputs) }} - className="hover:bg-violets-are-blue! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-orange-400! transition-colors dark:border-[#2C2C2C]!" + className="hover:bg-primary/90! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-orange-400! transition-colors dark:border-border!" /> ))} ) => { id="else" isConnectable style={{ top: getHandleTop(cases.length, totalOutputs) }} - className="hover:bg-violets-are-blue! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!" + className="hover:bg-primary/90! -right-1! h-3! w-3! rounded-full! border-2! border-card! bg-gray-400! transition-colors!" /> ); diff --git a/frontend/src/agents/workflow/nodes/index.tsx b/frontend/src/agents/workflow/nodes/index.tsx index 94b34263..cdbfb37c 100644 --- a/frontend/src/agents/workflow/nodes/index.tsx +++ b/frontend/src/agents/workflow/nodes/index.tsx @@ -77,7 +77,7 @@ export const AgentNode = memo(function AgentNode({ )} {config.model_id && ( {config.model_id} diff --git a/frontend/src/components/Accordion.tsx b/frontend/src/components/Accordion.tsx index afc54483..bb0da630 100644 --- a/frontend/src/components/Accordion.tsx +++ b/frontend/src/components/Accordion.tsx @@ -37,7 +37,7 @@ export default function Accordion({ className={`flex w-full items-center justify-between focus:outline-hidden ${titleClassName}`} onClick={toggleAccordion} > - {title} + {title} setShareModalState(true)} - className="hover:bg-bright-gray rounded-full p-2 dark:hover:bg-[#28292E]" + className="hover:bg-accent rounded-full p-2 dark:hover:bg-accent" > = { }; function getArtifactTitle(artifact: ArtifactData | null, toolName?: string) { - if (artifact) return ARTIFACT_TITLE_BY_TYPE[artifact.artifact_type] ?? 'Artifact'; + if (artifact) + return ARTIFACT_TITLE_BY_TYPE[artifact.artifact_type] ?? 'Artifact'; const formattedToolName = (toolName ?? '') .replace(/_/g, ' ') @@ -161,7 +162,7 @@ function NoteView({ data }: { data: NoteArtifactData }) { {data.content ? ( - - + + + {language} ) : ( {children} @@ -315,17 +316,17 @@ export default function ArtifactSidebar({ // Generate a unique ID for this fetch const fetchId = `${effectiveArtifactId}-${Date.now()}`; currentFetchIdRef.current = fetchId; - + setLoading(true); setError(null); - + // Note: For todo artifacts, the endpoint always returns all todos for the tool; will be coversation scoped later userService .getArtifact(effectiveArtifactId, token) .then(async (res: any) => { // Ignore if this is not the current fetch if (currentFetchIdRef.current !== fetchId) return; - + const isResponseLike = res && typeof res.json === 'function'; const status = isResponseLike ? res.status : undefined; const ok = isResponseLike ? Boolean(res.ok) : true; @@ -453,7 +454,7 @@ export default function ArtifactSidebar({ {title} @@ -481,7 +482,7 @@ export default function ArtifactSidebar({ {title} void; @@ -73,7 +75,7 @@ const LineNumberedTextarea: React.FC = ({ ))} = ({ setEditingChunk(null) @@ -315,17 +317,17 @@ const Chunks: React.FC = ({ {/* Removed the directory icon */} - + {documentName} {pathParts.length > 0 && ( <> - / + / {pathParts.map((part, index) => ( = ({ {part} {index < pathParts.length - 1 && ( - - / - + / )} ))} @@ -350,7 +350,7 @@ const Chunks: React.FC = ({ !isEditing ? ( <> setIsEditing(true)} > {t('modals.chunk.edit')} @@ -370,7 +370,7 @@ const Chunks: React.FC = ({ onClick={() => { setIsEditing(false); }} - className="dark:text-light-gray flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50" + className="dark:text-foreground hover:bg-accent dark:hover:bg-accent flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap" > {t('modals.chunk.cancel')} @@ -402,7 +402,7 @@ const Chunks: React.FC = ({ editingText.trim() && (editingTitle !== (editingChunk?.metadata?.title || '') || editingText !== (editingChunk?.text || '')) - ? 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer' + ? 'bg-primary hover:bg-primary/90 cursor-pointer' : 'cursor-not-allowed bg-gray-400' }`} > @@ -414,7 +414,7 @@ const Chunks: React.FC = ({ <> setIsAddingChunk(false)} - className="dark:text-light-gray flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50" + className="dark:text-foreground hover:bg-accent dark:hover:bg-accent flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap" > {t('modals.chunk.cancel')} @@ -428,7 +428,7 @@ const Chunks: React.FC = ({ disabled={!editingText.trim()} className={`flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 py-1 text-[14px] font-medium text-nowrap text-white transition-all ${ editingText.trim() - ? 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer' + ? 'bg-primary hover:bg-primary/90 cursor-pointer' : 'cursor-not-allowed bg-gray-400' }`} > @@ -488,14 +488,14 @@ const Chunks: React.FC = ({ value={fileSearchQuery} onChange={(e) => handleFileSearchChange(e.target.value)} placeholder={t('settings.sources.searchFiles')} - className={`h-[38px] w-full border border-[#D1D9E0] py-2 pr-4 pl-10 dark:border-[#6A6A6A] ${ + className={`border-border dark:border-border h-[38px] w-full border py-2 pr-4 pl-10 ${ fileSearchQuery ? 'rounded-t-[6px]' : 'rounded-[6px]' - } bg-transparent transition-all duration-200 focus:outline-none dark:text-[#E0E0E0]`} + } bg-transparent transition-all duration-200 focus:outline-none`} /> {fileSearchQuery && ( - + {fileSearchResults.length === 0 ? ( @@ -507,18 +507,18 @@ const Chunks: React.FC = ({ key={index} title={result.path} onClick={() => handleSearchResultClick(result)} - className={`flex cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${ + className={`hover:bg-muted dark:hover:bg-muted flex cursor-pointer items-center px-3 py-2 ${ index !== fileSearchResults.length - 1 - ? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]' + ? 'border-border dark:border-border border-b' : '' }`} > - + {result.name || result.path.split('/').pop() || result.path} @@ -546,8 +546,8 @@ const Chunks: React.FC = ({ {!editingChunk && !isAddingChunk ? ( <> - - + + {totalChunks > 999999 ? `${(totalChunks / 1000000).toFixed(2)}M` : totalChunks > 999 @@ -555,19 +555,19 @@ const Chunks: React.FC = ({ : totalChunks}{' '} {t('settings.sources.chunks')} - + setSearchTerm(e.target.value)} - className="h-full w-full border-none bg-transparent px-3 py-2 text-[13.56px] leading-[100%] font-normal outline-none dark:text-[#E0E0E0]" + className="h-full w-full border-none bg-transparent px-3 py-2 text-[13.56px] leading-[100%] font-normal outline-none" /> { setIsAddingChunk(true); @@ -579,11 +579,11 @@ const Chunks: React.FC = ({ {loading ? ( - + ) : ( - + {filteredChunks.length === 0 ? ( = ({ filteredChunks.map((chunk, index) => ( { setEditingChunk(chunk); setEditingTitle(chunk.metadata?.title || ''); @@ -605,8 +605,8 @@ const Chunks: React.FC = ({ }} > - - + + {chunk.metadata.token_count ? chunk.metadata.token_count.toLocaleString() : '-'}{' '} @@ -614,7 +614,7 @@ const Chunks: React.FC = ({ - + {chunk.text} @@ -627,7 +627,7 @@ const Chunks: React.FC = ({ > ) : isAddingChunk ? ( - + = ({ ) : ( editingChunk && ( - - - + + + {editingChunk.metadata.token_count ? editingChunk.metadata.token_count.toLocaleString() : '-'}{' '} diff --git a/frontend/src/components/ConnectorAuth.tsx b/frontend/src/components/ConnectorAuth.tsx index 6411df90..eefeb999 100644 --- a/frontend/src/components/ConnectorAuth.tsx +++ b/frontend/src/components/ConnectorAuth.tsx @@ -136,7 +136,7 @@ const ConnectorAuth: React.FC = ({ = ({ {/* Left side with path navigation */} @@ -340,7 +340,7 @@ const ConnectorTree: React.FC = ({ / {currentPath.map((dir, index) => ( - + {dir} {index < currentPath.length - 1 && ( @@ -365,7 +365,7 @@ const ConnectorTree: React.FC = ({ className={`flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap transition-colors ${ isSyncing ? 'cursor-not-allowed bg-gray-300 text-gray-600 dark:bg-gray-600 dark:text-gray-400' - : 'bg-purple-30 hover:bg-violets-are-blue text-white' + : 'bg-primary hover:bg-primary/90 text-white' }`} title={ isSyncing @@ -466,7 +466,7 @@ const ConnectorTree: React.FC = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ } }} placeholder={t('settings.sources.searchFiles')} - className={`h-[38px] w-full border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`} + className={`h-[38px] w-full border border-border px-4 py-2 dark:border-border ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none`} /> {searchQuery && ( - + {searchResults.length === 0 ? ( @@ -641,9 +641,9 @@ const ConnectorTree: React.FC = ({ key={index} onClick={() => handleSearchSelect(result)} title={result.path} - className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${ + className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-muted dark:hover:bg-muted ${ index !== searchResults.length - 1 - ? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]' + ? 'border-b border-border dark:border-border' : '' }`} > @@ -656,7 +656,7 @@ const ConnectorTree: React.FC = ({ } className="mr-2 h-4 w-4 flex-shrink-0" /> - + {result.name} diff --git a/frontend/src/components/ContextMenu.tsx b/frontend/src/components/ContextMenu.tsx index 9348a45c..cc0de0db 100644 --- a/frontend/src/components/ContextMenu.tsx +++ b/frontend/src/components/ContextMenu.tsx @@ -127,7 +127,7 @@ export default function ContextMenu({ onClick={(e) => e.stopPropagation()} > {options.map((option, index) => ( @@ -141,8 +141,8 @@ export default function ContextMenu({ }} className={`flex items-center justify-start gap-4 p-3 transition-colors duration-200 ease-in-out ${index === 0 ? 'rounded-t-xl' : ''} ${index === options.length - 1 ? 'rounded-b-xl' : ''} ${ option.variant === 'danger' - ? 'text-rosso-corsa hover:bg-bright-gray dark:text-red-2000 dark:hover:bg-charcoal-grey/20' - : 'text-eerie-black hover:bg-bright-gray dark:text-bright-gray dark:hover:bg-charcoal-grey/20' + ? 'text-destructive hover:bg-muted' + : 'text-foreground hover:bg-muted' } `} > {option.icon && ( diff --git a/frontend/src/components/CopyButton.tsx b/frontend/src/components/CopyButton.tsx index f032fc11..bf32a58d 100644 --- a/frontend/src/components/CopyButton.tsx +++ b/frontend/src/components/CopyButton.tsx @@ -39,7 +39,7 @@ export default function CopyButton({ 'flex items-center justify-center rounded-full transition-colors duration-150 ease-in-out', padding, { - [`bg-[#FFFFFF}] dark:bg-transparent hover:bg-[#EEEEEE] dark:hover:bg-purple-taupe`]: + [`bg-transparent hover:bg-muted`]: !isCopied, 'bg-green-100 dark:bg-green-900 hover:bg-green-100 dark:hover:bg-green-900': isCopied, diff --git a/frontend/src/components/DocumentPagination.tsx b/frontend/src/components/DocumentPagination.tsx index 7f255ed0..f52618d7 100644 --- a/frontend/src/components/DocumentPagination.tsx +++ b/frontend/src/components/DocumentPagination.tsx @@ -61,12 +61,12 @@ const Pagination: React.FC = ({ {rowsPerPage} = ({ handleSelectRowsPerPage(option)} - className={`cursor-pointer px-4 py-2 text-xs hover:bg-gray-100 dark:hover:bg-neutral-700 ${ + className={`cursor-pointer px-4 py-2 text-xs hover:bg-accent dark:hover:bg-accent ${ rowsPerPage === option - ? 'dark:text-light-gray bg-gray-100 dark:bg-neutral-700' - : 'dark:bg-dark-charcoal dark:text-light-gray bg-white' + ? 'dark:text-foreground bg-gray-100 dark:bg-neutral-700' + : 'bg-card dark:text-foreground' }`} > {option} diff --git a/frontend/src/components/Dropdown.tsx b/frontend/src/components/Dropdown.tsx index 9a14ac27..9897498a 100644 --- a/frontend/src/components/Dropdown.tsx +++ b/frontend/src/components/Dropdown.tsx @@ -1,163 +1,221 @@ -import React from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; -import Arrow2 from '../assets/dropdown-arrow.svg'; -import Edit from '../assets/edit.svg'; -import Trash from '../assets/trash.svg'; -import { DropdownOption, DropdownProps } from './types/Dropdown.types'; +import { ChevronDown, Pencil, Search, Trash2 } from 'lucide-react'; + +type OptionBase = { id?: string; type?: string }; +type NameIdOption = { name: string; id: string } & OptionBase; +type LabelValueOption = { label: string; value: string } & OptionBase; +type ValueDescriptionOption = { + value: number; + description: string; +} & OptionBase; + +export type DropdownOption = + | string + | NameIdOption + | LabelValueOption + | ValueDescriptionOption; + +export type { NameIdOption, LabelValueOption, ValueDescriptionOption }; + +function getOptionText(option: DropdownOption): string { + if (typeof option === 'string') return option; + if ('name' in option) return option.name; + if ('label' in option) return option.label; + if ('description' in option) + return option.value < 1e9 + ? `${option.value} (${option.description})` + : option.description; + return ''; +} + +function optionMatches( + option: DropdownOption, + selected: DropdownOption | null, +): boolean { + if (!selected) return false; + if (typeof selected === 'string') return selected === option; + if (typeof option === 'string') return false; + const a = option as Record; + const b = selected as Record; + if ('name' in a && 'name' in b) return a.name === b.name; + if ('label' in a && 'label' in b) return a.label === b.label; + if ('value' in a && 'value' in b) return a.value === b.value; + return false; +} + +export interface DropdownProps { + options: T[]; + selectedValue: DropdownOption | null; + onSelect: (value: T) => void; + size?: string; + rounded?: 'xl' | '3xl'; + searchable?: boolean; + placeholder?: string; + contentSize?: string; + showEdit?: boolean; + onEdit?: (value: NameIdOption) => void; + showDelete?: boolean | ((option: T) => boolean); + onDelete?: (id: string) => void; +} function Dropdown({ options, selectedValue, onSelect, - size = 'w-32', - rounded = 'xl', - buttonClassName = 'border-silver bg-white dark:bg-transparent dark:border-dim-gray', - optionsClassName = 'border-silver bg-white dark:border-dim-gray dark:bg-dark-charcoal', - border = 'border-2', + size = 'w-full', + rounded = '3xl', + searchable = false, + placeholder = 'Select...', + contentSize = 'text-sm', showEdit, onEdit, showDelete, onDelete, - placeholder, - placeholderClassName = 'text-gray-500 dark:text-gray-400', - contentSize = 'text-base', }: DropdownProps) { - const dropdownRef = React.useRef(null); - const [isOpen, setIsOpen] = React.useState(false); - const borderRadius = rounded === 'xl' ? 'rounded-xl' : 'rounded-3xl'; - const borderTopRadius = rounded === 'xl' ? 'rounded-t-xl' : 'rounded-t-3xl'; + const ref = useRef(null); + const searchRef = useRef(null); + const [open, setOpen] = useState(false); + const [query, setQuery] = useState(''); - const handleClickOutside = (event: MouseEvent) => { - if ( - dropdownRef.current && - !dropdownRef.current.contains(event.target as Node) - ) { - setIsOpen(false); - } - }; + const radius = rounded === '3xl' ? 'rounded-3xl' : 'rounded-xl'; + const radiusTop = rounded === '3xl' ? 'rounded-t-3xl' : 'rounded-t-xl'; + const radiusBottom = rounded === '3xl' ? 'rounded-b-3xl' : 'rounded-b-xl'; - React.useEffect(() => { - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); + useEffect(() => { + const handler = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + setOpen(false); + setQuery(''); + } }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); }, []); + useEffect(() => { + if (open && searchable && searchRef.current) searchRef.current.focus(); + }, [open, searchable]); + + const filtered = useMemo(() => { + if (!searchable || !query.trim()) return options; + const q = query.toLowerCase(); + return options.filter((o) => getOptionText(o).toLowerCase().includes(q)); + }, [options, query, searchable]); + + const displayValue = selectedValue ? getOptionText(selectedValue) : null; + return ( - + setIsOpen(!isOpen)} - className={`flex w-full cursor-pointer items-center justify-between ${border} ${buttonClassName} px-5 py-3 ${ - isOpen ? `${borderTopRadius}` : `${borderRadius}` - }`} + type="button" + onClick={() => setOpen((v) => !v)} + className={`flex w-full cursor-pointer items-center justify-between border border-border bg-card px-5 py-3 text-foreground ${open ? radiusTop : radius}`} > - {typeof selectedValue === 'string' ? ( - - {selectedValue} - - ) : ( - - {selectedValue && 'label' in selectedValue - ? selectedValue.label - : selectedValue && 'description' in selectedValue - ? `${ - selectedValue.value < 1e9 - ? selectedValue.value + ` (${selectedValue.description})` - : selectedValue.description - }` - : placeholder - ? placeholder - : 'From URL'} - - )} - + {displayValue ?? placeholder} + + - {isOpen && ( + + {open && ( - {options.map((option: any, index) => ( - - { - onSelect(option); - setIsOpen(false); - }} - className={`dark:text-light-gray ml-5 flex-1 overflow-hidden py-3 text-ellipsis whitespace-nowrap ${contentSize}`} - > - {typeof option === 'string' - ? option - : option.name - ? option.name - : option.label - ? option.label - : `${ - option.value < 1e9 - ? option.value + ` (${option.description})` - : option.description - }`} - - {showEdit && onEdit && option.type !== 'public' && ( - { - onEdit({ - id: option.id, - name: option.name, - type: option.type, - }); - setIsOpen(false); - }} - /> - )} - {showDelete && onDelete && ( - { - e.stopPropagation(); - onDelete?.(typeof option === 'string' ? option : option.id); - }} - className={`${ - typeof showDelete === 'function' && !showDelete(option) - ? 'hidden' - : '' - } mr-2 h-4 w-4 cursor-pointer hover:opacity-50`} - > - - - )} + {searchable && ( + + + setQuery(e.target.value)} + placeholder="Search..." + className="w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus:outline-none" + onClick={(e) => e.stopPropagation()} + /> - ))} + )} + + + {filtered.length === 0 ? ( + + No results found + + ) : ( + filtered.map((option, i) => { + const active = optionMatches(option, selectedValue); + const optObj = + typeof option !== 'string' + ? (option as Record) + : null; + + return ( + + { + onSelect(option); + setOpen(false); + setQuery(''); + }} + className={`flex-1 truncate px-4 py-2.5 text-foreground ${contentSize}`} + > + {getOptionText(option)} + + + {showEdit && + onEdit && + optObj && + optObj.type !== 'public' && ( + { + onEdit({ + id: optObj.id as string, + name: optObj.name as string, + type: optObj.type as string, + }); + setOpen(false); + setQuery(''); + }} + className="mr-1 rounded p-1 hover:bg-accent" + > + + + )} + + {showDelete && onDelete && ( + { + e.stopPropagation(); + onDelete( + typeof option === 'string' + ? option + : ((optObj?.id as string) ?? ''), + ); + }} + className={`mr-1 rounded p-1 hover:bg-accent ${ + typeof showDelete === 'function' && !showDelete(option) + ? 'hidden' + : '' + } ${optObj?.type === 'public' ? 'pointer-events-none opacity-30' : ''}`} + > + + + )} + + ); + }) + )} + )} diff --git a/frontend/src/components/DropdownMenu.tsx b/frontend/src/components/DropdownMenu.tsx index 57e84eaa..b9803912 100644 --- a/frontend/src/components/DropdownMenu.tsx +++ b/frontend/src/components/DropdownMenu.tsx @@ -88,7 +88,7 @@ export default function DropdownMenu({ onClick={(e) => e.stopPropagation()} > ( setIsOpen(!isOpen)} > {selectedModel?.display_name ? ( - + {selectedModel.display_name} ) : ( - + Select Model )} @@ -101,7 +101,7 @@ export default function DropdownModel() { /> {isOpen && ( - + {availableModels && (availableModels?.length ?? 0) > 0 ? ( availableModels.map((model: Model) => ( @@ -127,8 +127,8 @@ export default function DropdownModel() { )) ) : ( - - No models available + + No models available )} diff --git a/frontend/src/components/FilePicker.tsx b/frontend/src/components/FilePicker.tsx index 36b484f4..7db6c132 100644 --- a/frontend/src/components/FilePicker.tsx +++ b/frontend/src/components/FilePicker.tsx @@ -459,10 +459,10 @@ export const FilePicker: React.FC = ({ /> {isConnected && ( - - + + {provider === 'share_point' && allowsSharedContent && ( - + handleTabChange('my_files')} className={`px-4 py-2 text-sm font-medium ${ @@ -485,7 +485,7 @@ export const FilePicker: React.FC = ({ )} - + {folderPath.map((path, index) => ( = ({ onChange={(e) => handleSearchChange(e.target.value)} colorVariant="silver" borderVariant="thin" - labelBgClassName="bg-[#EEE6FF78] dark:bg-[#2A262E]" + labelBgClassName="bg-[#EEE6FF78] dark:bg-muted" leftIcon={ } @@ -531,7 +531,7 @@ export const FilePicker: React.FC = ({ - + = ({ > { e.stopPropagation(); handleFileSelect(file.id, isFolder(file)); diff --git a/frontend/src/components/FileSelectionSkeleton.tsx b/frontend/src/components/FileSelectionSkeleton.tsx index 1aa45769..b26927b9 100644 --- a/frontend/src/components/FileSelectionSkeleton.tsx +++ b/frontend/src/components/FileSelectionSkeleton.tsx @@ -1,5 +1,5 @@ const FilesSectionSkeleton = () => ( - + diff --git a/frontend/src/components/FileTree.tsx b/frontend/src/components/FileTree.tsx index f150d804..11424380 100644 --- a/frontend/src/components/FileTree.tsx +++ b/frontend/src/components/FileTree.tsx @@ -450,7 +450,7 @@ const FileTree: React.FC = ({ {/* Left side with path navigation */} @@ -503,7 +503,7 @@ const FileTree: React.FC = ({ {!processingRef.current && ( {t('settings.sources.addFile')} @@ -599,7 +599,7 @@ const FileTree: React.FC = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ } }} placeholder={t('settings.sources.searchFiles')} - className={`h-[38px] w-full border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`} + className={`h-[38px] w-full border border-border px-4 py-2 dark:border-border ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none`} /> {searchQuery && ( - + {searchResults.length === 0 ? ( @@ -770,9 +770,9 @@ const FileTree: React.FC = ({ key={index} onClick={() => handleSearchSelect(result)} title={result.path} - className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${ + className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-muted dark:hover:bg-muted ${ index !== searchResults.length - 1 - ? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]' + ? 'border-b border-border dark:border-border' : '' }`} > @@ -785,7 +785,7 @@ const FileTree: React.FC = ({ } className="mr-2 h-4 w-4 flex-shrink-0" /> - + {result.name} diff --git a/frontend/src/components/FileUpload.tsx b/frontend/src/components/FileUpload.tsx index cc9dcb26..fb4ad9b6 100644 --- a/frontend/src/components/FileUpload.tsx +++ b/frontend/src/components/FileUpload.tsx @@ -41,7 +41,7 @@ export const FileUpload = ({ showPreview = false, previewSize = 80, children, - className = 'border-2 border-dashed rounded-3xl p-6 text-center cursor-pointer transition-colors border-silver dark:border-[#7E7E7E]', + className = 'border-2 border-dashed rounded-3xl p-6 text-center cursor-pointer transition-colors border-border dark:border-border', activeClassName = 'border-blue-500 bg-blue-50', acceptClassName = 'border-green-500 dark:border-green-500 bg-green-50 dark:bg-green-50/10', rejectClassName = 'border-red-500 bg-red-50 dark:bg-red-500/10 dark:border-red-500', @@ -133,7 +133,7 @@ export const FileUpload = ({ }); const currentClassName = twMerge( - 'border-2 border-dashed rounded-3xl p-8 text-center cursor-pointer transition-colors border-silver dark:border-[#7E7E7E]', + 'border-2 border-dashed rounded-3xl p-8 text-center cursor-pointer transition-colors border-border dark:border-border', className, isDragActive && activeClassName, isDragAccept && acceptClassName, diff --git a/frontend/src/components/GoogleDrivePicker.tsx b/frontend/src/components/GoogleDrivePicker.tsx index bb3d240b..6cedb828 100644 --- a/frontend/src/components/GoogleDrivePicker.tsx +++ b/frontend/src/components/GoogleDrivePicker.tsx @@ -262,7 +262,7 @@ const GoogleDrivePicker: React.FC = ({ /> {isConnected && ( - + diff --git a/frontend/src/components/Help.tsx b/frontend/src/components/Help.tsx index 896bd2ec..498de3c1 100644 --- a/frontend/src/components/Help.tsx +++ b/frontend/src/components/Help.tsx @@ -36,20 +36,20 @@ const Help = () => { {t('help')} {isOpen && ( { { const colorStyles = { - silver: 'border-silver dark:border-silver/40', + silver: 'border-border dark:border-border', jet: 'border-jet', - gray: 'border-gray-5000 dark:text-silver', + gray: 'border-gray-5000 dark:text-muted-foreground', }; const borderStyles = { thin: 'border', @@ -44,7 +44,7 @@ const Input = ({ {placeholder} {required && ( - * + * )} )} diff --git a/frontend/src/components/MermaidRenderer.tsx b/frontend/src/components/MermaidRenderer.tsx index 3e8c2eb8..6a81b806 100644 --- a/frontend/src/components/MermaidRenderer.tsx +++ b/frontend/src/components/MermaidRenderer.tsx @@ -1,16 +1,17 @@ +import mermaid from 'mermaid'; import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import mermaid from 'mermaid'; -import CopyButton from './CopyButton'; +import { useSelector } from 'react-redux'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneLight, vscDarkPlus, } from 'react-syntax-highlighter/dist/cjs/styles/prism'; -import { MermaidRendererProps } from './types'; -import { useSelector } from 'react-redux'; + import { selectStatus } from '../conversation/conversationSlice'; import { useDarkTheme } from '../hooks'; +import CopyButton from './CopyButton'; +import { MermaidRendererProps } from './types'; const MermaidRenderer: React.FC = ({ code, @@ -262,9 +263,9 @@ const MermaidRenderer: React.FC
diff --git a/frontend/src/agents/workflow/WorkflowBuilder.tsx b/frontend/src/agents/workflow/WorkflowBuilder.tsx index b1b5faf6..5092efe5 100644 --- a/frontend/src/agents/workflow/WorkflowBuilder.tsx +++ b/frontend/src/agents/workflow/WorkflowBuilder.tsx @@ -1333,12 +1333,12 @@ function WorkflowBuilderInner() { return ( <> -
Image updates are included the next time you save. @@ -1435,7 +1435,7 @@ function WorkflowBuilderInner() { Done @@ -1446,7 +1446,7 @@ function WorkflowBuilderInner() {
Create conditions to branch your workflow
@@ -618,7 +618,7 @@ export default function WorkflowPreview({
The Workflow Builder requires a larger screen for the best experience. Please open this page on a desktop or laptop computer.
{title}
{children} @@ -315,17 +316,17 @@ export default function ArtifactSidebar({ // Generate a unique ID for this fetch const fetchId = `${effectiveArtifactId}-${Date.now()}`; currentFetchIdRef.current = fetchId; - + setLoading(true); setError(null); - + // Note: For todo artifacts, the endpoint always returns all todos for the tool; will be coversation scoped later userService .getArtifact(effectiveArtifactId, token) .then(async (res: any) => { // Ignore if this is not the current fetch if (currentFetchIdRef.current !== fetchId) return; - + const isResponseLike = res && typeof res.json === 'function'; const status = isResponseLike ? res.status : undefined; const ok = isResponseLike ? Boolean(res.ok) : true; @@ -453,7 +454,7 @@ export default function ArtifactSidebar({ {title} @@ -481,7 +482,7 @@ export default function ArtifactSidebar({ {title} void; @@ -73,7 +75,7 @@ const LineNumberedTextarea: React.FC = ({ ))} = ({ setEditingChunk(null) @@ -315,17 +317,17 @@ const Chunks: React.FC = ({ {/* Removed the directory icon */} - + {documentName} {pathParts.length > 0 && ( <> - / + / {pathParts.map((part, index) => ( = ({ {part} {index < pathParts.length - 1 && ( - - / - + / )} ))} @@ -350,7 +350,7 @@ const Chunks: React.FC = ({ !isEditing ? ( <> setIsEditing(true)} > {t('modals.chunk.edit')} @@ -370,7 +370,7 @@ const Chunks: React.FC = ({ onClick={() => { setIsEditing(false); }} - className="dark:text-light-gray flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50" + className="dark:text-foreground hover:bg-accent dark:hover:bg-accent flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap" > {t('modals.chunk.cancel')} @@ -402,7 +402,7 @@ const Chunks: React.FC = ({ editingText.trim() && (editingTitle !== (editingChunk?.metadata?.title || '') || editingText !== (editingChunk?.text || '')) - ? 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer' + ? 'bg-primary hover:bg-primary/90 cursor-pointer' : 'cursor-not-allowed bg-gray-400' }`} > @@ -414,7 +414,7 @@ const Chunks: React.FC = ({ <> setIsAddingChunk(false)} - className="dark:text-light-gray flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50" + className="dark:text-foreground hover:bg-accent dark:hover:bg-accent flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap" > {t('modals.chunk.cancel')} @@ -428,7 +428,7 @@ const Chunks: React.FC = ({ disabled={!editingText.trim()} className={`flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 py-1 text-[14px] font-medium text-nowrap text-white transition-all ${ editingText.trim() - ? 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer' + ? 'bg-primary hover:bg-primary/90 cursor-pointer' : 'cursor-not-allowed bg-gray-400' }`} > @@ -488,14 +488,14 @@ const Chunks: React.FC = ({ value={fileSearchQuery} onChange={(e) => handleFileSearchChange(e.target.value)} placeholder={t('settings.sources.searchFiles')} - className={`h-[38px] w-full border border-[#D1D9E0] py-2 pr-4 pl-10 dark:border-[#6A6A6A] ${ + className={`border-border dark:border-border h-[38px] w-full border py-2 pr-4 pl-10 ${ fileSearchQuery ? 'rounded-t-[6px]' : 'rounded-[6px]' - } bg-transparent transition-all duration-200 focus:outline-none dark:text-[#E0E0E0]`} + } bg-transparent transition-all duration-200 focus:outline-none`} /> {fileSearchQuery && ( - + {fileSearchResults.length === 0 ? ( @@ -507,18 +507,18 @@ const Chunks: React.FC = ({ key={index} title={result.path} onClick={() => handleSearchResultClick(result)} - className={`flex cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${ + className={`hover:bg-muted dark:hover:bg-muted flex cursor-pointer items-center px-3 py-2 ${ index !== fileSearchResults.length - 1 - ? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]' + ? 'border-border dark:border-border border-b' : '' }`} > - + {result.name || result.path.split('/').pop() || result.path} @@ -546,8 +546,8 @@ const Chunks: React.FC = ({ {!editingChunk && !isAddingChunk ? ( <> - - + + {totalChunks > 999999 ? `${(totalChunks / 1000000).toFixed(2)}M` : totalChunks > 999 @@ -555,19 +555,19 @@ const Chunks: React.FC = ({ : totalChunks}{' '} {t('settings.sources.chunks')} - + setSearchTerm(e.target.value)} - className="h-full w-full border-none bg-transparent px-3 py-2 text-[13.56px] leading-[100%] font-normal outline-none dark:text-[#E0E0E0]" + className="h-full w-full border-none bg-transparent px-3 py-2 text-[13.56px] leading-[100%] font-normal outline-none" /> { setIsAddingChunk(true); @@ -579,11 +579,11 @@ const Chunks: React.FC = ({ {loading ? ( - + ) : ( - + {filteredChunks.length === 0 ? ( = ({ filteredChunks.map((chunk, index) => ( { setEditingChunk(chunk); setEditingTitle(chunk.metadata?.title || ''); @@ -605,8 +605,8 @@ const Chunks: React.FC = ({ }} > - - + + {chunk.metadata.token_count ? chunk.metadata.token_count.toLocaleString() : '-'}{' '} @@ -614,7 +614,7 @@ const Chunks: React.FC = ({ - + {chunk.text} @@ -627,7 +627,7 @@ const Chunks: React.FC = ({ > ) : isAddingChunk ? ( - + = ({ ) : ( editingChunk && ( - - - + + + {editingChunk.metadata.token_count ? editingChunk.metadata.token_count.toLocaleString() : '-'}{' '} diff --git a/frontend/src/components/ConnectorAuth.tsx b/frontend/src/components/ConnectorAuth.tsx index 6411df90..eefeb999 100644 --- a/frontend/src/components/ConnectorAuth.tsx +++ b/frontend/src/components/ConnectorAuth.tsx @@ -136,7 +136,7 @@ const ConnectorAuth: React.FC = ({ = ({ {/* Left side with path navigation */} @@ -340,7 +340,7 @@ const ConnectorTree: React.FC = ({ / {currentPath.map((dir, index) => ( - + {dir} {index < currentPath.length - 1 && ( @@ -365,7 +365,7 @@ const ConnectorTree: React.FC = ({ className={`flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap transition-colors ${ isSyncing ? 'cursor-not-allowed bg-gray-300 text-gray-600 dark:bg-gray-600 dark:text-gray-400' - : 'bg-purple-30 hover:bg-violets-are-blue text-white' + : 'bg-primary hover:bg-primary/90 text-white' }`} title={ isSyncing @@ -466,7 +466,7 @@ const ConnectorTree: React.FC = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ } }} placeholder={t('settings.sources.searchFiles')} - className={`h-[38px] w-full border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`} + className={`h-[38px] w-full border border-border px-4 py-2 dark:border-border ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none`} /> {searchQuery && ( - + {searchResults.length === 0 ? ( @@ -641,9 +641,9 @@ const ConnectorTree: React.FC = ({ key={index} onClick={() => handleSearchSelect(result)} title={result.path} - className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${ + className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-muted dark:hover:bg-muted ${ index !== searchResults.length - 1 - ? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]' + ? 'border-b border-border dark:border-border' : '' }`} > @@ -656,7 +656,7 @@ const ConnectorTree: React.FC = ({ } className="mr-2 h-4 w-4 flex-shrink-0" /> - + {result.name} diff --git a/frontend/src/components/ContextMenu.tsx b/frontend/src/components/ContextMenu.tsx index 9348a45c..cc0de0db 100644 --- a/frontend/src/components/ContextMenu.tsx +++ b/frontend/src/components/ContextMenu.tsx @@ -127,7 +127,7 @@ export default function ContextMenu({ onClick={(e) => e.stopPropagation()} > {options.map((option, index) => ( @@ -141,8 +141,8 @@ export default function ContextMenu({ }} className={`flex items-center justify-start gap-4 p-3 transition-colors duration-200 ease-in-out ${index === 0 ? 'rounded-t-xl' : ''} ${index === options.length - 1 ? 'rounded-b-xl' : ''} ${ option.variant === 'danger' - ? 'text-rosso-corsa hover:bg-bright-gray dark:text-red-2000 dark:hover:bg-charcoal-grey/20' - : 'text-eerie-black hover:bg-bright-gray dark:text-bright-gray dark:hover:bg-charcoal-grey/20' + ? 'text-destructive hover:bg-muted' + : 'text-foreground hover:bg-muted' } `} > {option.icon && ( diff --git a/frontend/src/components/CopyButton.tsx b/frontend/src/components/CopyButton.tsx index f032fc11..bf32a58d 100644 --- a/frontend/src/components/CopyButton.tsx +++ b/frontend/src/components/CopyButton.tsx @@ -39,7 +39,7 @@ export default function CopyButton({ 'flex items-center justify-center rounded-full transition-colors duration-150 ease-in-out', padding, { - [`bg-[#FFFFFF}] dark:bg-transparent hover:bg-[#EEEEEE] dark:hover:bg-purple-taupe`]: + [`bg-transparent hover:bg-muted`]: !isCopied, 'bg-green-100 dark:bg-green-900 hover:bg-green-100 dark:hover:bg-green-900': isCopied, diff --git a/frontend/src/components/DocumentPagination.tsx b/frontend/src/components/DocumentPagination.tsx index 7f255ed0..f52618d7 100644 --- a/frontend/src/components/DocumentPagination.tsx +++ b/frontend/src/components/DocumentPagination.tsx @@ -61,12 +61,12 @@ const Pagination: React.FC = ({ {rowsPerPage} = ({ handleSelectRowsPerPage(option)} - className={`cursor-pointer px-4 py-2 text-xs hover:bg-gray-100 dark:hover:bg-neutral-700 ${ + className={`cursor-pointer px-4 py-2 text-xs hover:bg-accent dark:hover:bg-accent ${ rowsPerPage === option - ? 'dark:text-light-gray bg-gray-100 dark:bg-neutral-700' - : 'dark:bg-dark-charcoal dark:text-light-gray bg-white' + ? 'dark:text-foreground bg-gray-100 dark:bg-neutral-700' + : 'bg-card dark:text-foreground' }`} > {option} diff --git a/frontend/src/components/Dropdown.tsx b/frontend/src/components/Dropdown.tsx index 9a14ac27..9897498a 100644 --- a/frontend/src/components/Dropdown.tsx +++ b/frontend/src/components/Dropdown.tsx @@ -1,163 +1,221 @@ -import React from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; -import Arrow2 from '../assets/dropdown-arrow.svg'; -import Edit from '../assets/edit.svg'; -import Trash from '../assets/trash.svg'; -import { DropdownOption, DropdownProps } from './types/Dropdown.types'; +import { ChevronDown, Pencil, Search, Trash2 } from 'lucide-react'; + +type OptionBase = { id?: string; type?: string }; +type NameIdOption = { name: string; id: string } & OptionBase; +type LabelValueOption = { label: string; value: string } & OptionBase; +type ValueDescriptionOption = { + value: number; + description: string; +} & OptionBase; + +export type DropdownOption = + | string + | NameIdOption + | LabelValueOption + | ValueDescriptionOption; + +export type { NameIdOption, LabelValueOption, ValueDescriptionOption }; + +function getOptionText(option: DropdownOption): string { + if (typeof option === 'string') return option; + if ('name' in option) return option.name; + if ('label' in option) return option.label; + if ('description' in option) + return option.value < 1e9 + ? `${option.value} (${option.description})` + : option.description; + return ''; +} + +function optionMatches( + option: DropdownOption, + selected: DropdownOption | null, +): boolean { + if (!selected) return false; + if (typeof selected === 'string') return selected === option; + if (typeof option === 'string') return false; + const a = option as Record; + const b = selected as Record; + if ('name' in a && 'name' in b) return a.name === b.name; + if ('label' in a && 'label' in b) return a.label === b.label; + if ('value' in a && 'value' in b) return a.value === b.value; + return false; +} + +export interface DropdownProps { + options: T[]; + selectedValue: DropdownOption | null; + onSelect: (value: T) => void; + size?: string; + rounded?: 'xl' | '3xl'; + searchable?: boolean; + placeholder?: string; + contentSize?: string; + showEdit?: boolean; + onEdit?: (value: NameIdOption) => void; + showDelete?: boolean | ((option: T) => boolean); + onDelete?: (id: string) => void; +} function Dropdown({ options, selectedValue, onSelect, - size = 'w-32', - rounded = 'xl', - buttonClassName = 'border-silver bg-white dark:bg-transparent dark:border-dim-gray', - optionsClassName = 'border-silver bg-white dark:border-dim-gray dark:bg-dark-charcoal', - border = 'border-2', + size = 'w-full', + rounded = '3xl', + searchable = false, + placeholder = 'Select...', + contentSize = 'text-sm', showEdit, onEdit, showDelete, onDelete, - placeholder, - placeholderClassName = 'text-gray-500 dark:text-gray-400', - contentSize = 'text-base', }: DropdownProps) { - const dropdownRef = React.useRef(null); - const [isOpen, setIsOpen] = React.useState(false); - const borderRadius = rounded === 'xl' ? 'rounded-xl' : 'rounded-3xl'; - const borderTopRadius = rounded === 'xl' ? 'rounded-t-xl' : 'rounded-t-3xl'; + const ref = useRef(null); + const searchRef = useRef(null); + const [open, setOpen] = useState(false); + const [query, setQuery] = useState(''); - const handleClickOutside = (event: MouseEvent) => { - if ( - dropdownRef.current && - !dropdownRef.current.contains(event.target as Node) - ) { - setIsOpen(false); - } - }; + const radius = rounded === '3xl' ? 'rounded-3xl' : 'rounded-xl'; + const radiusTop = rounded === '3xl' ? 'rounded-t-3xl' : 'rounded-t-xl'; + const radiusBottom = rounded === '3xl' ? 'rounded-b-3xl' : 'rounded-b-xl'; - React.useEffect(() => { - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); + useEffect(() => { + const handler = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + setOpen(false); + setQuery(''); + } }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); }, []); + useEffect(() => { + if (open && searchable && searchRef.current) searchRef.current.focus(); + }, [open, searchable]); + + const filtered = useMemo(() => { + if (!searchable || !query.trim()) return options; + const q = query.toLowerCase(); + return options.filter((o) => getOptionText(o).toLowerCase().includes(q)); + }, [options, query, searchable]); + + const displayValue = selectedValue ? getOptionText(selectedValue) : null; + return ( - + setIsOpen(!isOpen)} - className={`flex w-full cursor-pointer items-center justify-between ${border} ${buttonClassName} px-5 py-3 ${ - isOpen ? `${borderTopRadius}` : `${borderRadius}` - }`} + type="button" + onClick={() => setOpen((v) => !v)} + className={`flex w-full cursor-pointer items-center justify-between border border-border bg-card px-5 py-3 text-foreground ${open ? radiusTop : radius}`} > - {typeof selectedValue === 'string' ? ( - - {selectedValue} - - ) : ( - - {selectedValue && 'label' in selectedValue - ? selectedValue.label - : selectedValue && 'description' in selectedValue - ? `${ - selectedValue.value < 1e9 - ? selectedValue.value + ` (${selectedValue.description})` - : selectedValue.description - }` - : placeholder - ? placeholder - : 'From URL'} - - )} - + {displayValue ?? placeholder} + + - {isOpen && ( + + {open && ( - {options.map((option: any, index) => ( - - { - onSelect(option); - setIsOpen(false); - }} - className={`dark:text-light-gray ml-5 flex-1 overflow-hidden py-3 text-ellipsis whitespace-nowrap ${contentSize}`} - > - {typeof option === 'string' - ? option - : option.name - ? option.name - : option.label - ? option.label - : `${ - option.value < 1e9 - ? option.value + ` (${option.description})` - : option.description - }`} - - {showEdit && onEdit && option.type !== 'public' && ( - { - onEdit({ - id: option.id, - name: option.name, - type: option.type, - }); - setIsOpen(false); - }} - /> - )} - {showDelete && onDelete && ( - { - e.stopPropagation(); - onDelete?.(typeof option === 'string' ? option : option.id); - }} - className={`${ - typeof showDelete === 'function' && !showDelete(option) - ? 'hidden' - : '' - } mr-2 h-4 w-4 cursor-pointer hover:opacity-50`} - > - - - )} + {searchable && ( + + + setQuery(e.target.value)} + placeholder="Search..." + className="w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus:outline-none" + onClick={(e) => e.stopPropagation()} + /> - ))} + )} + + + {filtered.length === 0 ? ( + + No results found + + ) : ( + filtered.map((option, i) => { + const active = optionMatches(option, selectedValue); + const optObj = + typeof option !== 'string' + ? (option as Record) + : null; + + return ( + + { + onSelect(option); + setOpen(false); + setQuery(''); + }} + className={`flex-1 truncate px-4 py-2.5 text-foreground ${contentSize}`} + > + {getOptionText(option)} + + + {showEdit && + onEdit && + optObj && + optObj.type !== 'public' && ( + { + onEdit({ + id: optObj.id as string, + name: optObj.name as string, + type: optObj.type as string, + }); + setOpen(false); + setQuery(''); + }} + className="mr-1 rounded p-1 hover:bg-accent" + > + + + )} + + {showDelete && onDelete && ( + { + e.stopPropagation(); + onDelete( + typeof option === 'string' + ? option + : ((optObj?.id as string) ?? ''), + ); + }} + className={`mr-1 rounded p-1 hover:bg-accent ${ + typeof showDelete === 'function' && !showDelete(option) + ? 'hidden' + : '' + } ${optObj?.type === 'public' ? 'pointer-events-none opacity-30' : ''}`} + > + + + )} + + ); + }) + )} + )} diff --git a/frontend/src/components/DropdownMenu.tsx b/frontend/src/components/DropdownMenu.tsx index 57e84eaa..b9803912 100644 --- a/frontend/src/components/DropdownMenu.tsx +++ b/frontend/src/components/DropdownMenu.tsx @@ -88,7 +88,7 @@ export default function DropdownMenu({ onClick={(e) => e.stopPropagation()} > ( setIsOpen(!isOpen)} > {selectedModel?.display_name ? ( - + {selectedModel.display_name} ) : ( - + Select Model )} @@ -101,7 +101,7 @@ export default function DropdownModel() { /> {isOpen && ( - + {availableModels && (availableModels?.length ?? 0) > 0 ? ( availableModels.map((model: Model) => ( @@ -127,8 +127,8 @@ export default function DropdownModel() { )) ) : ( - - No models available + + No models available )} diff --git a/frontend/src/components/FilePicker.tsx b/frontend/src/components/FilePicker.tsx index 36b484f4..7db6c132 100644 --- a/frontend/src/components/FilePicker.tsx +++ b/frontend/src/components/FilePicker.tsx @@ -459,10 +459,10 @@ export const FilePicker: React.FC = ({ /> {isConnected && ( - - + + {provider === 'share_point' && allowsSharedContent && ( - + handleTabChange('my_files')} className={`px-4 py-2 text-sm font-medium ${ @@ -485,7 +485,7 @@ export const FilePicker: React.FC = ({ )} - + {folderPath.map((path, index) => ( = ({ onChange={(e) => handleSearchChange(e.target.value)} colorVariant="silver" borderVariant="thin" - labelBgClassName="bg-[#EEE6FF78] dark:bg-[#2A262E]" + labelBgClassName="bg-[#EEE6FF78] dark:bg-muted" leftIcon={ } @@ -531,7 +531,7 @@ export const FilePicker: React.FC = ({ - + = ({ > { e.stopPropagation(); handleFileSelect(file.id, isFolder(file)); diff --git a/frontend/src/components/FileSelectionSkeleton.tsx b/frontend/src/components/FileSelectionSkeleton.tsx index 1aa45769..b26927b9 100644 --- a/frontend/src/components/FileSelectionSkeleton.tsx +++ b/frontend/src/components/FileSelectionSkeleton.tsx @@ -1,5 +1,5 @@ const FilesSectionSkeleton = () => ( - + diff --git a/frontend/src/components/FileTree.tsx b/frontend/src/components/FileTree.tsx index f150d804..11424380 100644 --- a/frontend/src/components/FileTree.tsx +++ b/frontend/src/components/FileTree.tsx @@ -450,7 +450,7 @@ const FileTree: React.FC = ({ {/* Left side with path navigation */} @@ -503,7 +503,7 @@ const FileTree: React.FC = ({ {!processingRef.current && ( {t('settings.sources.addFile')} @@ -599,7 +599,7 @@ const FileTree: React.FC = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ handleMenuClick(e, itemId)} - className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]" + className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-muted" aria-label={t('settings.sources.menuAlt')} > = ({ } }} placeholder={t('settings.sources.searchFiles')} - className={`h-[38px] w-full border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`} + className={`h-[38px] w-full border border-border px-4 py-2 dark:border-border ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none`} /> {searchQuery && ( - + {searchResults.length === 0 ? ( @@ -770,9 +770,9 @@ const FileTree: React.FC = ({ key={index} onClick={() => handleSearchSelect(result)} title={result.path} - className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${ + className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-muted dark:hover:bg-muted ${ index !== searchResults.length - 1 - ? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]' + ? 'border-b border-border dark:border-border' : '' }`} > @@ -785,7 +785,7 @@ const FileTree: React.FC = ({ } className="mr-2 h-4 w-4 flex-shrink-0" /> - + {result.name} diff --git a/frontend/src/components/FileUpload.tsx b/frontend/src/components/FileUpload.tsx index cc9dcb26..fb4ad9b6 100644 --- a/frontend/src/components/FileUpload.tsx +++ b/frontend/src/components/FileUpload.tsx @@ -41,7 +41,7 @@ export const FileUpload = ({ showPreview = false, previewSize = 80, children, - className = 'border-2 border-dashed rounded-3xl p-6 text-center cursor-pointer transition-colors border-silver dark:border-[#7E7E7E]', + className = 'border-2 border-dashed rounded-3xl p-6 text-center cursor-pointer transition-colors border-border dark:border-border', activeClassName = 'border-blue-500 bg-blue-50', acceptClassName = 'border-green-500 dark:border-green-500 bg-green-50 dark:bg-green-50/10', rejectClassName = 'border-red-500 bg-red-50 dark:bg-red-500/10 dark:border-red-500', @@ -133,7 +133,7 @@ export const FileUpload = ({ }); const currentClassName = twMerge( - 'border-2 border-dashed rounded-3xl p-8 text-center cursor-pointer transition-colors border-silver dark:border-[#7E7E7E]', + 'border-2 border-dashed rounded-3xl p-8 text-center cursor-pointer transition-colors border-border dark:border-border', className, isDragActive && activeClassName, isDragAccept && acceptClassName, diff --git a/frontend/src/components/GoogleDrivePicker.tsx b/frontend/src/components/GoogleDrivePicker.tsx index bb3d240b..6cedb828 100644 --- a/frontend/src/components/GoogleDrivePicker.tsx +++ b/frontend/src/components/GoogleDrivePicker.tsx @@ -262,7 +262,7 @@ const GoogleDrivePicker: React.FC = ({ /> {isConnected && ( - + diff --git a/frontend/src/components/Help.tsx b/frontend/src/components/Help.tsx index 896bd2ec..498de3c1 100644 --- a/frontend/src/components/Help.tsx +++ b/frontend/src/components/Help.tsx @@ -36,20 +36,20 @@ const Help = () => { {t('help')} {isOpen && ( { { const colorStyles = { - silver: 'border-silver dark:border-silver/40', + silver: 'border-border dark:border-border', jet: 'border-jet', - gray: 'border-gray-5000 dark:text-silver', + gray: 'border-gray-5000 dark:text-muted-foreground', }; const borderStyles = { thin: 'border', @@ -44,7 +44,7 @@ const Input = ({ {placeholder} {required && ( - * + * )} )} diff --git a/frontend/src/components/MermaidRenderer.tsx b/frontend/src/components/MermaidRenderer.tsx index 3e8c2eb8..6a81b806 100644 --- a/frontend/src/components/MermaidRenderer.tsx +++ b/frontend/src/components/MermaidRenderer.tsx @@ -1,16 +1,17 @@ +import mermaid from 'mermaid'; import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import mermaid from 'mermaid'; -import CopyButton from './CopyButton'; +import { useSelector } from 'react-redux'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneLight, vscDarkPlus, } from 'react-syntax-highlighter/dist/cjs/styles/prism'; -import { MermaidRendererProps } from './types'; -import { useSelector } from 'react-redux'; + import { selectStatus } from '../conversation/conversationSlice'; import { useDarkTheme } from '../hooks'; +import CopyButton from './CopyButton'; +import { MermaidRendererProps } from './types'; const MermaidRenderer: React.FC = ({ code, @@ -262,9 +263,9 @@ const MermaidRenderer: React.FC
{chunk.text}
{selectedModel.display_name}
Select Model
@@ -127,8 +127,8 @@ export default function DropdownModel() {
No models available