# Microtables – TODO ## 1. Keyboard Navigation & Table Bounds - [ ] **1.1 Decouple table size from keyboard movement** Currently `press-enter-in-cell` uses `highest-row`/`highest-col` to decide where to move, coupling navigation to data extent. Intended design: - `highest-row`/`highest-col` should include the cursor's current position - `press-enter-in-cell` should only increment the position - Add a dedicated event handler for Tab (native Tab is no longer sufficient) - [ ] **1.2 Add arrow-key navigation** Implement `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight` handlers that move the cursor between cells, firing from `on-keyDown` (not `on-keyPress`). Navigation logic should live alongside Enter/Tab handling (1.1). - [ ] **1.3 Determine initial table size and scroll behaviour** `maxrow = 20` and `maxcol = "G"` are hardcoded. Decide whether the grid grows as the cursor reaches the edge or stays fixed with a scroll container. A fixed viewport with overflow scroll is likely right for the quick-calculation use case. - [ ] **1.4 Movement within a range selection** When a range is selected, Enter/Tab/arrow keys should cycle within the selection rather than the full table. Depends on the selection model (see section 5). --- ## 2. Formula Evaluation Engine - [ ] **2.1 Support lowercase cell references** `parse-variables` already uppercases symbol names, but confirm end-to-end that typing `=a1 + b2` in a cell evaluates correctly. - [ ] **2.2 Clarify dirty-forward-reference error semantics** `gather-variables-and-evaluate-cell` returns `:error` when forward references are dirty, but the intended semantics vs. `:insufficient-data` are not fully specified. Clarify and document the distinction. - [ ] **2.3 Verify `evaluate-all` handles cycles correctly** `evaluate-all` calls `evaluate-from-cell` which runs `find-cycle`, so cycles should be caught at initialization. Confirm this is sufficient and clean up the open question. - [ ] **2.4 Handle multiple cells modified simultaneously** The model processes one cell change at a time. A batch-update path will be needed for paste operations or future server sync. - [ ] **2.5 Consider incremental diff for back-reference updates** `denotify-references` + `notify-references` wipe and re-add all back-references on every edit. Diffing old vs. new `:refs` and updating only the delta may help at scale. - [ ] **2.6 Consider memoizing `find-cycle`** Evaluate whether a dynamic memoization strategy with cache invalidation on data change is worth the complexity. --- ## 3. Tests - [x] **3.1 Unit tests for formula evaluation** Implemented in `frontend/test/microtables_frontend/evaluation_test.cljs` using `cljs.test` + shadow-cljs `:node-test` target. Covers: - Basic arithmetic formulas - Cell references and chained references - Range functions (`sum`, `average`, etc.) - Cycle detection (mutual cycles, self-reference, three-cell cycles) - Error propagation (`:calc-error`, `:cycle-error`) - Edge cases: empty cells in ranges, non-formula cells - Run with `npm test` in `frontend/` - [x] **3.2 Integration tests for state transitions** Implemented in `frontend/test/microtables_frontend/events_test.cljs`. Tests the pure function pipelines that event handlers compose (not through re-frame dispatch). Covers: - Editing a cell and verifying downstream re-evaluation - `change-datum-value` marks dirty without evaluating - Reference graph updates when a formula changes (old refs removed, new refs added) - Deeply nested dependency chain propagation - [ ] **3.3 End-to-end tests** Browser-level tests covering full user flows: - Data entry and formula results visible in cells - Keyboard navigation - Range selection operations (once implemented) - Touch interactions (once implemented) --- ## 4. Formula Autocomplete - [ ] **4.1 Show available math functions after `=` is typed** When a cell value begins with `=`, display a dropdown of available math.js functions. Filter the list as the user continues typing. Selecting an item inserts the function name with a placeholder for arguments. - [ ] **4.2 Cell and range reference autocomplete** As the user types a column letter that could start a cell reference (e.g. `A`), suggest cell addresses that contain data. For ranges, trigger suggestions when `:` follows a cell reference. - [ ] **4.3 Argument hint tooltip** Inside a function call, show a tooltip indicating expected arguments and their types (e.g. `sum(value1, value2, ...)`). --- ## 5. Selection & Highlight Mode - [ ] **5.1 Implement the selection data model** The `:selection` key in `:position` is always `nil`. Implement `{:start {:col "A" :row 1} :end {:col "C" :row 3}}` as the selection shape. Set on shift+arrow or click-drag; clear on unmodified navigation. - [ ] **5.2 Wire up highlight rendering for selected cells** `highlight-cells` in `subs.cljs` already sets `:view :highlighted` on cells within a selection range, but the `::table-data` subscription never calls it when `:selection` is non-nil. Connect the two so selected cells receive the `highlighted` CSS class. - [ ] **5.3 Aggregate display for a selection** When a numeric range is selected, display the sum and average in the UI (e.g. a status bar or floating chip). Offer to insert the result as a formula into a user-chosen cell. --- ## 6. Cell & Range Control Buttons When a cell or range is selected, show a compact set of floating action buttons positioned around the selection. Buttons must be large enough to tap comfortably on a phone screen. Implement in this order: - [ ] **6.1 Expand / contract selection** — drag handles or +/− buttons to resize the selection boundary - [ ] **6.2 Calculate** — show sum/average inline for numeric selections (ties into 5.3) - [ ] **6.3 Clear** — delete cell contents and re-evaluate dependents - [ ] **6.4 Copy** — copy selection to the clipboard - [ ] **6.5 Move** — designate a new top-left cell; handle destination conflicts - [ ] **6.6 Delete** — remove cells and displace neighbours inward - [ ] **6.7 Intelligent fill** — trigger pattern inference (see section 10) --- ## 7. Column & Row Aggregates - [x] **7.1 Always-visible column totals row** A pinned row below the table showing the sum (and possibly average) of each column, updating reactively. Show only for columns that contain numbers. Make sure that it is not visually confused for another cell/row. - [ ] **7.2 Always-visible row totals column** Equivalent pinned column to the right of the table for row aggregates. Perhaps, due to concerns about mobile screen real estate, the row totals could be toggleable (not "always-visible" after all) - maybe behind the row header. --- ## 8. UI & Control Panel - [ ] **8.2 Add a "Help" modal** A small modal (triggered from the control panel) listing and explaining: - Supported formula syntax - Available math functions - Keyboard shortcuts - How cell references and ranges work - [ ] **8.4 Replace the logo** The current `logo.svg` is a placeholder. Design and replace with a better mark. --- ## 13. Visual Design & Styling The goal is intentional minimalism — not bare HTML. The current stylesheet has good structural foundations but is visibly unfinished: placeholder colours throughout, a content-template base, and unstyled interactive elements. Items are ordered so that each one builds on the previous. ### Foundational - [ ] **13.1 Establish a CSS design token set** Define all colours, spacing, and radii as CSS custom properties on `:root` before touching anything else. Suggested palette: - `--color-bg: #f9fafb` (page background) - `--color-surface: #ffffff` (cell / panel background) - `--color-border: #e5e7eb` (grid lines, panel edges) - `--color-header-bg: #f3f4f6` (row/column headers) - `--color-text: #111827` (primary text) - `--color-text-muted: #6b7280` (header labels, hints) - `--color-accent: #3b82f6` (focus ring, selection border) - `--color-accent-subtle: #eff6ff` (selection fill, hover tint) - [ ] **13.2 Strip out content-template CSS** The stylesheet was adapted from a blog template. Remove or replace: - `max-width: 600px` on `body` — clips the table on most screens; a spreadsheet should use available width - `h1`, `h2`, `h3` type scale — not used in the app - `line-height: 1.5em` on `body` — intended for prose, not a grid - `a` tag styles (`text-decoration`, `color: #09f`) — not relevant to the app chrome - [ ] **13.3 Replace all placeholder colours** Several elements have placeholder colours that were clearly never updated: - `#controls-left`: `background-color: pink` → `var(--color-surface)` - `#controls-bottom`: `background-color: lightblue` → `var(--color-surface)` - `#main-logo`: `background-color: lightblue` → transparent (let the SVG stand alone) - `.control-group`: `background-color: lightgreen` → remove; use spacing to separate groups - [ ] **13.4 Change grid lines from black to the border token** Replace `border: 1px solid black` on `td, th` with `border: 1px solid var(--color-border)`. This is the single highest-impact change to the table's appearance. ### Typography - [ ] **13.5 Switch to a system font stack** Replace the current `'Helvetica Neue', Verdana, Helvetica, Arial, sans-serif` with `system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`. This makes the app feel native on each platform rather than web-circa-2010. - [ ] **13.6 Use monospace for cell values** Numbers in a proportional font look accidental; numbers in a monospace font look deliberate. Apply `font-family: 'SF Mono', ui-monospace, 'Fira Code', monospace` to `td input` and use a slightly smaller size (around `0.875rem`) relative to the header labels. ### Grid & Cells - [ ] **13.7 Make inputs fill their cells** `td input` currently has a fixed `width: 80px`, leaving dead space in the `