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

Configure Feed

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

fix(calendar): replace settings sunburst icon with gear glyph (#697)

Closes #697

scott d645b8c0 e982c015

+78 -3
+3
CHANGELOG.md
··· 7 7 8 8 ## [Unreleased] 9 9 10 + ### Changed 11 + - Calendar: topbar settings button now renders as a proper 8-tooth gear glyph instead of a sunburst that was visually indistinguishable from the adjacent theme-toggle sun (v0.62.4, #697). The old `#btn-cal-settings` SVG was a circle-with-8-radiating-lines (`circle r=2.5` + eight `M..v.. / M..h..` rays) and the theme-toggle `#btn-theme-toggle` right next to it was the same circle-with-8-rays pattern at `r=3.5` — users clicked the wrong one constantly, with the settings panel hiding some destructive controls. Replaced the settings icon with a true 32-vertex 8-tooth gear polygon (outer tooth tip radius 6.8, valley radius 4.6, hub circle r=2.2) traced in `src/calendar/index.html`. Added an `icon-gear` marker class so future refactors can't silently regress to a sunburst, plus four Playwright regression tests in `e2e/calendar.spec.ts` that pin the contract: (1) the icon carries the `icon-gear` class; (2) the settings path `d` attribute differs from the theme-toggle path; (3) the path has either curves, a closed subpath, or >24 draw commands (sunburst had ~16 and no close); (4) both buttons remain visible and adjacent. Also added `aria-label` + `aria-hidden` attributes that the theme-toggle already had for a11y parity. (#697) 12 + 10 13 ### Fixed 11 14 - Calendar: mini-calendar date cells now expose a full spoken date via `aria-label` (v0.62.3, #696). Previously the `.cal-mini-day` buttons contained only the day number as text, so screen readers announced "button, 29" with no month/year/weekday context — unusable for non-sighted navigation. `renderMiniCalendar` now builds an `aria-label` like `"Tuesday, March 29, 2026"` (plus `, today`, `, selected`, and `, N events` suffixes when applicable) using a new shared `formatLongDate(d)` helper in `src/calendar/helpers.ts` that wraps `toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })`. The today cell also carries `aria-current="date"` and the selected cell carries `aria-pressed="true"` so state is conveyed through standard AT semantics rather than class names alone. 4 regression tests in `tests/calendar-mini-day-aria.test.ts` pin the helper output and verify every emitted `.cal-mini-day` button tag carries an `aria-label=` attribute. (#696) 12 15 - CSP: externalized 3 inline scripts from every page template so the app's `script-src 'self'` CSP stops silently blocking them (v0.62.2, #694). Theme init (FOUC prevention, reads localStorage before paint), theme-toggle click handler, and service-worker update→reload handler now live in `public/theme-init.js`, `public/theme-toggle.js`, and `public/sw-reload.js` respectively, loaded via `<script src="...">` so they satisfy the strict CSP without needing nonces or `unsafe-inline`. Before: the theme toggle button did nothing, dark-mode users saw a flash of light theme on every page load, and users never auto-reloaded when a new version deployed. All 7 HTML templates (landing + 6 editors) had the inline blocks; all 7 are now externalized. Added `tests/csp-no-inline-scripts.test.ts` which scans every template for inline `<script>` blocks and fails if any are reintroduced. Caught live by driving the deployed v0.62.1 app via Playwright MCP. (#694)
+72
e2e/calendar.spec.ts
··· 563 563 expect(restoredTheme).toBe(initialTheme); 564 564 }); 565 565 }); 566 + 567 + // --------------------------------------------------------------------------- 568 + // Calendar - Topbar Icon Distinction (#697) 569 + // --------------------------------------------------------------------------- 570 + // 571 + // The calendar settings button (#btn-cal-settings) and the theme toggle 572 + // (#btn-theme-toggle) historically rendered as near-identical sun/sunburst 573 + // glyphs (circle + 8 radiating lines). Users clicked the wrong one. 574 + // 575 + // These tests lock in that the settings icon is a distinct gear glyph — it 576 + // must have visibly different SVG geometry than the theme toggle, and it 577 + // must carry a marker class so future refactors don't silently regress to 578 + // a sunburst. 579 + // --------------------------------------------------------------------------- 580 + 581 + test.describe('Calendar - Topbar Icon Distinction (#697)', () => { 582 + test.beforeEach(async ({ page }) => { 583 + await createNewCalendar(page); 584 + }); 585 + 586 + test('settings icon uses a gear glyph, not a sunburst', async ({ page }) => { 587 + const settingsSvg = page.locator('#btn-cal-settings svg'); 588 + // The gear icon carries a dedicated marker class so tooling / future 589 + // visual tests can identify it without parsing path data. 590 + await expect(settingsSvg).toHaveClass(/icon-gear/); 591 + }); 592 + 593 + test('settings icon SVG geometry differs from theme toggle SVG geometry', async ({ 594 + page, 595 + }) => { 596 + const settingsPathData = await page 597 + .locator('#btn-cal-settings svg path') 598 + .first() 599 + .getAttribute('d'); 600 + const themePathData = await page 601 + .locator('#btn-theme-toggle svg path') 602 + .first() 603 + .getAttribute('d'); 604 + 605 + expect(settingsPathData).toBeTruthy(); 606 + expect(themePathData).toBeTruthy(); 607 + expect(settingsPathData).not.toBe(themePathData); 608 + }); 609 + 610 + test('settings icon does not contain the sunburst radiating-lines pattern', async ({ 611 + page, 612 + }) => { 613 + // The old buggy icon was a sunburst: 8 short single-segment rays with 614 + // 8 separate M moves, ~16 total draw commands, and no closed subpath. 615 + // A proper gear glyph must be substantially more geometrically complex: 616 + // either many more segments, or include curves/arcs. 617 + const d = await page 618 + .locator('#btn-cal-settings svg path') 619 + .first() 620 + .getAttribute('d'); 621 + expect(d).toBeTruthy(); 622 + const hasCurves = /[ACQ]/i.test(d!); 623 + const hasClosedSubpath = /[Zz]/.test(d!); 624 + // Count all drawing commands (M, L, H, V, C, Q, A, Z). 625 + const commandCount = (d!.match(/[MLHVCQAZ]/gi) || []).length; 626 + // Sunburst had ~16 commands (8 M + 8 L-ish). Gear polygon has ~34+ or 627 + // curved gear has arcs. Require clear differentiation from sunburst. 628 + expect(hasCurves || hasClosedSubpath || commandCount > 24).toBe(true); 629 + }); 630 + 631 + test('settings and theme buttons remain visually adjacent and both visible', async ({ 632 + page, 633 + }) => { 634 + await expect(page.locator('#btn-cal-settings')).toBeVisible(); 635 + await expect(page.locator('#btn-theme-toggle')).toBeVisible(); 636 + }); 637 + });
+1 -1
package.json
··· 1 1 { 2 2 "name": "tools", 3 - "version": "0.62.3", 3 + "version": "0.62.4", 4 4 "private": true, 5 5 "type": "module", 6 6 "main": "electron/main.js",
+2 -2
src/calendar/index.html
··· 28 28 <span class="save-dot save-dot--saved"></span> 29 29 <span id="save-text">Saved</span> 30 30 </div> 31 - <button class="btn-icon" id="btn-cal-settings" title="Calendar settings"> 32 - <svg class="tb-icon" viewBox="0 0 16 16" style="width:16px;height:16px" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="2.5"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M2.9 2.9l1.4 1.4M11.7 11.7l1.4 1.4M2.9 13.1l1.4-1.4M11.7 4.3l1.4-1.4"/></svg> 31 + <button class="btn-icon" id="btn-cal-settings" title="Calendar settings" aria-label="Calendar settings"> 32 + <svg class="tb-icon icon-gear" viewBox="0 0 16 16" style="width:16px;height:16px" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.57 6.24 L14.57 9.76 L12.44 9.19 L11.98 10.30 L13.89 11.40 L11.40 13.89 L10.30 11.98 L9.19 12.44 L9.76 14.57 L6.24 14.57 L6.81 12.44 L5.70 11.98 L4.60 13.89 L2.11 11.40 L4.02 10.30 L3.56 9.19 L1.43 9.76 L1.43 6.24 L3.56 6.81 L4.02 5.70 L2.11 4.60 L4.60 2.11 L5.70 4.02 L6.81 3.56 L6.24 1.43 L9.76 1.43 L9.19 3.56 L10.30 4.02 L11.40 2.11 L13.89 4.60 L11.98 5.70 L12.44 6.81 Z"/><circle cx="8" cy="8" r="2.2"/></svg> 33 33 </button> 34 34 <button class="btn-icon" id="btn-theme-toggle" title="Toggle theme"> 35 35 <svg class="tb-icon" viewBox="0 0 16 16" style="width:16px;height:16px" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="3.5"/><path d="M8 1.5v1M8 13.5v1M1.5 8h1M13.5 8h1M3.4 3.4l.7.7M11.9 11.9l.7.7M3.4 12.6l.7-.7M11.9 4.1l.7-.7"/></svg>