Disability-Aware, Privacy-First, Local AI Chat Editor
Electron + React desktop app
Transformers.js with quantized DistilBERT models
Meta’s Lexical editor for rich text
Tailwind CSS + Shadcn/UI for styling
OpenDyslexic font support
High-contrast mode (WCAG AAA)
Cognitive Focus Mode
Zero-network protocol — no HTTP requests ever
4 phases: Foundation → AI Logic → Accessibility Modules → Testing & Docs
Python CLI tool (simplify.py + mode modules)
Fine-tuned T5-small model (./t5-simplifier/, ~242MB)
3 working modes: dyslexia, ADHD, autism
Readability metrics (Flesch, word count, sentence length)
No UI at all
Build the Electron + React frontend that was promised, but instead of re-implementing the AI in JavaScript, call the existing Python backend as a subprocess. This is architecturally honest — the “Local Backend (The Brain)” from the presentation runs as a Python process managed by Electron’s main process.
2 weeks. Need a working demo + results slide for Phase 2 presentation.
┌──────────────────────────────────────────────────────┐
│ Electron App │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ React Frontend (Renderer) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │ │
│ │ │ Editor │ │ Mode │ │ Output Panel │ │ │
│ │ │ (input) │ │ Selector │ │ (result) │ │ │
│ │ └──────────┘ └──────────┘ └────────────────┘ │ │
│ │ ┌──────────────────────┐ ┌────────────────┐ │ │
│ │ │ Metrics Display │ │ Settings │ │ │
│ │ └──────────────────────┘ └────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ IPC │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Electron Main Process │ │
│ │ │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ Python Bridge (child_process.spawn) │ │ │
│ │ │ Calls: python simplify_server.py │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ stdin/stdout (JSON) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Python Backend │ │
│ │ │ │
│ │ simplify_server.py (JSON stdin/stdout mode) │ │
│ │ ├── simplify.py (core logic) │ │
│ │ ├── dyslexia_mode.py │ │
│ │ ├── adhd_mode.py │ │
│ │ ├── autism_mode.py │ │
│ │ ├── utils.py │ │
│ │ └── t5-simplifier/ (fine-tuned model) │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
Electron main process spawns python simplify_server.py
Frontend sends JSON requests via IPC → main process → stdin to Python
Python processes and writes JSON response to stdout
Main process reads stdout → sends result via IPC → frontend updates
chat editor ( mini project )/
├── electron-app/ # NEW — Electron desktop app
│ ├── package.json
│ ├── electron/
│ │ ├── main.js # Electron main process
│ │ ├── preload.js # Secure bridge (contextBridge)
│ │ └── python-bridge.js # Spawns & manages Python process
│ ├── src/
│ │ ├── App.jsx # Root React component
│ │ ├── App.css # Global styles
│ │ ├── index.jsx # React entry point
│ │ ├── index.html # HTML shell
│ │ ├── components/
│ │ │ ├── Editor.jsx # Text input area
│ │ │ ├── OutputPanel.jsx # Simplified text display
│ │ │ ├── ModeSelector.jsx # Dyslexia/ADHD/Autism toggle
│ │ │ ├── MetricsPanel.jsx # Before/after readability stats
│ │ │ ├── SettingsPanel.jsx # Font, theme, contrast settings
│ │ │ └── Toolbar.jsx # Top action bar
│ │ ├── styles/
│ │ │ ├── themes.css # Light/dark/high-contrast themes
│ │ │ └── fonts.css # OpenDyslexic font-face
│ │ └── assets/
│ │ └── fonts/
│ │ └── OpenDyslexic-Regular.otf
│ ├── tailwind.config.js
│ ├── postcss.config.js
│ └── vite.config.js # Bundler config
│
├── simplify.py # Existing — CLI tool (unchanged)
├── simplify_server.py # NEW — JSON stdin/stdout wrapper
├── dyslexia_mode.py # Existing
├── adhd_mode.py # Existing
├── autism_mode.py # Existing
├── utils.py # Existing
├── t5-simplifier/ # Existing — fine-tuned model
├── requirements.txt # Existing
└── docs/ # Existing
simplify_server.pyThe existing simplify.py uses argparse and reads from files. The Electron app needs to send text directly (not via files). We create a thin JSON wrapper that:
Reads JSON from stdin: {"text": "...", "mode": "dyslexia", "model": "small"}
Calls the existing process_text() and compute_metrics() functions
Writes JSON to stdout: {"result": "...", "metrics": {...}, "error": null}
Stays alive for multiple requests (persistent process, not one-shot)
Protocol:
Each request is one line of JSON followed by \n
Each response is one line of JSON followed by \n
Special request: {"action": "ping"} → responds {"status": "ready"}
Special request: {"action": "quit"} → process exits cleanly
Why a persistent process? Loading the T5 model takes 5-10 seconds. We load once and keep the process alive, so subsequent simplifications are instant.
File: simplify_server.py
#!/usr/bin/env python3
"""JSON stdin/stdout server for Electron bridge."""
import sys
import json
# Reuse existing imports from simplify.py
from simplify import process_text, _select_model, _load_model
from utils import compute_metrics
def handle_request(request):
action = request.get("action", "simplify")
if action == "ping":
return {"status": "ready"}
if action == "quit":
sys.exit(0)
if action == "simplify":
text = request.get("text", "")
mode = request.get("mode", "dyslexia")
model_choice = request.get("model", "small")
if not text.strip():
return {"error": "No text provided", "result": "", "metrics": None}
try:
model_name = _select_model(model_choice, text)
result = process_text(text, mode, model_name)
metrics = compute_metrics(text, result)
return {"result": result, "metrics": metrics, "error": None}
except Exception as e:
return {"error": str(e), "result": "", "metrics": None}
return {"error": f"Unknown action: {action}"}
def main():
# Pre-load model on startup
_load_model("./t5-simplifier")
# Signal ready
print(json.dumps({"status": "ready"}), flush=True)
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
response = handle_request(request)
except json.JSONDecodeError:
response = {"error": "Invalid JSON"}
print(json.dumps(response), flush=True)
if __name__ == "__main__":
main()
# Terminal test (type JSON, get JSON back):
echo '{"action":"ping"}' | python simplify_server.py
echo '{"text":"The treaty was signed in 1648 and ended the war.","mode":"dyslexia"}' | python simplify_server.py
mkdir electron-app
cd electron-app
npm init -y
npm install electron --save-dev
npm install react react-dom
npm install -D vite @vitejs/plugin-react
npm install -D tailwindcss @tailwindcss/vite
npm install -D concurrently
electron/main.jsThe main process:
Creates a BrowserWindow
Spawns the Python process via python-bridge.js
Sets up IPC handlers so the renderer can send simplify requests
Loads the React app from Vite’s dev server (dev) or built files (prod)
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const { PythonBridge } = require('./python-bridge');
let mainWindow;
let pythonBridge;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
// Dev: load from Vite server. Prod: load built files.
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
}
app.whenReady().then(async () => {
// Start Python backend
pythonBridge = new PythonBridge();
await pythonBridge.start();
createWindow();
// IPC: renderer asks to simplify text
ipcMain.handle('simplify', async (event, { text, mode, model }) => {
return pythonBridge.send({ action: 'simplify', text, mode, model });
});
// IPC: renderer asks for ping/health check
ipcMain.handle('ping', async () => {
return pythonBridge.send({ action: 'ping' });
});
});
app.on('window-all-closed', () => {
pythonBridge?.stop();
app.quit();
});
electron/python-bridge.jsManages the Python subprocess lifecycle:
const { spawn } = require('child_process');
const path = require('path');
class PythonBridge {
constructor() {
this.process = null;
this.pending = new Map(); // id -> {resolve, reject}
this.requestId = 0;
this.buffer = '';
}
start() {
return new Promise((resolve, reject) => {
// Path to Python backend (parent of electron-app/)
const backendDir = path.join(__dirname, '..', '..');
const pythonScript = path.join(backendDir, 'simplify_server.py');
this.process = spawn('python', [pythonScript], {
cwd: backendDir,
stdio: ['pipe', 'pipe', 'pipe'],
});
// Wait for the "ready" signal
const onFirstLine = (data) => {
const line = data.toString().trim();
try {
const msg = JSON.parse(line);
if (msg.status === 'ready') {
// Now set up the normal data handler
this.process.stdout.removeListener('data', onFirstLine);
this.process.stdout.on('data', (d) => this._onData(d));
resolve();
}
} catch (e) {
// Not JSON yet, ignore (could be model loading logs)
}
};
this.process.stdout.on('data', onFirstLine);
this.process.stderr.on('data', (data) => {
// Suppress Python warnings/loading messages
// Could log to file if needed for debugging
});
this.process.on('error', reject);
// Timeout after 60s (model loading can take a while first time)
setTimeout(() => reject(new Error('Python startup timeout')), 60000);
});
}
send(request) {
return new Promise((resolve, reject) => {
const id = ++this.requestId;
this.pending.set(id, { resolve, reject });
const payload = JSON.stringify({ ...request, _id: id }) + '\n';
this.process.stdin.write(payload);
// Timeout per request
setTimeout(() => {
if (this.pending.has(id)) {
this.pending.delete(id);
reject(new Error('Request timeout'));
}
}, 30000);
});
}
_onData(data) {
this.buffer += data.toString();
const lines = this.buffer.split('\n');
this.buffer = lines.pop(); // Keep incomplete line in buffer
for (const line of lines) {
if (!line.trim()) continue;
try {
const response = JSON.parse(line);
// Resolve the oldest pending request
const [firstId] = this.pending.keys();
if (firstId !== undefined) {
const { resolve } = this.pending.get(firstId);
this.pending.delete(firstId);
resolve(response);
}
} catch (e) {
// Non-JSON output, ignore
}
}
}
stop() {
if (this.process) {
try {
this.process.stdin.write(JSON.stringify({ action: 'quit' }) + '\n');
} catch (e) {}
setTimeout(() => this.process?.kill(), 2000);
}
}
}
module.exports = { PythonBridge };
electron/preload.jsSecure bridge between renderer (React) and main process:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
simplify: (text, mode, model) =>
ipcRenderer.invoke('simplify', { text, mode, model }),
ping: () =>
ipcRenderer.invoke('ping'),
});
This is where Abishek’s Google Stitch design comes in. The UI components below define the structure and logic — styling/layout will be adapted to match the Stitch design.
┌─────────────────────────────────────────────────────────┐
│ Toolbar: [Mode: Dyslexia ▾] [Simplify ▶] [⚙ Settings] │
├────────────────────────────┬────────────────────────────┤
│ │ │
│ INPUT PANEL │ OUTPUT PANEL │
│ │ │
│ (Textarea or rich editor) │ (Formatted result) │
│ │ │
│ Type or paste your │ Simplified text appears │
│ complex text here... │ here after clicking │
│ │ "Simplify" │
│ │ │
├────────────────────────────┴────────────────────────────┤
│ METRICS BAR │
│ Words: 45 → 28 (-38%) | Sentences: 3 → 5 | FRE: +17 │
└─────────────────────────────────────────────────────────┘
App.jsx — Root. Manages state: inputText, outputText, mode, metrics, isLoading, settings (theme, font).
Toolbar.jsx — Mode selector dropdown, “Simplify” button, settings gear icon. The “Simplify” button calls window.api.simplify(text, mode, model).
Editor.jsx — Left panel. A <textarea> (or contenteditable div) for input. Could later be upgraded to Lexical editor. Shows placeholder text.
OutputPanel.jsx — Right panel. Displays the simplified result. Applies:
OpenDyslexic font (when dyslexia mode)
Markdown rendering for bold (ADHD mode)
Larger line spacing
High-contrast colors if enabled
MetricsPanel.jsx — Bottom bar. Shows word count change, sentence length change, Flesch score change. Color-coded: green = improvement, red = worse.
SettingsPanel.jsx — Slide-out or modal. Controls:
Theme: Light / Dark / High Contrast
Font: System / OpenDyslexic
Font size slider
(These match what was shown in the Phase 1 results slide)
User types text in Editor
↓
User selects mode (Dyslexia/ADHD/Autism)
↓
User clicks "Simplify"
↓
App.jsx sets isLoading = true
↓
window.api.simplify(text, mode, "small")
↓ (IPC to main process → stdin to Python → stdout back)
Response: { result, metrics, error }
↓
App.jsx sets outputText, metrics, isLoading = false
↓
OutputPanel renders the result with mode-appropriate formatting
MetricsPanel shows the before/after numbers
These are the specific features shown in the Phase 1 presentation that make this more than just “a text simplifier.”
Download OpenDyslexic-Regular.otf (free, open source)
@font-face declaration in CSS
Toggle between system font and OpenDyslexic in settings
When dyslexia mode is active, auto-switch to OpenDyslexic
Three themes:
Light (default):
Background: #FFFFFF, Text: #1A1A1A
Links/accents: #2563EB
Dark:
Background: #1A1A2E, Text: #E4E4E4
Links/accents: #60A5FA
High Contrast (WCAG AAA — 7:1 ratio minimum):
Background: #000000, Text: #FFFFFF
Links/accents: #FFFF00
Borders: #FFFFFF 2px solid
This is what was shown in the Phase 1 results slide (“High Contrast View WCAG AAA”)
From the Phase 1 results slide — highlights the current sentence/paragraph being read:
One sentence highlighted at a time
Rest of text dimmed (opacity: 0.4)
User clicks or arrow-keys to advance
Useful for ADHD mode
Slider or +/- buttons
Range: 14px to 28px
Persisted in localStorage
When Python is processing, show a spinner or skeleton UI
Message: “Simplifying… (first run loads the AI model, ~10 seconds)”
Python process crashes → auto-restart, show “Reconnecting…” message
Empty input → disable Simplify button
Very long input → show character count warning
{
"scripts": {
"dev": "concurrently \"vite\" \"electron .\"",
"build": "vite build && electron-builder",
"start": "electron ."
}
}
Take screenshots of:
Main view — input + output side by side with metrics
High Contrast mode — matching Phase 1 slide
Cognitive Focus mode — matching Phase 1 slide
OpenDyslexic font — showing the font change
All three modes — dyslexia/ADHD/autism output comparison
docs/design.mdAdd Electron architecture section
Document the Python bridge protocol
Update “CLI-first, UI later” → “Desktop app with Python backend”
docs/paper.texAdd section on the desktop application
Include architecture diagram
Add screenshots of the UI
Update results section with new screenshots
project_explanation.mdReflect the full desktop app
Update project structure diagram
Update the “How to Use It” section
docs/design.md T5 conditioning notesummarize: to simplify: since we now use the fine-tuned model| Risk | Likelihood | Mitigation |
|------|-----------|------------|
| Python path issues on different machines | Medium | Use python3 fallback, allow config in settings |
| Model loading too slow (10s+ wait) | Medium | Show loading screen, pre-warm on app start |
| Electron + Vite setup issues | Low | Well-documented stack, many examples |
| Google Stitch design → code mismatch | Medium | Stitch gives HTML/CSS, adapt to React components |
| 2-week deadline tight | Medium | Prioritize working demo over polish. Core: input→simplify→output. Everything else is bonus. |
If by day 10 things are behind schedule, the absolute minimum for a results slide is:
✅ Electron window opens
✅ Text input area works
✅ Mode selector works
✅ “Simplify” button calls Python and shows result
✅ Metrics display
✅ One accessibility feature (OpenDyslexic font OR high contrast)
This is enough for a convincing results screenshot. Focus mode and settings panel are bonus.
simplify_server.py — Python JSON bridge
electron/main.js, preload.js, python-bridge.js — Electron core
src/App.jsx — React shell with state management
src/components/Editor.jsx — Input panel
src/components/OutputPanel.jsx — Output panel
src/components/ModeSelector.jsx — Mode toggle
src/components/Toolbar.jsx — Top bar
src/components/MetricsPanel.jsx — Stats bar
CSS themes (light/dark/high-contrast)
OpenDyslexic font integration
src/components/SettingsPanel.jsx — Settings
Cognitive Focus Mode (if time)
Abishek handles: UI design via Google Stitch → provides the visual layout/CSS that gets integrated into the React components above.# Electron Desktop App — Complete Implementation Plan
Disability-Aware, Privacy-First, Local AI Chat Editor
Electron + React desktop app
Transformers.js with quantized DistilBERT models
Meta’s Lexical editor for rich text
Tailwind CSS + Shadcn/UI for styling
OpenDyslexic font support
High-contrast mode (WCAG AAA)
Cognitive Focus Mode
Zero-network protocol — no HTTP requests ever
4 phases: Foundation → AI Logic → Accessibility Modules → Testing & Docs
Python CLI tool (simplify.py + mode modules)
Fine-tuned T5-small model (./t5-simplifier/, ~242MB)
3 working modes: dyslexia, ADHD, autism
Readability metrics (Flesch, word count, sentence length)
No UI at all
Build the Electron + React frontend that was promised, but instead of re-implementing the AI in JavaScript, call the existing Python backend as a subprocess. This is architecturally honest — the “Local Backend (The Brain)” from the presentation runs as a Python process managed by Electron’s main process.
2 weeks. Need a working demo + results slide for Phase 2 presentation.
┌──────────────────────────────────────────────────────┐
│ Electron App │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ React Frontend (Renderer) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │ │
│ │ │ Editor │ │ Mode │ │ Output Panel │ │ │
│ │ │ (input) │ │ Selector │ │ (result) │ │ │
│ │ └──────────┘ └──────────┘ └────────────────┘ │ │
│ │ ┌──────────────────────┐ ┌────────────────┐ │ │
│ │ │ Metrics Display │ │ Settings │ │ │
│ │ └──────────────────────┘ └────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ IPC │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Electron Main Process │ │
│ │ │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ Python Bridge (child_process.spawn) │ │ │
│ │ │ Calls: python simplify_server.py │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ stdin/stdout (JSON) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Python Backend │ │
│ │ │ │
│ │ simplify_server.py (JSON stdin/stdout mode) │ │
│ │ ├── simplify.py (core logic) │ │
│ │ ├── dyslexia_mode.py │ │
│ │ ├── adhd_mode.py │ │
│ │ ├── autism_mode.py │ │
│ │ ├── utils.py │ │
│ │ └── t5-simplifier/ (fine-tuned model) │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
Electron main process spawns python simplify_server.py
Frontend sends JSON requests via IPC → main process → stdin to Python
Python processes and writes JSON response to stdout
Main process reads stdout → sends result via IPC → frontend updates
chat editor ( mini project )/
├── electron-app/ # NEW — Electron desktop app
│ ├── package.json
│ ├── electron/
│ │ ├── main.js # Electron main process
│ │ ├── preload.js # Secure bridge (contextBridge)
│ │ └── python-bridge.js # Spawns & manages Python process
│ ├── src/
│ │ ├── App.jsx # Root React component
│ │ ├── App.css # Global styles
│ │ ├── index.jsx # React entry point
│ │ ├── index.html # HTML shell
│ │ ├── components/
│ │ │ ├── Editor.jsx # Text input area
│ │ │ ├── OutputPanel.jsx # Simplified text display
│ │ │ ├── ModeSelector.jsx # Dyslexia/ADHD/Autism toggle
│ │ │ ├── MetricsPanel.jsx # Before/after readability stats
│ │ │ ├── SettingsPanel.jsx # Font, theme, contrast settings
│ │ │ └── Toolbar.jsx # Top action bar
│ │ ├── styles/
│ │ │ ├── themes.css # Light/dark/high-contrast themes
│ │ │ └── fonts.css # OpenDyslexic font-face
│ │ └── assets/
│ │ └── fonts/
│ │ └── OpenDyslexic-Regular.otf
│ ├── tailwind.config.js
│ ├── postcss.config.js
│ └── vite.config.js # Bundler config
│
├── simplify.py # Existing — CLI tool (unchanged)
├── simplify_server.py # NEW — JSON stdin/stdout wrapper
├── dyslexia_mode.py # Existing
├── adhd_mode.py # Existing
├── autism_mode.py # Existing
├── utils.py # Existing
├── t5-simplifier/ # Existing — fine-tuned model
├── requirements.txt # Existing
└── docs/ # Existing
simplify_server.pyThe existing simplify.py uses argparse and reads from files. The Electron app needs to send text directly (not via files). We create a thin JSON wrapper that:
Reads JSON from stdin: {"text": "...", "mode": "dyslexia", "model": "small"}
Calls the existing process_text() and compute_metrics() functions
Writes JSON to stdout: {"result": "...", "metrics": {...}, "error": null}
Stays alive for multiple requests (persistent process, not one-shot)
Protocol:
Each request is one line of JSON followed by \n
Each response is one line of JSON followed by \n
Special request: {"action": "ping"} → responds {"status": "ready"}
Special request: {"action": "quit"} → process exits cleanly
Why a persistent process? Loading the T5 model takes 5-10 seconds. We load once and keep the process alive, so subsequent simplifications are instant.
File: simplify_server.py
#!/usr/bin/env python3
"""JSON stdin/stdout server for Electron bridge."""
import sys
import json
# Reuse existing imports from simplify.py
from simplify import process_text, _select_model, _load_model
from utils import compute_metrics
def handle_request(request):
action = request.get("action", "simplify")
if action == "ping":
return {"status": "ready"}
if action == "quit":
sys.exit(0)
if action == "simplify":
text = request.get("text", "")
mode = request.get("mode", "dyslexia")
model_choice = request.get("model", "small")
if not text.strip():
return {"error": "No text provided", "result": "", "metrics": None}
try:
model_name = _select_model(model_choice, text)
result = process_text(text, mode, model_name)
metrics = compute_metrics(text, result)
return {"result": result, "metrics": metrics, "error": None}
except Exception as e:
return {"error": str(e), "result": "", "metrics": None}
return {"error": f"Unknown action: {action}"}
def main():
# Pre-load model on startup
_load_model("./t5-simplifier")
# Signal ready
print(json.dumps({"status": "ready"}), flush=True)
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
response = handle_request(request)
except json.JSONDecodeError:
response = {"error": "Invalid JSON"}
print(json.dumps(response), flush=True)
if __name__ == "__main__":
main()
# Terminal test (type JSON, get JSON back):
echo '{"action":"ping"}' | python simplify_server.py
echo '{"text":"The treaty was signed in 1648 and ended the war.","mode":"dyslexia"}' | python simplify_server.py
mkdir electron-app
cd electron-app
npm init -y
npm install electron --save-dev
npm install react react-dom
npm install -D vite @vitejs/plugin-react
npm install -D tailwindcss @tailwindcss/vite
npm install -D concurrently
electron/main.jsThe main process:
Creates a BrowserWindow
Spawns the Python process via python-bridge.js
Sets up IPC handlers so the renderer can send simplify requests
Loads the React app from Vite’s dev server (dev) or built files (prod)
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const { PythonBridge } = require('./python-bridge');
let mainWindow;
let pythonBridge;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
// Dev: load from Vite server. Prod: load built files.
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
}
app.whenReady().then(async () => {
// Start Python backend
pythonBridge = new PythonBridge();
await pythonBridge.start();
createWindow();
// IPC: renderer asks to simplify text
ipcMain.handle('simplify', async (event, { text, mode, model }) => {
return pythonBridge.send({ action: 'simplify', text, mode, model });
});
// IPC: renderer asks for ping/health check
ipcMain.handle('ping', async () => {
return pythonBridge.send({ action: 'ping' });
});
});
app.on('window-all-closed', () => {
pythonBridge?.stop();
app.quit();
});
electron/python-bridge.jsManages the Python subprocess lifecycle:
const { spawn } = require('child_process');
const path = require('path');
class PythonBridge {
constructor() {
this.process = null;
this.pending = new Map(); // id -> {resolve, reject}
this.requestId = 0;
this.buffer = '';
}
start() {
return new Promise((resolve, reject) => {
// Path to Python backend (parent of electron-app/)
const backendDir = path.join(__dirname, '..', '..');
const pythonScript = path.join(backendDir, 'simplify_server.py');
this.process = spawn('python', [pythonScript], {
cwd: backendDir,
stdio: ['pipe', 'pipe', 'pipe'],
});
// Wait for the "ready" signal
const onFirstLine = (data) => {
const line = data.toString().trim();
try {
const msg = JSON.parse(line);
if (msg.status === 'ready') {
// Now set up the normal data handler
this.process.stdout.removeListener('data', onFirstLine);
this.process.stdout.on('data', (d) => this._onData(d));
resolve();
}
} catch (e) {
// Not JSON yet, ignore (could be model loading logs)
}
};
this.process.stdout.on('data', onFirstLine);
this.process.stderr.on('data', (data) => {
// Suppress Python warnings/loading messages
// Could log to file if needed for debugging
});
this.process.on('error', reject);
// Timeout after 60s (model loading can take a while first time)
setTimeout(() => reject(new Error('Python startup timeout')), 60000);
});
}
send(request) {
return new Promise((resolve, reject) => {
const id = ++this.requestId;
this.pending.set(id, { resolve, reject });
const payload = JSON.stringify({ ...request, _id: id }) + '\n';
this.process.stdin.write(payload);
// Timeout per request
setTimeout(() => {
if (this.pending.has(id)) {
this.pending.delete(id);
reject(new Error('Request timeout'));
}
}, 30000);
});
}
_onData(data) {
this.buffer += data.toString();
const lines = this.buffer.split('\n');
this.buffer = lines.pop(); // Keep incomplete line in buffer
for (const line of lines) {
if (!line.trim()) continue;
try {
const response = JSON.parse(line);
// Resolve the oldest pending request
const [firstId] = this.pending.keys();
if (firstId !== undefined) {
const { resolve } = this.pending.get(firstId);
this.pending.delete(firstId);
resolve(response);
}
} catch (e) {
// Non-JSON output, ignore
}
}
}
stop() {
if (this.process) {
try {
this.process.stdin.write(JSON.stringify({ action: 'quit' }) + '\n');
} catch (e) {}
setTimeout(() => this.process?.kill(), 2000);
}
}
}
module.exports = { PythonBridge };
electron/preload.jsSecure bridge between renderer (React) and main process:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
simplify: (text, mode, model) =>
ipcRenderer.invoke('simplify', { text, mode, model }),
ping: () =>
ipcRenderer.invoke('ping'),
});
This is where Abishek’s Google Stitch design comes in. The UI components below define the structure and logic — styling/layout will be adapted to match the Stitch design.
┌─────────────────────────────────────────────────────────┐
│ Toolbar: [Mode: Dyslexia ▾] [Simplify ▶] [⚙ Settings] │
├────────────────────────────┬────────────────────────────┤
│ │ │
│ INPUT PANEL │ OUTPUT PANEL │
│ │ │
│ (Textarea or rich editor) │ (Formatted result) │
│ │ │
│ Type or paste your │ Simplified text appears │
│ complex text here... │ here after clicking │
│ │ "Simplify" │
│ │ │
├────────────────────────────┴────────────────────────────┤
│ METRICS BAR │
│ Words: 45 → 28 (-38%) | Sentences: 3 → 5 | FRE: +17 │
└─────────────────────────────────────────────────────────┘
App.jsx — Root. Manages state: inputText, outputText, mode, metrics, isLoading, settings (theme, font).
Toolbar.jsx — Mode selector dropdown, “Simplify” button, settings gear icon. The “Simplify” button calls window.api.simplify(text, mode, model).
Editor.jsx — Left panel. A <textarea> (or contenteditable div) for input. Could later be upgraded to Lexical editor. Shows placeholder text.
OutputPanel.jsx — Right panel. Displays the simplified result. Applies:
OpenDyslexic font (when dyslexia mode)
Markdown rendering for bold (ADHD mode)
Larger line spacing
High-contrast colors if enabled
MetricsPanel.jsx — Bottom bar. Shows word count change, sentence length change, Flesch score change. Color-coded: green = improvement, red = worse.
SettingsPanel.jsx — Slide-out or modal. Controls:
Theme: Light / Dark / High Contrast
Font: System / OpenDyslexic
Font size slider
(These match what was shown in the Phase 1 results slide)
User types text in Editor
↓
User selects mode (Dyslexia/ADHD/Autism)
↓
User clicks "Simplify"
↓
App.jsx sets isLoading = true
↓
window.api.simplify(text, mode, "small")
↓ (IPC to main process → stdin to Python → stdout back)
Response: { result, metrics, error }
↓
App.jsx sets outputText, metrics, isLoading = false
↓
OutputPanel renders the result with mode-appropriate formatting
MetricsPanel shows the before/after numbers
These are the specific features shown in the Phase 1 presentation that make this more than just “a text simplifier.”
Download OpenDyslexic-Regular.otf (free, open source)
@font-face declaration in CSS
Toggle between system font and OpenDyslexic in settings
When dyslexia mode is active, auto-switch to OpenDyslexic
Three themes:
Light (default):
Background: #FFFFFF, Text: #1A1A1A
Links/accents: #2563EB
Dark:
Background: #1A1A2E, Text: #E4E4E4
Links/accents: #60A5FA
High Contrast (WCAG AAA — 7:1 ratio minimum):
Background: #000000, Text: #FFFFFF
Links/accents: #FFFF00
Borders: #FFFFFF 2px solid
This is what was shown in the Phase 1 results slide (“High Contrast View WCAG AAA”)
From the Phase 1 results slide — highlights the current sentence/paragraph being read:
One sentence highlighted at a time
Rest of text dimmed (opacity: 0.4)
User clicks or arrow-keys to advance
Useful for ADHD mode
Slider or +/- buttons
Range: 14px to 28px
Persisted in localStorage
When Python is processing, show a spinner or skeleton UI
Message: “Simplifying… (first run loads the AI model, ~10 seconds)”
Python process crashes → auto-restart, show “Reconnecting…” message
Empty input → disable Simplify button
Very long input → show character count warning
{
"scripts": {
"dev": "concurrently \"vite\" \"electron .\"",
"build": "vite build && electron-builder",
"start": "electron ."
}
}
Take screenshots of:
Main view — input + output side by side with metrics
High Contrast mode — matching Phase 1 slide
Cognitive Focus mode — matching Phase 1 slide
OpenDyslexic font — showing the font change
All three modes — dyslexia/ADHD/autism output comparison
docs/design.mdAdd Electron architecture section
Document the Python bridge protocol
Update “CLI-first, UI later” → “Desktop app with Python backend”
docs/paper.texAdd section on the desktop application
Include architecture diagram
Add screenshots of the UI
Update results section with new screenshots
project_explanation.mdReflect the full desktop app
Update project structure diagram
Update the “How to Use It” section
docs/design.md T5 conditioning notesummarize: to simplify: since we now use the fine-tuned model| Risk | Likelihood | Mitigation |
|------|-----------|------------|
| Python path issues on different machines | Medium | Use python3 fallback, allow config in settings |
| Model loading too slow (10s+ wait) | Medium | Show loading screen, pre-warm on app start |
| Electron + Vite setup issues | Low | Well-documented stack, many examples |
| Google Stitch design → code mismatch | Medium | Stitch gives HTML/CSS, adapt to React components |
| 2-week deadline tight | Medium | Prioritize working demo over polish. Core: input→simplify→output. Everything else is bonus. |
If by day 10 things are behind schedule, the absolute minimum for a results slide is:
✅ Electron window opens
✅ Text input area works
✅ Mode selector works
✅ “Simplify” button calls Python and shows result
✅ Metrics display
✅ One accessibility feature (OpenDyslexic font OR high contrast)
This is enough for a convincing results screenshot. Focus mode and settings panel are bonus.
simplify_server.py — Python JSON bridge
electron/main.js, preload.js, python-bridge.js — Electron core
src/App.jsx — React shell with state management
src/components/Editor.jsx — Input panel
src/components/OutputPanel.jsx — Output panel
src/components/ModeSelector.jsx — Mode toggle
src/components/Toolbar.jsx — Top bar
src/components/MetricsPanel.jsx — Stats bar
CSS themes (light/dark/high-contrast)
OpenDyslexic font integration
src/components/SettingsPanel.jsx — Settings
Cognitive Focus Mode (if time)
Abishek handles: UI design via Google Stitch → provides the visual layout/CSS that gets integrated into the React components above.