Live demo: Open One-Shot Table Parser
One-Shot Table Parser is a web application for extracting repeated industrial table documents with a reusable, human-verified HTML structure. Define the table topology once, then give the application another document with the same layout. A multimodal model fills the cells while the application rejects outputs that change the approved rows, columns, or merged-cell structure.
The template is deliberately content-free: it contains only <table>, <tr>, and empty <td> elements, plus rowspan and colspan where needed. This keeps structure definition separate from OCR content and makes templates easy to inspect, edit, and reuse.
General-purpose vision models often recognize text correctly but reconstruct complex tables inconsistently. They may add or remove cells, flatten merged regions, or return a different layout between documents. This project turns a verified table structure into an explicit constraint:
- Create or extract a blank HTML structure.
- Review and correct it once.
- Reuse it with images that follow the same layout.
- Accept model output only when its topology matches the template.
- Three template creation modes
- Edit blank table HTML directly.
- Initialize a row-by-column grid and merge rectangular selections visually.
- Extract an initial blank structure from a table image, then correct it in the same visual editor.
- Structure-only templates with no labels, values, or placeholders.
- Merged-cell support through validated
rowspanandcolspanvalues. - OpenAI-compatible vision APIs configured from the web interface or environment variables.
- Real vision connectivity test using an image request, not a text-only health check.
- Model safeguards including deterministic generation, a 4,096-token output limit, a 110-second timeout, and automatic disabling of Qwen thinking mode.
- Client-side image resizing for large inputs before multimodal inference.
- Strict output validation that rejects changed table topology and unsafe HTML.
- Persistent templates, model settings, parse history, and uploaded images using Cloudflare D1 and R2.
- Mock mode for exploring the workflow without a model API.
- English interface across templates, parsing, history, settings, prompts, and errors.
Choose one of three paths on the Templates page:
| Mode | Best for | Result |
|---|---|---|
| Edit HTML | Users who already know the exact topology | Validated blank table HTML |
| Visual Builder | Manual construction without editing source | Grid with visual merge and split operations |
| Start from Image | Fast initialization from a representative document | Model-generated blank structure for manual correction |
An accepted template looks like this:
<table>
<tr>
<td></td>
<td colspan="2"></td>
</tr>
<tr>
<td rowspan="2"></td>
<td></td>
<td></td>
</tr>
<tr>
<td colspan="2"></td>
</tr>
</table>All cells must be empty. Attributes other than rowspan and colspan are rejected.
On the Parse page, select a saved template and upload a document image with the same layout. The application sends the blank HTML and image to the configured vision model. The model fills each cell by spatial position without being allowed to alter the structure.
The returned HTML is checked against the template's topology. Successful and failed runs are recorded on the History page with the input image, result, raw model output, and error details where available.
Open Model Settings and enter:
- API Base URL — include the version path but omit
/chat/completions, for examplehttps://api.openai.com/v1. - Model Name — a multimodal model that accepts image input.
- API Key — sent only to the server; the browser receives a masked value after saving.
Click Test Vision Connection before saving. The endpoint must support:
POST /chat/completions- Bearer-token authorization
- OpenAI-compatible
image_urlmessage content
You may instead provide the same configuration through environment variables:
MODEL_API_BASE_URL=https://api.openai.com/v1
MODEL_NAME=your-vision-model
MODEL_API_KEY=your-api-keySaved database settings take precedence over environment variables. With neither configured, the application runs in Mock mode.
- Node.js 22.13 or newer
- npm
- Linux or WSL for the included build scripts (
flockand GNUtimeoutare used)
git clone https://github.com/lyc61c/OneShotTable.git
cd OneShotTable
npm ci
npm run db:migrate:local
npm run devOpen the local URL printed by Vite. Copy .env.example to .env.local only if you want environment-based model configuration.
The local migration command applies both checked-in D1 migrations to the same project-local state used by the development server. Local R2 storage is provided by the Cloudflare Vite plugin.
| Command | Purpose |
|---|---|
npm run dev |
Start the Vinext/Vite development server |
npm run db:migrate:local |
Apply checked-in migrations to local D1 state |
npm run lint |
Run ESLint |
npm run build |
Build and validate the deployable worker artifact |
npm test |
Build, validate, and run the rendered-page smoke test |
npm run validate:artifact |
Recheck an existing build artifact |
npm run db:generate |
Generate a new Drizzle migration after schema changes |
The application is designed for a Cloudflare Worker-compatible runtime with:
- a D1 binding named
DB - an R2 binding named
BUCKET - Node.js compatibility enabled
The repository includes .openai/hosting.json for ChatGPT Sites-compatible deployment and vite.config.ts for local D1/R2 simulation. For another Cloudflare deployment workflow, provision D1 and R2, map the bindings above, apply the SQL files in drizzle/, then build with npm run build.
Do not commit real API keys. Set secrets in the deployment environment or configure the model from the protected settings page after deployment.
| Layer | Implementation |
|---|---|
| UI | React 19, Next.js 16, Tailwind CSS 4, Lucide icons |
| Runtime/build | Vinext, Vite, Cloudflare Workers |
| Database | Cloudflare D1, Drizzle ORM |
| Object storage | Cloudflare R2 |
| Model integration | OpenAI-compatible multimodal Chat Completions API |
Key directories:
app/ Pages and API routes
components/ Template editor, parser workspace, history, and settings UI
db/ Drizzle schema and D1 access
drizzle/ Database migrations
lib/ Model client, prompts, image handling, and HTML validation
public/ Static assets
tests/ Rendered-worker smoke test
worker/ Cloudflare worker entry point
The server normalizes structure templates and permits only:
<table>,<tr>, and<td>- integer
rowspanandcolspanvalues from 1 to 100 - a rectangular, non-overlapping effective grid
Scripts, styles, event handlers, arbitrary attributes, text inside templates, inconsistent row widths, overlapping spans, and spans that extend past the final row are rejected. Parsed output must have the same ordered cell-span signature as the selected template.
The built-in model configuration page is intended for a trusted or access-controlled deployment. The API key is not returned to the browser after saving, but it is stored in D1 and the settings endpoints do not implement application-level user authorization. Do not expose an unrestricted deployment with a shared model key. Add authentication and per-user ownership before using this as a public multi-user service.
Image initialization is intentionally narrow:
You are a table structure extractor.
Generate a blank HTML table template from the uploaded image.
Accurately reconstruct rows, columns, and merged cells; remove all text;
use only table, tr, td, rowspan, and colspan; output only the table HTML.
Parsing uses the confirmed blank HTML as a hard structural reference and asks the model only to fill cell text from corresponding image positions.
