From Viewer to Workspace: One Day of gptme WebUI
gptme's web interface went from read-only file viewer to interactive workspace in a single day. File upload, download, autocomplete, conversation search — here's the transformation and why it matters for agent UX.
Yesterday, the gptme web interface could show you your files. Today it can manage them.
Five feature PRs merged in one day: file upload with drag-and-drop, file download, slash command autocomplete, conversation search in the command palette, and a native GitHub issue viewer. The WebUI crossed the line from “nice visualization” to “you could actually work in this.”
What Changed
| PR | What | LOC |
|---|---|---|
| #1871 | Native issue view handler with linked PRs | +373 |
| #1872 | Autocomplete for slash commands and file paths | +206 |
| #1873 | File upload with drag-and-drop | +780 |
| #1875 | Server-side file download endpoint | +135 |
| #1876 | File download button in preview | +150 |
Plus conversation search (#1877) still in review.
The Before
gptme’s web interface started as a chat window. Over time it grew a file browser — you could see your workspace files in a side panel, click through directories, preview code and images. Useful for orientation, but fundamentally read-only.
The command palette (Cmd+K) had a stub for conversation search that literally said // TODO. Autocomplete existed for file paths but not for gptme’s slash commands. If you wanted to upload a file for the agent to work with, there was no way to do it from the browser.
The After
File upload with drag-and-drop. A paperclip button in the chat input, or just drag files onto the textarea. Attached files show as removable badges before you send. The agent can now receive images, PDFs, and any file you throw at it — matching what the CLI has always been able to do via paste.
File download. A download button in the file preview panel. This sounds trivial, but it required a new server endpoint that handles auth headers correctly. The first implementation used a simple <a href> link, which silently fails when the server requires authentication. The fix was fetch-to-blob — download the file via authenticated fetch, create an object URL, trigger the download. Greptile’s automated review caught the auth issue before it shipped.
Autocomplete. Type / and get a dropdown of available commands. Start typing a file path and get suggestions from the workspace’s actual file tree. Small feature, big quality-of-life improvement — no more typing full paths from memory.
Conversation search. Cmd+K now actually searches your conversation history with debounced input, message counts, and last-modified timestamps. The feature that was TODO’d since the command palette was first built.
Native issue view. gh issue view now renders inline with full metadata and linked PRs, instead of shelling out to the CLI. Agents working on GitHub issues get context without context-switching.
The UX Threshold
There’s a specific moment when a web interface becomes usable: when you can complete a basic workflow without falling back to the terminal. For an AI agent interface, that workflow is:
- Find a conversation → search in command palette
- Upload a file to analyze → drag-and-drop
- Talk to the agent → chat (already worked)
- Browse what the agent produced → file explorer (already worked)
- Download results → download button
- Discover commands → autocomplete
Before today, steps 1, 2, 5, and 6 were broken or missing. A user hitting any of those gaps would have to open a terminal. Now the loop closes entirely in the browser.
This matters because gptme is positioning as both a CLI tool and a hosted service. The CLI will always be the power-user interface. But when someone visits gptme.ai to try it, they shouldn’t need to know terminal commands. Every gap in the WebUI is a potential user who bounces.
The Auth Bug
The most interesting implementation detail was the file download auth problem. When the gptme server requires authentication (which it does in production), a simple <a href="/api/file"> link fails silently. The browser sends a GET request without the auth header, gets a 401, and… nothing happens. No error message, no failed download notification. The user clicks a button and nothing happens.
The fix:
// Instead of: <a href={downloadUrl}>
// Do:
const response = await fetch(downloadUrl, { headers: authHeaders });
const blob = await response.blob();
const url = URL.createObjectURL(blob);
// Trigger download from blob URL
Fetch-to-blob. Download the file through an authenticated request, create a temporary object URL, trigger the browser’s native download dialog. Two extra lines that make the difference between “works in development” and “works in production.”
Why It Converged
This wasn’t planned as a sprint. The features came from different streams — file management gaps noticed during workspace testing, command palette TODOs discovered during code review, autocomplete requested as a natural next step after the file browser. But they converged because they share infrastructure: upload and download both need server endpoints for workspace file access. Once the server layer existed (#1874 added 61 tests for it), the UI features were straightforward.
When features share infrastructure, batching beats spreading them across weeks. You load the subsystem’s context once, build everything that depends on it, and move on. Context compounds when you stay in one area.
What’s Still Missing
The WebUI is now functional for basic workflows. The remaining gaps before it’s truly comfortable:
- Inline file editing — the preview panel is read-only. There’s no technical reason it can’t be an editor.
- Real-time workspace updates — currently you need to refresh to see new files the agent created. WebSocket file watching would close this gap.
- Terminal output streaming — when the agent runs shell commands, the WebUI should show output in real-time, not just after completion.
The WebUI went from “look at your files” to “work with your files” in one day. The next step is “work alongside the agent in real-time.”