# 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 *Under consideration.* - [ ] **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. - [ ] **7.2 Always-visible row totals column** Equivalent pinned column to the right of the table for row aggregates. --- ## 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 `