Files
goodbuddy/tests/support/canvas-core-regression.tsx
T
mesalogo c98eb69cbd
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
feat: refine notes workspace and add canvas zoom
Simplify sidebar selection and note record framing, compact the empty composer, and emphasize note titles. Replace redundant AI comment headings with an accessible panel toggle.

Add view-only canvas zoom and fit width, correct PDF import/export icons, and allow image files up to 20 MiB. Include regression coverage and updated design documentation.

Release note: Notes have clearer navigation and a more compact writing area. Canvases support zoom and fit width, and image imports accept files up to 20 MiB.
2026-09-19 20:56:03 +08:00

98 lines
5.2 KiB
TypeScript

import Quill from 'quill'
import { createRoot } from 'react-dom/client'
import { mountCanvasNote } from '../../src/renderer/src/magic-canvas/canvas-note.mjs'
import { MagicCanvasThumbnail } from '../../src/renderer/src/MagicCanvasThumbnail'
import { fromCoreContent, toCoreContent } from '../../src/renderer/src/magic-canvas/model'
import '../../src/renderer/src/i18n'
import 'quill/dist/quill.snow.css'
import '../../src/renderer/src/styles.css'
import '../../src/renderer/src/magic-canvas.css'
async function canvasRegression() {
const errors: string[] = []
const host = document.createElement('div')
host.className = 'magic-canvas-editor'
document.body.append(host)
let editor = mountCanvasNote(host, { version: 2, pages: [{
id: 'first', width: 794, height: 1123,
background: { type: 'template', template: 'blank' },
objects: [{ type: 'Path', canvasKind: 'pen', path: [['M', 20, 20], ['L', 150, 80]],
stroke: '#ff0000', strokeWidth: 4, fill: null }]
}], flow: { version: 1, ops: [{ insert: 'Flow line\n'.repeat(50) }] } }, { onError: (error) => errors.push(error) })
const initial = await editor.flush()
const ink = async () => {
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
const canvas = host.querySelector<HTMLCanvasElement>('.lower-canvas')!
const pixels = canvas.getContext('2d')!.getImageData(0, 0, canvas.width, canvas.height).data
let count = 0
for (let i = 3; i < pixels.length; i += 4) if (pixels[i]) count++
return count
}
const visibleInkBefore = await ink()
host.querySelector<HTMLButtonElement>('[data-tool="flow-text"]')!.click()
host.querySelector<HTMLButtonElement>('.canvas-note-next-page')!.click()
await editor.flush()
const beforeShrink = host.querySelector('.canvas-note-page-counter')!.textContent
const quill = Quill.find(host.querySelector('.canvas-flow-quill')!) as Quill
quill.setText('Short flow\n', 'user')
const saved = JSON.parse(JSON.stringify(await editor.flush()))
const afterShrink = host.querySelector('.canvas-note-page-counter')!.textContent
const visibleInkAfter = await ink()
const flowTransform = host.querySelector<HTMLElement>('.canvas-flow-surface')!.style.transform
await editor.destroy()
editor = mountCanvasNote(host, saved, { onError: (error) => errors.push(error) })
const reopened = await editor.flush()
const reopenedInk = await ink()
const styles = (surface: Element) => {
const style = getComputedStyle(surface)
return { fontSize: style.fontSize, lineHeight: style.lineHeight, columnFill: style.columnFill, padding: style.padding }
}
const editorStyles = styles(host.querySelector('.canvas-flow-surface')!)
const [expectedThumbnail] = await editor.capturePages({ firstPageOnly: true, thumbnailWidth: 240 })
await editor.destroy()
const thumbnailHost = document.createElement('div')
document.body.append(thumbnailHost)
let thumbnailStyles: ReturnType<typeof styles> | undefined
const observer = new MutationObserver(() => {
const surface = document.querySelector('.magic-note-thumbnail-renderer .canvas-flow-surface')
if (surface) thumbnailStyles = styles(surface)
})
observer.observe(document.body, { childList: true, subtree: true })
const root = createRoot(thumbnailHost)
root.render(<MagicCanvasThumbnail content={fromCoreContent(saved, [])} />)
for (let i = 0; i < 400 && !thumbnailHost.querySelector('img'); i++) await new Promise(resolve => setTimeout(resolve, 25))
const thumbnailMatchesEditor = thumbnailHost.querySelector('img')?.src === expectedThumbnail.dataUrl
root.unmount()
observer.disconnect()
thumbnailHost.remove()
const imageImports = []
const pixel = document.createElement('canvas')
pixel.width = pixel.height = 1
const png = await new Promise<Blob>((resolve) => pixel.toBlob(blob => resolve(blob!), 'image/png'))
for (const size of [2 * 1024 * 1024 + 1, 20 * 1024 * 1024, 20 * 1024 * 1024 + 1]) {
const importErrors: string[] = []
editor = mountCanvasNote(host, undefined, { onError: error => importErrors.push(error) })
await editor.flush()
const input = host.querySelector<HTMLInputElement>('.canvas-note-image-input')!
const transfer = new DataTransfer()
transfer.items.add(new File([png, new Uint8Array(size - png.size)], 'boundary.png', { type: 'image/png' }))
input.files = transfer.files
input.dispatchEvent(new Event('change'))
const imported = await editor.flush()
const persisted = fromCoreContent(imported, [])
await editor.destroy()
editor = mountCanvasNote(host, toCoreContent(persisted), { onError: error => importErrors.push(error) })
const restored = await editor.flush()
imageImports.push({ size, imported: imported.pages[0].objects.length,
restored: restored.pages[0].objects.length, errors: importErrors })
await editor.destroy()
}
host.remove()
return { errors, initialPages: initial.pages.length, initialObjects: initial.pages[0].objects,
beforeShrink, afterShrink, savedPages: saved.pages.length, savedObjects: saved.pages[0].objects,
reopenedObjects: reopened.pages[0].objects, visibleInkBefore, visibleInkAfter, reopenedInk, flowTransform,
editorStyles, thumbnailStyles, thumbnailMatchesEditor, imageImports }
}
Object.assign(window, { canvasRegression })