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

Configure Feed

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

fix(docs): TipTap v3 StarterKit duplicates and undoRedo not disabled (#710)

Closes #710

scott 6504e942 d645b8c0

+93 -12
+1
CHANGELOG.md
··· 11 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 12 13 13 ### Fixed 14 + - Docs: TipTap v3 StarterKit config no longer emits duplicate-extension and Collaboration/UndoRedo-conflict warnings (v0.62.5, #710). PR #409 (the v2→v3 migration for #692) passed `history: false` to StarterKit.configure, but v3 renamed the History extension to UndoRedo — `history: false` is a silent no-op, so StarterKit's UndoRedo stayed active and fought Yjs for undo state. StarterKit v3 also vendors Link and Underline, so our explicit `@tiptap/extension-link` + `@tiptap/extension-underline` imports registered duplicates of those extensions. Updated `src/docs/main.ts` to use `StarterKit.configure({ undoRedo: false, codeBlock: false, link: { openOnClick: false } })` and removed the explicit Link/Underline imports. Dropped the two packages from package.json. Added `tests/tiptap-v3-no-warnings.test.ts` (2 tests: jsdom editor-construction with zero TipTap warnings, plus static scan of src/docs/main.ts for deprecated v2 patterns). Caught live by inspecting the browser console on a deployed doc. (#710) 14 15 - 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) 15 16 - 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) 16 17 - Sheets: first printable keystroke against an empty cell no longer duplicates the character (v0.62.1, #693). The grid's keydown handler for printable chars in `src/sheets/keyboard-handler.ts` entered edit mode and set `editor.value = key` but never called `e.preventDefault()`, so the browser's native keypress/input pipeline on the now-focused cell-editor input also inserted the same character — pressing `5` produced `55`, pressing `1`→`0`→Enter produced `110`. Added the missing `preventDefault()` plus a targeted regression test in `tests/sheets-keyboard-handler.test.ts` (3 new tests: preventDefault invariant, single-char-not-doubled invariant, and the pre-existing Cmd+key-skip invariant). Caught live by driving the deployed app in a real browser via Playwright MCP during TipTap v3 post-ship smoke testing. (#693)
+2 -4
package-lock.json
··· 1 1 { 2 2 "name": "tools", 3 - "version": "0.61.0", 3 + "version": "0.62.3", 4 4 "lockfileVersion": 3, 5 5 "requires": true, 6 6 "packages": { 7 7 "": { 8 8 "name": "tools", 9 - "version": "0.61.0", 9 + "version": "0.62.3", 10 10 "dependencies": { 11 11 "@tiptap/core": "^3.22.3", 12 12 "@tiptap/extension-code-block-lowlight": "^3.22.3", ··· 16 16 "@tiptap/extension-font-family": "^3.22.3", 17 17 "@tiptap/extension-highlight": "^3.22.3", 18 18 "@tiptap/extension-image": "^3.22.3", 19 - "@tiptap/extension-link": "^3.22.3", 20 19 "@tiptap/extension-placeholder": "^3.22.3", 21 20 "@tiptap/extension-subscript": "^3.22.3", 22 21 "@tiptap/extension-superscript": "^3.22.3", ··· 28 27 "@tiptap/extension-task-list": "^3.22.3", 29 28 "@tiptap/extension-text-align": "^3.22.3", 30 29 "@tiptap/extension-text-style": "^3.22.3", 31 - "@tiptap/extension-underline": "^3.22.3", 32 30 "@tiptap/pm": "^3.22.3", 33 31 "@tiptap/starter-kit": "^3.22.3", 34 32 "@tiptap/suggestion": "^3.22.3",
+1 -3
package.json
··· 1 1 { 2 2 "name": "tools", 3 - "version": "0.62.4", 3 + "version": "0.62.5", 4 4 "private": true, 5 5 "type": "module", 6 6 "main": "electron/main.js", ··· 25 25 "@tiptap/extension-font-family": "^3.22.3", 26 26 "@tiptap/extension-highlight": "^3.22.3", 27 27 "@tiptap/extension-image": "^3.22.3", 28 - "@tiptap/extension-link": "^3.22.3", 29 28 "@tiptap/extension-placeholder": "^3.22.3", 30 29 "@tiptap/extension-subscript": "^3.22.3", 31 30 "@tiptap/extension-superscript": "^3.22.3", ··· 37 36 "@tiptap/extension-task-list": "^3.22.3", 38 37 "@tiptap/extension-text-align": "^3.22.3", 39 38 "@tiptap/extension-text-style": "^3.22.3", 40 - "@tiptap/extension-underline": "^3.22.3", 41 39 "@tiptap/pm": "^3.22.3", 42 40 "@tiptap/starter-kit": "^3.22.3", 43 41 "@tiptap/suggestion": "^3.22.3",
+14 -5
src/docs/main.ts
··· 11 11 import { setupTooltips } from '../lib/tooltips.js'; 12 12 import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'; 13 13 import { common, createLowlight } from 'lowlight'; 14 - import Underline from '@tiptap/extension-underline'; 15 - import Link from '@tiptap/extension-link'; 14 + // Note: Underline + Link are vendored by StarterKit in TipTap v3. Do not 15 + // import them explicitly — TipTap warns about duplicate extension names 16 + // and behavior becomes undefined. See #710. 16 17 import { ResizableImage } from './extensions/resizable-image.js'; 17 18 import { Table } from '@tiptap/extension-table'; 18 19 import TableRow from '@tiptap/extension-table-row'; ··· 176 177 const editor = new Editor({ 177 178 element: document.getElementById('editor'), 178 179 extensions: [ 179 - StarterKit.configure({ history: false, codeBlock: false }), 180 + StarterKit.configure({ 181 + // Yjs Collaboration owns undo/redo — disable StarterKit's UndoRedo 182 + // (renamed from History in v3). The old `history: false` key is a no-op 183 + // in v3 and leaves UndoRedo active, which fights Yjs. See #710. 184 + undoRedo: false, 185 + // CodeBlockLowlight handles code blocks with syntax highlighting. 186 + codeBlock: false, 187 + // StarterKit v3 vendors Link — pass our config through instead of 188 + // registering a second Link extension. 189 + link: { openOnClick: false }, 190 + }), 180 191 CodeBlockLowlight.configure({ 181 192 lowlight: createLowlight(common), 182 193 defaultLanguage: 'plaintext', 183 194 }), 184 - Underline, 185 - Link.configure({ openOnClick: false }), 186 195 ResizableImage, 187 196 Table.configure({ resizable: true }), 188 197 TableRow,
+75
tests/tiptap-v3-no-warnings.test.ts
··· 1 + /** 2 + * @vitest-environment jsdom 3 + * 4 + * Regression test for #710: TipTap v3 StarterKit vendors Link, Underline, and 5 + * UndoRedo. Registering those extensions explicitly alongside StarterKit (or 6 + * disabling them via the old `history: false` key that no longer exists) 7 + * produces runtime warnings like: 8 + * 9 + * [tiptap warn]: Duplicate extension names found: ['link', 'underline'] 10 + * [tiptap warn]: "@tiptap/extension-collaboration" ... not compatible with 11 + * "@tiptap/extension-undo-redo" 12 + * 13 + * The second warning matters — UndoRedo fights Yjs for undo state. 14 + * 15 + * This test builds an editor with the production config shape and asserts 16 + * zero TipTap console warnings fire. Any future drift (e.g. someone re-adds 17 + * an explicit Link import, or forgets to pass `undoRedo: false`) fails here 18 + * before hitting the browser. 19 + */ 20 + import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; 21 + import { readFileSync } from 'node:fs'; 22 + import { resolve } from 'node:path'; 23 + import { Editor } from '@tiptap/core'; 24 + import StarterKit from '@tiptap/starter-kit'; 25 + import * as Y from 'yjs'; 26 + import Collaboration from '@tiptap/extension-collaboration'; 27 + 28 + describe('#710 — TipTap v3 StarterKit config produces no warnings', () => { 29 + let warnSpy: ReturnType<typeof vi.spyOn>; 30 + 31 + beforeEach(() => { 32 + warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); 33 + }); 34 + 35 + afterEach(() => { 36 + warnSpy.mockRestore(); 37 + }); 38 + 39 + it('matches the production docs editor extension shape with zero TipTap warnings', () => { 40 + const ydoc = new Y.Doc(); 41 + const host = document.createElement('div'); 42 + document.body.appendChild(host); 43 + 44 + const editor = new Editor({ 45 + element: host, 46 + extensions: [ 47 + StarterKit.configure({ 48 + undoRedo: false, 49 + codeBlock: false, 50 + link: { openOnClick: false }, 51 + }), 52 + Collaboration.configure({ document: ydoc }), 53 + ], 54 + }); 55 + 56 + const tipTapWarnings = warnSpy.mock.calls 57 + .map(args => args.map(a => String(a)).join(' ')) 58 + .filter(msg => /tiptap/i.test(msg)); 59 + 60 + expect(tipTapWarnings).toEqual([]); 61 + 62 + editor.destroy(); 63 + }); 64 + 65 + it('src/docs/main.ts does not use deprecated v2 StarterKit keys (#710)', () => { 66 + // Scan the real production config for patterns that silently break v3. 67 + // `history: false` is a no-op (renamed to `undoRedo`), and explicit 68 + // `import Link/Underline from @tiptap/extension-*` paths duplicate the 69 + // vendored StarterKit extensions. Any of these resurrect the warnings. 70 + const src = readFileSync(resolve(process.cwd(), 'src/docs/main.ts'), 'utf-8'); 71 + expect(src).not.toMatch(/StarterKit\.configure\([^)]*history\s*:/); 72 + expect(src).not.toMatch(/^import\s+Underline\s+from\s+['"]@tiptap\/extension-underline['"]/m); 73 + expect(src).not.toMatch(/^import\s+Link\s+from\s+['"]@tiptap\/extension-link['"]/m); 74 + }); 75 + });