Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: UI polish batch — #714 #715 #727 #728 (#423)

scott 02c37621 88873ee1

+37 -4
+4
CHANGELOG.md
··· 8 8 ## [Unreleased] 9 9 10 10 ### Fixed 11 + - Slides: thumbnail element-count badge no longer visually merges with the slide-number, so two slides with single elements used to read as "11"/"22" etc. (v0.62.17, #727). The thumbnail previously rendered `<span class="slides-thumb-num">{N}</span>` followed by a bare `<span class="slides-thumb-count">{M}</span>` inside the preview — with no separator they appeared flush together. Prefixed the count with a `▦` glyph + space and added `aria-label="{M} element(s)"` so screen readers hear "N elements" rather than the number alone. (#727) 12 + - Slides: each click of "Add text" now offsets the new box by 20×N where N is the current element count on the slide, so repeated clicks don't stack boxes at the identical (100, 100) default position where the second would silently cover the first (v0.62.17, #728). (#728) 13 + - Forms: Preview and Responses modes hide the "+ Add Question" button and the editable form-description textarea, leaving only the mode-switch bar so the author can toggle back (v0.62.17, #715). Before: Preview mixed builder controls with respondent UI, making it unclear what a real respondent would see. (#715) 14 + - Docs: opening the formatting-toolbar kebab overflow menu while the cursor sits inside a table no longer has the table-context toolbar (cell-background color picker, row/col buttons) bleeding over "Code block" and other menu items (v0.62.17, #714). Both `.more-menu-panel` and `.table-toolbar` used `z-index: var(--z-overlay)` so DOM order decided who won. Raised the more-menu-panel to `var(--z-tooltip)` since the menu is always a modal-ish short-lived foreground overlay. (#714) 11 15 - Sheets: cross-sheet formula references (e.g. `='Sheet 1'!F11`) now resolve to actual values instead of returning `#REF!` (v0.62.16, #725). The formula engine in `src/sheets/formulas.ts` has always accepted a third `crossSheetResolver` argument and `tests/cross-sheet.test.ts` had 21 passing unit tests proving the parser works — but `evaluateFormula()` in `src/sheets/cell-computation.ts` (the one entry point every in-app cell flows through) never passed one. Every runtime cross-sheet formula hit the `#REF!` default branch. Built a CrossSheetResolver that walks `getYSheets()`, reads the named sheet's `cells` Y.Map, and recurses into formula cells with a sheet-scoped local resolver so nested references resolve in the correct sheet's context. 5 jsdom regression tests in `tests/sheets-cross-sheet-runtime.test.ts` build a real Y.Doc and pin: plain cross-ref, SUM over cross-sheet range, arithmetic, `#REF!` for missing sheet, and cross-sheet → formula-cell recursion. Caught while building a Homelab Budget workbook with Sheet 1 data + Sheet 2 summary. (#725) 12 16 - Docs: version-history preview now renders readable HTML instead of raw ProseMirror schema XML (v0.62.11, #719). Before: clicking a version entry dumped `<paragraph indent="0"><heading level="1">Title</heading><bulletList>…` into the preview pane — unreadable, and "Restore this version" was effectively blind. Added `renderYjsFragmentAsHtml()` in `src/version-panel.ts` that walks the decrypted Yjs XmlFragment and maps each node name to a sensible HTML tag (heading→h1..h6, paragraph→p, bulletList→ul, listItem→li, table/tableRow/tableCell/tableHeader→table/tr/td/th, codeBlock→pre/code, image→img, etc.), with HTML-escaped text and a `<div>` fallback for unknown node types. 11 regression tests in `tests/version-panel-render-preview.test.ts`. (#719) 13 17 - Sheets: Share button click now opens the in-app share dialog (v0.62.12, #720). The share-dialog markup shipped in `src/sheets/index.html` and the reusable `initShareDialog()` helper existed in `src/lib/share-dialog.ts`, but sheets' `main.ts` never called it — so clicking the Share button was a silent no-op. Threaded `keyString` through `session-bootstrap.ts`'s return value and added `initShareDialog({ docId, docType: 'sheets', keyString })` in sheets' init path. Share now opens a dialog with a live-generated link, mode selector, and expiry picker — same as docs. (#720)
+1 -1
package.json
··· 1 1 { 2 2 "name": "tools", 3 - "version": "0.62.16", 3 + "version": "0.62.17", 4 4 "private": true, 5 5 "type": "module", 6 6 "main": "electron/main.js",
+4 -1
src/css/app.css
··· 1320 1320 border: 1px solid var(--color-border); 1321 1321 border-radius: var(--radius-md); 1322 1322 box-shadow: var(--shadow-md); 1323 - z-index: var(--z-overlay); 1323 + /* Raise above .table-toolbar (also z-overlay=100) so opening the kebab 1324 + overflow while the cursor is inside a table doesn't leave the cell 1325 + background picker bleeding over "Code block" etc. See #714. */ 1326 + z-index: var(--z-tooltip); 1324 1327 padding: var(--space-xs); 1325 1328 flex-direction: column; 1326 1329 }
+13
src/forms/main.ts
··· 60 60 const previewPane = document.getElementById('form-preview')!; 61 61 const responsesPane = document.getElementById('form-responses-view')!; 62 62 const toolbar = document.getElementById('form-toolbar')!; 63 + const addQuestionBtn = document.getElementById('btn-add-question')!; 64 + const formHeader = document.getElementById('form-header')!; 63 65 64 66 // --- Sync form to Yjs --- 65 67 function syncFormToYjs() { ··· 90 92 // --- Mode rendering --- 91 93 function renderBuilder() { 92 94 toolbar.style.display = ''; 95 + // Builder mode shows + Add Question and the editable description textarea. 96 + addQuestionBtn.style.display = ''; 97 + formHeader.style.display = ''; 93 98 questionsContainer.style.display = ''; 94 99 previewPane.style.display = 'none'; 95 100 responsesPane.style.display = 'none'; ··· 102 107 103 108 function showPreview() { 104 109 toolbar.style.display = ''; 110 + // Hide builder-only controls so the preview matches what a respondent will 111 + // see. Keep Preview/Responses/Settings so the author can toggle back. See #715. 112 + addQuestionBtn.style.display = 'none'; 113 + formHeader.style.display = 'none'; 105 114 questionsContainer.style.display = 'none'; 106 115 previewPane.style.display = ''; 107 116 responsesPane.style.display = 'none'; ··· 115 124 116 125 function showResponses() { 117 126 toolbar.style.display = ''; 127 + // Responses view is author-only but also shouldn't show the editable 128 + // description textarea / Add Question button — those belong in Builder mode. 129 + addQuestionBtn.style.display = 'none'; 130 + formHeader.style.display = 'none'; 118 131 questionsContainer.style.display = 'none'; 119 132 previewPane.style.display = 'none'; 120 133 responsesPane.style.display = '';
+8 -1
src/slides/event-handlers.ts
··· 57 57 58 58 $('btn-add-text').addEventListener('click', () => { 59 59 const s = actions.getState(); 60 - actions.setState({ deck: addElement(s.deck, 'text', 100, 100, 300, 60, 'Click to edit') }); 60 + // Offset each new text box by 20px × (existing element count on this 61 + // slide) so repeated clicks don't stack boxes at the identical default 62 + // position where the second would silently cover the first. See #728. 63 + const slide = s.deck.slides[s.deck.currentSlide]; 64 + const n = slide ? slide.elements.length : 0; 65 + const x = 100 + (n * 20); 66 + const y = 100 + (n * 20); 67 + actions.setState({ deck: addElement(s.deck, 'text', x, y, 300, 60, 'Click to edit') }); 61 68 actions.syncDeckToYjs(); 62 69 actions.renderCanvas(); 63 70 });
+7 -1
src/slides/rendering.ts
··· 34 34 preview.style.background = slide.background; 35 35 preview.style.aspectRatio = '16/9'; 36 36 37 + // Element count as a corner badge — previously this rendered as bare 38 + // `<span>{N}</span>` which rendered visually flush against the slide- 39 + // number above ("slide 1 with 1 element" read as "11", slide 2 with 2 40 + // elements as "22", etc). See #727. Prefix with the element glyph so 41 + // the badge reads as "▦ N" and is clearly separate from the slide 42 + // number. 37 43 if (slide.elements.length > 0) { 38 - preview.innerHTML = `<span class="slides-thumb-count">${slide.elements.length}</span>`; 44 + preview.innerHTML = `<span class="slides-thumb-count" aria-label="${slide.elements.length} element${slide.elements.length === 1 ? '' : 's'}">\u25a6 ${slide.elements.length}</span>`; 39 45 } 40 46 41 47 thumb.appendChild(num);