-
+
(
}>
{
})
}
+async function hasSelectedEditorNode(page: Page): Promise {
+ return page.evaluate(() => document.querySelector('.ProseMirror-selectednode') !== null)
+}
+
+async function expectNoEditorNodeSelection(page: Page): Promise {
+ expect(await hasSelectedEditorNode(page)).toBe(false)
+}
+
+async function applyZoom(page: Page, percent: number): Promise {
+ await page.evaluate((pct) => {
+ document.documentElement.style.setProperty('zoom', `${pct}%`)
+ window.dispatchEvent(new Event('laputa-zoom-change'))
+ }, percent)
+ await page.waitForTimeout(250)
+}
+
+async function firstTldrawShapeOrigin(page: Page): Promise<{ x: number, y: number } | null> {
+ return page.locator('.tl-shape').first().evaluate((element) => {
+ const whiteboard = element.closest('.tldraw-whiteboard')
+ if (!whiteboard) return null
+
+ const matrix = new DOMMatrixReadOnly(getComputedStyle(element).transform)
+ const boardBox = whiteboard.getBoundingClientRect()
+ const zoomStyle = document.documentElement.style.getPropertyValue('zoom')
+ || getComputedStyle(document.documentElement).zoom
+ const parsedZoom = Number.parseFloat(zoomStyle)
+ const zoom = Number.isFinite(parsedZoom) && parsedZoom > 0
+ ? zoomStyle.endsWith('%') ? parsedZoom / 100 : parsedZoom
+ : 1
+
+ return {
+ x: boardBox.x + matrix.m41 * zoom,
+ y: boardBox.y + matrix.m42 * zoom,
+ }
+ })
+}
+
test('tldraw whiteboard fences render as embedded canvases and remain Markdown-durable', async ({ page }) => {
await openNote(page, 'Whiteboard Embed')
@@ -68,6 +105,7 @@ test('tldraw whiteboard fences render as embedded canvases and remain Markdown-d
await expect(page.locator('.bn-editor')).toContainText('Context before the board.')
await expect(page.locator('.bn-editor')).toContainText('Context after the board.')
+ await page.waitForTimeout(500)
await toggleRawMode(page, '.cm-content')
const rawAfterRichMode = await getRawEditorContent(page)
@@ -75,3 +113,68 @@ test('tldraw whiteboard fences render as embedded canvases and remain Markdown-d
expect(rawAfterRichMode).toContain('{}')
expect(rawAfterRichMode).not.toContain('@@TOLARIA_TLDRAW')
})
+
+test('embedded tldraw interactions stay inside the whiteboard', async ({ page }) => {
+ await openNote(page, 'Whiteboard Embed')
+
+ const whiteboard = page.locator('.tldraw-whiteboard')
+ await expect(whiteboard).toBeVisible({ timeout: 20_000 })
+
+ const boardBox = await whiteboard.boundingBox()
+ expect(boardBox).not.toBeNull()
+
+ await page.mouse.click(boardBox!.x + boardBox!.width / 2, boardBox!.y + boardBox!.height / 2)
+ await expectNoEditorNodeSelection(page)
+
+ await page.getByTestId('tools.select').click()
+ await expectNoEditorNodeSelection(page)
+
+ const pageMenuButton = page.getByTestId('page-menu.button')
+ const buttonBox = await pageMenuButton.boundingBox()
+ expect(buttonBox).not.toBeNull()
+
+ await pageMenuButton.click()
+
+ const pageMenu = page.locator('.tlui-page-menu__wrapper')
+ await expect(pageMenu).toBeVisible({ timeout: 5_000 })
+
+ const menuBox = await pageMenu.boundingBox()
+ expect(menuBox).not.toBeNull()
+ expect(menuBox!.x).toBeGreaterThanOrEqual(boardBox!.x - 1)
+ expect(menuBox!.x).toBeLessThanOrEqual(buttonBox!.x + 1)
+ await expectNoEditorNodeSelection(page)
+})
+
+test('embedded tldraw drawing uses the clicked coordinates while zoomed', async ({ page }) => {
+ await openNote(page, 'Whiteboard Embed')
+ await applyZoom(page, 110)
+
+ const whiteboard = page.locator('.tldraw-whiteboard')
+ await expect(whiteboard).toBeVisible({ timeout: 20_000 })
+ const boardBox = await whiteboard.boundingBox()
+ expect(boardBox).not.toBeNull()
+
+ await page.getByTestId('tools.draw').click()
+
+ const start = {
+ x: boardBox!.x + 180,
+ y: boardBox!.y + 180,
+ }
+ const end = {
+ x: start.x + 120,
+ y: start.y + 90,
+ }
+
+ await page.mouse.move(start.x, start.y)
+ await page.mouse.down()
+ await page.mouse.move(end.x, end.y, { steps: 8 })
+ await page.mouse.up()
+
+ const shape = page.locator('.tl-shape').first()
+ await expect(shape).toBeVisible({ timeout: 5_000 })
+
+ const shapeOrigin = await firstTldrawShapeOrigin(page)
+ expect(shapeOrigin).not.toBeNull()
+ expect(Math.abs(shapeOrigin!.x - start.x)).toBeLessThan(30)
+ expect(Math.abs(shapeOrigin!.y - start.y)).toBeLessThan(30)
+})