Chatbot UI Design Recommendation for Lawyer Assistant
Overview
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Electron App │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Main Process (Node.js) │ │
│ │ • App lifecycle management │ │
│ │ • Window management │ │
│ │ • Python backend spawning │ │
│ │ • IPC communication │ │
│ │ • File system access │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ IPC Bridge │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Renderer Process (React + TypeScript) │ │
│ │ • Chat UI │ │
│ │ • Source viewer │ │
│ │ • Settings │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ HTTP/WebSocket │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Python Backend (FastAPI/Flask) │ │
│ │ • ChatPipeline │ │
│ │ • RAG processing │ │
│ │ • Document management │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Key Design Principles
- Professional & Trustworthy - Legal domain requires a clean, authoritative design
- Clean & Modern - Contemporary design with clear visual hierarchy
- Information-Rich - Display sources, citations, and document references prominently
- Responsive - Works seamlessly on desktop, tablet, and mobile
- Accessible - WCAG 2.1 compliance for broad usability
- Offline-Capable - Core functionality works without internet
Technology Stack
Frontend (Renderer Process)
- React 18 + TypeScript - UI components
- Vite - Fast build tooling
- Tailwind CSS - Styling
- Zustand - State management
- shadcn/ui - UI component library
Electron Main Process
- electron-vite - Build tool for Electron + Vite
- electron-builder - Packaging and distribution
- electron-updater - Auto-updates
Backend (Python)
- FastAPI or Flask - Local HTTP server
- ChatPipeline - Existing RAG implementation
- uvicorn - ASGI server
Project Structure
lawyer-assistant/
├── frontend/electron/ # Electron main process
│ ├── src/
│ │ ├── main/
│ │ │ ├── index.ts # Main entry point
│ │ │ ├── window.ts # Window management
│ │ │ ├── menu.ts # Application menu
│ │ │ ├── tray.ts # System tray
│ │ │ └── python-bridge.ts # Python process management
│ │ ├── preload/
│ │ │ └── index.ts # Context bridge
│ │ └── utils/
│ │ └── paths.ts # File paths utility
│ ├── electron-builder.yml
│ └── package.json
├── frontend/ # React UI (Renderer)
│ ├── src/
│ │ ├── components/
│ │ │ ├── Chat/
│ │ │ │ ├── ChatArea.tsx
│ │ │ │ ├── Message.tsx
│ │ │ │ ├── UserMessage.tsx
│ │ │ │ ├── AssistantMessage.tsx
│ │ │ │ ├── TypingIndicator.tsx
│ │ │ │ └── InputBar.tsx
│ │ │ ├── Sources/
│ │ │ │ ├── SourcesPanel.tsx
│ │ │ │ ├── SourceItem.tsx
│ │ │ │ └── SourceViewerModal.tsx
│ │ │ ├── Sidebar/
│ │ │ │ ├── Sidebar.tsx
│ │ │ │ └── ConversationList.tsx
│ │ │ ├── Layout/
│ │ │ │ ├── Header.tsx
│ │ │ │ └── TitleBar.tsx
│ │ │ └── UI/
│ │ │ └── ...
│ │ ├── hooks/
│ │ ├── services/
│ │ │ └── api.ts
│ │ ├── store/
│ │ ├── types/
│ │ └── App.tsx
│ ├── index.html
│ ├── package.json
│ ├── vite.config.ts
│ └── tsconfig.json
├── backend/ # Python backend
│ ├── main.py # FastAPI server entry
│ ├── api/
│ │ ├── chat.py # Chat endpoints
│ │ └── documents.py # Document endpoints
│ ├── backend/legal_retrieval/ # Existing RAG code
│ └── requirements.txt
├── package.json # Root package.json
└── README.md
Layout Design
Desktop Layout
┌─────────────────────────────────────────────────────────────────┐
│ ─ Title Bar (Custom) ────────────────────────────────────────── │
│ [Logo] Lawyer Assistant [─] [□] [×] │
├──────────────┬──────────────────────────────┬───────────────────┤
│ │ │ │
│ Sidebar │ Chat Area │ Sources Panel │
│ (280px) │ │ (320px) │
│ │ ┌─────────────────────┐ │ │
│ [New Chat] │ │ User: What is... │ │ Sources [3] │
│ │ └─────────────────────┘ │ │
│ History │ ┌─────────────────────┐ │ ┌─────────────┐ │
│ ────────── │ │ Assistant: Based │ │ │ [1] Contract │ │
│ • Chat 1 │ │ on the document │ │ │ Page 5 │ │
│ • Chat 2 │ │ [1] [2] [3] │◄───│ │ [View Full] │ │
│ • Chat 3 │ └─────────────────────┘ │ └─────────────┘ │
│ │ │ │
│ Documents │ ┌─────────────────────┐ │ ┌─────────────┐ │
│ ────────── │ │ User: Can you... │ │ │ [2] Clause │ │
│ 📄 doc1.pdf │ └─────────────────────┘ │ │ Page 12 │ │
│ 📄 doc2.pdf │ │ │ [View Full] │ │
│ │ ┌─────────────────────┐ │ └─────────────┘ │
│ │ │ [Thinking...] │ │ │
│ │ └─────────────────────┘ │ │
│ │ │ │
├──────────────┴──────────────────────────────┴───────────────────┤
│ ─ Input Bar ────────────────────────────────────────────────── │
│ [Mode: RAG ▾] │ Type your question... │ [Send] │
└─────────────────────────────────────────────────────────────────┘
Source Viewer Modal (Floating)
┌────────────────────────────────────────────────┐
│ 📄 Source [1] - Contract Agreement [─][□][×] │
├────────────────────────────────────────────────┤
│ Document: contract_2024.pdf │
│ Page: 5 | Section: 3.2 | Relevance: 94% │
├────────────────────────────────────────────────┤
│ │
│ "The parties agree that any modification to │
│ this agreement must be made in writing and │
│ signed by both parties. Verbal agreements │
│ shall not be considered binding under this │
│ contract..." │
│ │
│ [Highlighted matching terms shown in bold] │
│ │
├────────────────────────────────────────────────┤
│ [📋 Copy] [📌 Pin] [Open Document] [Close] │
└────────────────────────────────────────────────┘
Component Breakdown
1. Custom Title Bar
typescriptinterface TitleBarProps {
appName: string;
onMinimize: () => void;
onMaximize: () => void;
onClose: () => void;
isMaximized: boolean;
}
- Custom window controls (minimize, maximize, close)
- Drag region for window movement
- App logo and title
2. Chat Area Component
typescriptinterface ChatAreaProps {
messages: Message[];
isTyping: boolean;
onRetry: (messageId: string) => void;
onCopy: (messageId: string) => void;
onCitationClick: (sourceId: string) => void;
}
Message Components:
- UserMessage - Right-aligned, primary color background
- AssistantMessage - Left-aligned, neutral background with:
- Markdown rendering
- Inline Citation Highlights (clickable [1], [2])
- Code blocks
- TypingIndicator - Animated dots with "Thinking..."
3. Source Viewer Modal
typescriptinterface SourceViewerModalProps {
source: Source | null;
isOpen: boolean;
onClose: () => void;
citationNumber: number;
}
- Floating draggable window
- Full source paragraph with highlighted terms
- Document metadata (filename, page, section)
- Actions: Copy, Pin, Open Document
- Multiple viewers can be open simultaneously
4. Sources Panel
typescriptinterface SourcesPanelProps {
sources: Source[];
selectedSourceId: string | null;
onSelectSource: (id: string) => void;
onOpenViewer: (sourceId: string) => void;
isOpen: boolean;
onToggle: () => void;
}
- Source list with citation badges
- "View Full Source" button on each
- Visual highlighting synced with chat
Electron Main Process
Window Management
typescript// frontend/electron/src/main/window.ts
import { BrowserWindow } from 'electron';
export function createMainWindow(): BrowserWindow {
const mainWindow = new BrowserWindow({
width: 1400,
height: 900,
minWidth: 1000,
minHeight: 600,
frame: false, // Custom title bar
titleBarStyle: 'hidden',
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, '../preload/index.js'),
},
});
return mainWindow;
}
Python Backend Bridge
typescript// frontend/electron/src/main/python-bridge.ts
import { spawn, ChildProcess } from 'child_process';
import { app } from 'electron';
export class PythonBackend {
private process: ChildProcess | null = null;
private port: number = 8765;
async start(): Promise<void> {
const pythonPath = this.getPythonPath();
const scriptPath = this.getBackendScript();
this.process = spawn(pythonPath, [scriptPath, '--port', String(this.port)], {
stdio: 'pipe',
});
// Wait for server to be ready
await this.waitForReady();
}
private getPythonPath(): string {
if (app.isPackaged) {
// Packaged: use bundled Python
return path.join(process.resourcesPath, 'python', 'python.exe');
}
// Development: use system Python
return 'python';
}
private getBackendScript(): string {
if (app.isPackaged) {
return path.join(process.resourcesPath, 'python', 'main.py');
}
return path.join(__dirname, '../../backend/main.py');
}
async stop(): Promise<void> {
if (this.process) {
this.process.kill();
this.process = null;
}
}
}
Preload Script (Security Bridge)
typescript// frontend/electron/src/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
// Window controls
minimize: () => ipcRenderer.invoke('window:minimize'),
maximize: () => ipcRenderer.invoke('window:maximize'),
close: () => ipcRenderer.invoke('window:close'),
// Python backend
getBackendPort: () => ipcRenderer.invoke('python:port'),
// File system
openFile: () => ipcRenderer.invoke('dialog:openFile'),
readFile: (path: string) => ipcRenderer.invoke('fs:readFile', path),
// App info
getVersion: () => ipcRenderer.invoke('app:version'),
getPlatform: () => ipcRenderer.invoke('app:platform'),
});
IPC Communication
Main Process Handlers
typescript// frontend/electron/src/main/index.ts
import { ipcMain } from 'electron';
ipcMain.handle('window:minimize', () => mainWindow.minimize());
ipcMain.handle('window:maximize', () => {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
});
ipcMain.handle('window:close', () => mainWindow.close());
ipcMain.handle('python:port', () => pythonBackend.getPort());
Frontend API Service
typescript// frontend/src/services/api.ts
class ApiService {
private baseUrl: string;
constructor() {
// In development, use localhost
// In production, get port from Electron
this.baseUrl = 'http://localhost:8765';
}
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
const response = await fetch(`${this.baseUrl}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
return response.json();
}
async getDocuments(): Promise<Document[]> {
const response = await fetch(`${this.baseUrl}/api/documents`);
return response.json();
}
}
Python Backend API
python# backend/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
app = FastAPI()
# Allow Electron renderer
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"], # Vite dev server
allow_methods=["*"],
allow_headers=["*"],
)
@app.post("/api/chat")
async def chat(request: ChatRequest):
# Use existing ChatPipeline
response = pipeline.chat(
query=request.query,
mode=request.mode,
top_k=request.top_k,
)
return response.to_dict()
@app.get("/api/documents")
async def get_documents():
# Return list of ingested documents
pass
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8765)
Packaging & Distribution
electron-builder.yml
yamlappId: com.lawyerassistant.app
productName: Lawyer Assistant
directories:
output: dist
files:
- frontend/dist/**/*
- frontend/electron/dist/**/*
extraResources:
- from: backend/
to: python/
filter:
- "**/*.py"
- "requirements.txt"
- from: python-env/
to: python/env/
win:
target: nsis
icon: assets/icon.ico
mac:
target: dmg
icon: assets/icon.icns
linux:
target: AppImage
icon: assets/icon.png
Build Scripts
json{
"scripts": {
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\" \"npm run dev:electron\"",
"dev:backend": "cd backend && python main.py",
"dev:frontend": "cd frontend && npm run dev",
"dev:electron": "cd electron && npm run dev",
"build": "npm run build:frontend && npm run build:electron",
"build:frontend": "cd frontend && npm run build",
"build:electron": "cd electron && npm run build",
"package": "npm run build && cd electron && npm run package",
"package:win": "npm run build && cd electron && npm run package:win",
"package:mac": "npm run build && cd electron && npm run package:mac",
"package:linux": "npm run build && cd electron && npm run package:linux"
}
}
Styling & Theming
Color Palette (Light Mode)
css:root {
--primary: #2563eb;
--primary-hover: #1d4ed8;
--background: #ffffff;
--surface: #f8fafc;
--text-primary: #0f172a;
--text-secondary: #64748b;
--border: #e2e8f0;
--citation: #8b5cf6;
}
Color Palette (Dark Mode)
css[data-theme="dark"] {
--background: #0f172a;
--surface: #1e293b;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--border: #334155;
}
Typography
cssfont-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
Interactive Features
1. Citation Interactions
- Hover - Tooltip with source preview
- Click - Opens Source Viewer Modal
- Visual sync between chat citations and sources panel
2. Source Viewer Modal
- Draggable, resizable, pinnable
- Multiple viewers simultaneously
- Full paragraph with context
- Query term highlighting
3. Keyboard Shortcuts
Ctrl+N- New chatCtrl+W- Close current chatCtrl+,- SettingsCtrl+Shift+M- Toggle sources panelEscape- Close modals
Security Considerations
- Context Isolation - Enabled by default
- Node Integration - Disabled in renderer
- Preload Script - Secure bridge only
- CSP Headers - Strict content security policy
- Input Sanitization - All IPC data validated
- Local Backend - No external network access required
Implementation Phases
Phase 1: Project Setup
- Initialize Electron + Vite + React project
- Set up Python backend with FastAPI
- Configure IPC communication
- Create basic window with custom title bar
Phase 2: Core Chat
- Implement chat interface
- Message display (user/assistant)
- API integration with ChatPipeline
- Basic error handling
Phase 3: Sources & Citations
- Sources panel
- Citation highlighting
- Source Viewer Modal
- Document preview
Phase 4: History & Settings
- Conversation history sidebar
- Settings modal
- Theme switching (light/dark)
Phase 5: Polish & Packaging
- Animations & transitions
- Keyboard shortcuts
- Auto-updater
- Platform packaging (Windows, Mac, Linux)
Estimated Timeline
- Week 1-2: Project setup, Electron + Python integration
- Week 3-4: Core chat, API integration
- Week 5-6: Sources, citations, Source Viewer Modal
- Week 7-8: History, settings, theming
- Week 9-10: Polish, packaging, testing
Dependencies
Frontend
json{
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.4.0",
"react-markdown": "^9.0.0",
"lucide-react": "^0.294.0"
}
Electron
json{
"electron": "^28.0.0",
"electron-builder": "^24.0.0",
"electron-vite": "^2.0.0"
}
Backend
fastapi>=0.104.0
uvicorn>=0.24.0
This design document provides a comprehensive roadmap for building a professional, secure, and feature-rich Electron desktop application for the Lawyer Assistant system.