Skip to content

Fang dev#2

Merged
a7m-1st merged 9 commits intomainfrom
fang-dev
Feb 19, 2026
Merged

Fang dev#2
a7m-1st merged 9 commits intomainfrom
fang-dev

Conversation

@ziflhigan
Copy link
Copy Markdown
Collaborator

UI/UX Enhancements

… chat-centric layout, render the `end` event as the main AI response
  Theme System:
  - Add full Light/Dark/System theme switching with CSS variables
  - Implement semantic color system with theme-aware utility classes
  - Persist theme preference to localStorage with system preference detection
  - Update all core components (Dashboard, MessageBubble, TaskInputPanel, etc.)

  Agent Activity Panel:
  - Add AgentActivityPanel component showing detailed agent progress
  - Display current task, tool usage, output, and activity log per agent
  - Add MonitoringPanel with tabs to switch between Task Map and Agents
  - Extend agentStatusStore with lastInput/lastOutput fields

  Bug Fixes:
  - Fix stale closure in useSSEHandler causing agent states not updating on task end
@ziflhigan ziflhigan requested review from a7m-1st and Copilot February 18, 2026 15:48
@ziflhigan ziflhigan self-assigned this Feb 18, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on UI/UX enhancements across the dashboard and conversation experience, while also adding a backend-driven fallback for model/API-key configuration.

Changes:

  • Added a theme system with persistence and “light/dark/system” support, plus new theme toggles in UI.
  • Restructured the Dashboard layout with a new Conversation panel and a right-side Monitoring panel (Task Map + Agent Activity).
  • Added backend config detection and env-defaulting for model/API key, plus a backend endpoint to report config status.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
frontend/src/stores/uiStore.ts Adds persisted theme state + DOM theme application and system-theme listening.
frontend/src/stores/apiConfigStore.ts Adds backend config status check and uses it as fallback when no local key exists.
frontend/src/stores/agentStatusStore.ts Stores last agent input/output for “thinking” and expanded activity displays.
frontend/src/pages/DashboardPage.tsx Reworks layout into Conversation center + animated Monitoring sidebar + theme toggle.
frontend/src/index.css Introduces CSS-variable-based theme palette and additional UI utilities/animations.
frontend/src/hooks/useSnapshots.ts Adjusts snapshot fetching behavior to capture final screenshot on completion.
frontend/src/hooks/useSSEHandler.ts Fixes agent-store reads by pulling fresh state via getState().
frontend/src/components/task-panel/TaskDecompositionPanel.tsx Updates Task Map visuals and adds a terminal-style footer.
frontend/src/components/task-panel/MonitoringPanel.tsx New tabbed wrapper for Task Map and Agent Activity.
frontend/src/components/task-panel/AgentActivityPanel.tsx New agent activity UI with expandable cards and parsed I/O display.
frontend/src/components/resources/BrowserSnapshots.tsx Restyles snapshots panel and adds empty/loading state when agent absent.
frontend/src/components/layout/Header.tsx Replaces simple toggle with a theme dropdown (light/dark/system).
frontend/src/components/layout/ErrorBanner.tsx Restyles banner to match the new theme tokens.
frontend/src/components/conversation/ThinkingIndicator.tsx New animated “thinking/planning/working” indicator based on stores.
frontend/src/components/conversation/MessageBubble.tsx New chat bubble UI for user/assistant/system messages.
frontend/src/components/conversation/ConversationPanel.tsx New conversation view with empty state and scroll management.
frontend/src/components/chat/TaskInputPanel.tsx Updates input styling; allows backend API key fallback by sending empty api_key.
frontend/src/components/api-key/ApiKeyModal.tsx Restyles API key modal to match new theme tokens.
backend/requirements.txt Adds a pip requirements file with pinned/minimum versions.
backend/app/model/chat.py Allows model config fields to default from environment variables.
backend/app/controller/health_controller.py Adds /config/status endpoint reporting backend config presence.
backend/app/controller/chat_controller.py Avoids overwriting OPENAI_API_KEY when api_key is empty.
backend/app/init.py Loads backend .env at startup.
backend/.env_example Adds example env file for backend model configuration.
Comments suppressed due to low confidence (1)

frontend/src/stores/apiConfigStore.ts:1

  • If there’s no valid localStorage key and the backend also has no API key, the modal may never open because isModalOpen defaults to false and checkBackendConfig() doesn’t set it to true on "no key". Fix by explicitly opening the modal when backend reports has_api_key=false (or after checkBackendConfig resolves), e.g., in checkBackendConfig set state.isModalOpen = !has_api_key when no frontend key is present.
import { create } from 'zustand';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +122 to +145
initializeTheme: () => {
const { theme } = get()
const resolved = resolveTheme(theme)
set((state) => {
state.resolvedTheme = resolved
})
applyTheme(resolved)

// Listen for system theme changes
if (typeof window !== 'undefined') {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handler = () => {
const currentTheme = get().theme
if (currentTheme === 'system') {
const newResolved = getSystemTheme()
set((state) => {
state.resolvedTheme = newResolved
})
applyTheme(newResolved)
}
}
mediaQuery.addEventListener('change', handler)
}
},
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initializeTheme() registers a matchMedia "change" listener but never removes it, and module-level initialization will re-register on HMR / repeated module evaluation. This can leak listeners and duplicate updates. Consider registering the listener exactly once (track a module-level flag and store the handler), and provide cleanup via removeEventListener (e.g., in a dedicated setup function called from the app root).

Copilot uses AI. Check for mistakes.
Comment on lines +157 to +163
// Initialize theme listener when module loads
if (typeof window !== 'undefined') {
// Delay to ensure store is ready
setTimeout(() => {
useUIStore.getState().initializeTheme()
}, 0)
} No newline at end of file
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initializeTheme() registers a matchMedia "change" listener but never removes it, and module-level initialization will re-register on HMR / repeated module evaluation. This can leak listeners and duplicate updates. Consider registering the listener exactly once (track a module-level flag and store the handler), and provide cleanup via removeEventListener (e.g., in a dedicated setup function called from the app root).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines +37 to 41
const applyTheme = (theme: 'light' | 'dark') => {
const root = document.documentElement
root.classList.remove('light', 'dark')
root.classList.add(theme)
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyTheme() accesses document unconditionally; setTheme() can be invoked in non-DOM contexts (tests, SSR, pre-render), which will throw. Align with your other guards by adding a typeof window/document check inside applyTheme() (or before calling it).

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +99
setTheme: (theme) =>
set((state) => {
state.theme = theme
const resolved = resolveTheme(theme)
state.resolvedTheme = resolved
applyTheme(resolved)
}),
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyTheme() accesses document unconditionally; setTheme() can be invoked in non-DOM contexts (tests, SSR, pre-render), which will throw. Align with your other guards by adding a typeof window/document check inside applyTheme() (or before calling it).

Copilot uses AI. Check for mistakes.
Comment on lines 33 to 39
startPolling(taskId);
} else {
stopPolling();
// Do a final fetch when the agent finishes to capture the last screenshot
fetchSnapshots();
}

// Initial fetch
fetchSnapshots();
}, [taskId, agentState, startPolling, stopPolling, fetchSnapshots]);
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removes the unconditional initial fetch. If startPolling() doesn’t immediately fetch, the UI may show no snapshots until the first polling interval elapses. Consider performing fetchSnapshots() when polling starts as well (e.g., call fetchSnapshots() before/after startPolling(taskId)) while keeping the “final fetch” on stop.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines +93 to +105
<button
key={suggestion.text}
className={cn(
"flex items-center gap-2 px-4 py-2.5 rounded-xl",
"bg-card border border-card-border",
"text-sm text-foreground-secondary",
"hover:border-accent hover:text-accent hover:bg-accent-light/50",
"transition-all duration-200 cursor-default"
)}
>
<suggestion.icon className="w-4 h-4" />
{suggestion.text}
</button>
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are rendered as elements but have no onClick behavior and are styled with cursor-default. For accessibility/semantics, either add an action (e.g., populate the input with the suggestion) or change them to non-interactive elements (e.g.,

/) so they aren’t focusable controls that do nothing.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines +104 to +107
<button className="flex items-center gap-1 hover:text-accent transition-colors">
<ExternalLink className="w-3 h-3" />
<span>Sources</span>
</button>
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This renders an interactive button with no click handler (and no disabled state), which is confusing for keyboard/screen-reader users. Either implement the “Sources” action (open a sources panel/modal) or render it as non-interactive text until the feature exists.

Suggested change
<button className="flex items-center gap-1 hover:text-accent transition-colors">
<ExternalLink className="w-3 h-3" />
<span>Sources</span>
</button>
<span className="flex items-center gap-1 hover:text-accent transition-colors">
<ExternalLink className="w-3 h-3" />
<span>Sources</span>
</span>

Copilot uses AI. Check for mistakes.
…er-configured model (from .env) via ModelFactory.create() in multi_modal.py.

Fix user message disappearing after send: startChat() was clearing all messages including the optimistically-added user message. Now reads fresh state via getState() to preserve it across the clear.
Fix UnicodeDecodeError on non-English Windows: override blocking shell_exec path with errors="replace" in subprocess.Popen so non-UTF-8 system codepage output doesn't crash the readerthread.
Fix ModernChatWindow sync dropping images and ignoring empty state.
…COMPLEX)

  - SIMPLE questions get direct answers without agent orchestration
  - Dynamic agent creation: only instantiate required agents based on triage
  - Save uploaded base64 images to project folder, pass file paths to agents
    (fixes abnormal token counts caused by passing raw base64 to ImageAnalysisToolkit)
Copy link
Copy Markdown

Copilot AI commented Feb 19, 2026

@a7m-1st I've opened a new pull request, #5, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown

Copilot AI commented Feb 19, 2026

@a7m-1st I've opened a new pull request, #6, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown

Copilot AI commented Feb 19, 2026

@a7m-1st I've opened a new pull request, #7, to work on those changes. Once the pull request is ready, I'll request review from you.

Comment on lines -38 to +46
image_analysis_toolkit = ImageAnalysisToolkit(options.project_id)
toolkit_model = ModelFactory.create(
model_platform=options.model_platform.lower(),
model_type=options.model_type,
api_key=options.api_key,
url=options.api_url,
)
image_analysis_toolkit = ImageAnalysisToolkit(
options.project_id, model=toolkit_model
)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

Comment on lines +1406 to +1416

# Medical Assistant Coordinator prompt for triage and direct answering
MEDICAL_COORDINATOR_PROMPT = """You are MedGemma, a knowledgeable and helpful medical assistant.

<your_role>
You serve as both a medical information assistant AND a coordinator for complex tasks.
For simple medical questions, you provide direct, accurate answers.
For complex tasks requiring specialized tools (image analysis, web search, document creation),
you coordinate with specialized agents.
</your_role>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is still gemini?

Comment on lines +69 to +71
# Normalize format extension
ext_map = {"jpeg": "jpg", "png": "png", "gif": "gif", "webp": "webp"}
ext = ext_map.get(image_format, image_format)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pdf can?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need requirements.txt. Better to use "uv sync" instead.
Let me remove it

@a7m-1st a7m-1st merged commit 05a2f2e into main Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants