The app shell (src/layout/) is a sidebar plus a main view area. The sidebar lists tables and charts and has buttons to switch between Canvas, Dashboard, and Report views. The main area swaps between five views: canvas, grid, chart, dashboard, and report. Double-clicking a table node opens the grid; clicking a chart node opens the chart editor.
Contextual tours introduce the canvas, grid, and report workflows the first time each view becomes relevant. Tours anchor to live controls, pause when a required target is unavailable, persist completion locally, and can be restarted from the interface. Definitions and persistence live under src/discovery/; the browser contract is covered by e2e/discovery-tours.spec.ts.
A node-based editor (ReactFlow) where you build pipelines by connecting tables.
- Source tables: created by importing a CSV/Excel file or via "New Table". Show file name, row count, and column count. Support in-place cell editing; edits are stored as patches, separate from the original data.
- Derived tables: created by connecting nodes and choosing a transform. Show the transform type and source. Recompute automatically when upstream data changes. Read-only.
- Charts: visualizations bound to a source table.
| Action | Behavior |
|---|---|
| Drag node | Move it; edges follow |
| Double-click table | Open grid view |
| Click chart | Open chart editor |
| Connect nodes | Open the transform modal |
| Delete / Backspace | Remove selected node |
| Cmd/Ctrl+Z | Undo |
| Cmd/Ctrl+Shift+Z | Redo |
Uses Dagre to lay out nodes by depth in the dependency graph, either left-to-right or top-to-bottom.
Connecting nodes is blocked if it would create a cycle; a warning toast explains why.
A virtualized spreadsheet for viewing and editing table data.
- Virtual scrolling (only visible rows are rendered), so large tables stay responsive
- Column resize, cell and range selection, copy (Cmd/Ctrl+C), drag-to-autofill
- Column filtering panel
- Editing (source tables only): double-click a cell, Enter to confirm, Escape to cancel, Tab to move on. Changes are stored as patches.
Add computed columns with a spreadsheet-like syntax:
=Column1 * 0.1
=IF(Status = "Active", Price, 0)
=CONCAT(FirstName, " ", LastName)
Functions are grouped by category (src/formula/):
| Category | Functions |
|---|---|
| Math | SUM, AVG, MIN, MAX, COUNT, ROUND, ABS, FLOOR, CEIL, POWER, SQRT, MOD, NUMBER |
| Text | CONCAT, UPPER, LOWER, TRIM, LEFT, RIGHT, MID, LEN, FIND, REPLACE, SUBSTITUTE, TEXT |
| Logic | IF, AND, OR, NOT, ISNULL, IFNULL, COALESCE, BOOLEAN |
| Date | NOW, TODAY, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, DATE, DATEDIFF |
Operators: +, -, *, /, %, ^, =, <>, >, <, >=, <=, AND, OR.
The transform modal exposes six data transforms:
- Filter: keep rows matching conditions, combined with AND or OR. Operators include equals, not equals, contains / not contains, starts/ends with, greater/less than, greater/less-or-equal, between, is null / is not null.
- Group & Summarize: group by columns and aggregate with SUM, AVG, MIN, MAX, COUNT, or COUNT DISTINCT.
- Join: combine two tables on key columns. Inner, left, right, or full. You can pick which columns to keep and how to disambiguate names.
- Select: project a subset of columns and/or rename them.
- Calculated column: add a column from a formula expression (same engine as formula columns).
- Union: stack rows from multiple tables.
The edge type also includes an internal reference relationship used to bind
charts to tables; it is not offered as a data-transform choice. There is no
pivot or standalone sort transform.
Created from the suggestions panel or by adding a chart node bound to a table. Types: bar, line, pie, scatter. Configure the X/Y columns, aggregation, and optional grouping. Charts update when their source data changes.
Analyzes table profiles and recommends transforms, charts, and cleaning actions (src/suggestions/).
Categories
- Analysis: trend charts (date + numeric), category breakdowns, distributions, top-N.
- Cleaning: type mismatches, whitespace, casing inconsistencies, outliers.
- Recipes: guided multi-step flows like time-series aggregation or variance analysis.
How it works
- Profile the table (column stats + semantic hints).
- Classify each column (continuous numeric, categorical, date, id-like, etc.).
- Match rules; each rule has a
whenpredicate. - Score matches by confidence/relevance.
- Show the top suggestions; applying one runs the corresponding command.
A read-only "Project Overview" (src/dashboard/) that summarizes the whole project:
- Header stats: total tables, rows, columns, and overall data completeness.
- Lineage mini-map: a compact view of the node graph; click a node to jump to it.
- Table stats: per-table row/column counts and data-quality metrics from the profiler.
- Quick actions: the top suggestions, applied directly from the dashboard.
Empty until you import data.
Notion-style rich-text documents (TipTap) that embed live tables and charts (src/report/).
- Editing: headings, bold/italic/underline, lists, code blocks, quotes, callouts, toggles, horizontal rules, plus a slash (
/) command menu for inserting blocks. - Embedded blocks: embedded table (references a project table, stays in sync), inline/editable table (static, manually entered), and chart.
- Export: reports are written as HTML and PDF into the project ZIP. "Export as PDF" uses pdfmake to generate and download a paged document directly, so text, tables, and charts stay vector and selectable.
- Local (IndexedDB): projects auto-save their graph (nodes/edges/patches), imported files, reports, and durable sync queues. Materialized DuckDB results and dirty/compute flags remain per-tab runtime state and are rebuilt when needed. Records are keyed by storage scope (
guest:<id>oraccount:<userId>). - Server sync: when connected to the backend, the project graph (nodes, edges, patches) and its reports sync to MongoDB on save. Files are stored in GridFS and fetched on load.
- Concurrency: one tab writes a project at a time and other tabs mirror it live in read-only mode until the owner closes or releases the document. Focus does not transfer ownership. Conflicting saves from another device merge on the client (see Reliability).
- Cross-tab auth: account cookies are origin-shared for new/reloaded tabs; guest choice remains tab-local. Explicit sign-out revokes the shared account session, while guest tabs remain isolated. Catalog and document updates use
BroadcastChannel(auth React state is not broadcast). See Cross-tab authentication and session.
- Project (
.tablecanvas.zip):project.tablecanvas.json(full state with base64-encoded source files),data.xlsx(every table as a sheet), andreports/*.htmlplusreports/*.pdf. Self-contained. - Data: all tables are included as sheets in
data.xlsxinside the project ZIP.
| Shortcut | Action |
|---|---|
| Cmd/Ctrl + Z | Undo |
| Cmd/Ctrl + Shift + Z | Redo |
| Delete / Backspace | Delete selected node |
| Escape | Cancel edit (grid) / close dialog |
| Tab | Next cell (grid) |
| Enter | Confirm edit / open cell |
| Cmd/Ctrl + C | Copy selected cells |