···55The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7788+## [0.21.0] — 2026-04-03
99+1010+### Added
1111+- Drag-to-create shapes on diagrams canvas instead of fixed-size click-to-place (#307)
1212+- On-canvas resize handles with 8 corner/edge handles for precise shape sizing (#308)
1313+- Persistent tool mode — drawing tools stay active until Escape or manual switch (#309)
1414+- Marquee box selection — drag on empty canvas to multi-select shapes (#310)
1515+- Smoothed freehand drawing using Catmull-Rom spline interpolation (#311)
1616+- Visual arrow routing with edge anchors and snap-to-target hover feedback (#312)
1717+- Multi-shape move and batch delete operations
1818+- Space+drag and middle-click panning
1919+2020+### Changed
2121+- Diagram tool mode no longer auto-reverts to select after placing a shape
2222+- Shape selection clicks target shape center for sub-pixel robustness in E2E tests
2323+824## [0.20.0] — 2026-04-01
9251026### Added
···5773- Unit tests for mobile CSS media query rules (sidebar overlays, link tooltip, toolbar dropdowns)
58745975### Fixed
7676+- Fix E2E: stale SQLite CHECK constraint blocks new doc types (#305)
6077- Mobile: sidebars overlay content instead of pushing layout on tablet/phone (#271)
6178- Mobile: link preview tooltip constrained to viewport width
6279- Mobile: toolbar dropdowns use full-width fixed overlay on phones
+26-18
e2e/diagrams.spec.ts
···7373 // Shape should contain a rect element (rectangle kind)
7474 await expect(shape.locator('rect')).toBeAttached();
75757676- // Tool should revert to select after placing a shape
7777- await expect(page.locator('#tool-select')).toHaveClass(/active/);
7676+ // Tool stays active (persistent tool mode) — does NOT revert to select
7777+ await expect(page.locator('#tool-rectangle')).toHaveClass(/active/);
7878 });
79798080 test('add ellipse shape', async ({ page }) => {
···224224 await page.click('#tool-rectangle');
225225 await canvas.click({ position: { x: 300, y: 300 } });
226226227227- // Now we are back in select mode; click on the shape to select it
228228- // The shape was placed around (300,300) in screen coords; click same spot
229229- await canvas.click({ position: { x: 300, y: 300 } });
227227+ // Switch to select mode (tool stays active after creation)
228228+ await page.click('#tool-select');
229229+ // Click on the shape center to select it (shape is 120x80 at snapped pos)
230230+ await canvas.click({ position: { x: 360, y: 340 } });
230231231232 // Properties panel should be visible
232233 await expect(page.locator('#props-panel')).toBeVisible();
···237238 test('properties panel shows width and height inputs', async ({ page }) => {
238239 const canvas = page.locator('#diagram-canvas');
239240240240- // Add and select a rectangle
241241+ // Add a rectangle, switch to select, then select it at center
241242 await page.click('#tool-rectangle');
242243 await canvas.click({ position: { x: 300, y: 300 } });
243243- await canvas.click({ position: { x: 300, y: 300 } });
244244+ await page.click('#tool-select');
245245+ await canvas.click({ position: { x: 360, y: 340 } });
244246245247 await expect(page.locator('#props-panel')).toBeVisible();
246248 await expect(page.locator('#prop-width')).toBeVisible();
···251253 test('properties panel shows correct default dimensions', async ({ page }) => {
252254 const canvas = page.locator('#diagram-canvas');
253255254254- // Add and select a rectangle (default size is 120x80)
256256+ // Add a rectangle (default size is 120x80), switch to select, then select it at center
255257 await page.click('#tool-rectangle');
256258 await canvas.click({ position: { x: 300, y: 300 } });
257257- await canvas.click({ position: { x: 300, y: 300 } });
259259+ await page.click('#tool-select');
260260+ await canvas.click({ position: { x: 360, y: 340 } });
258261259262 await expect(page.locator('#props-panel')).toBeVisible();
260263 await expect(page.locator('#prop-width')).toHaveValue('120');
···264267 test('clicking empty canvas deselects shape and hides properties panel', async ({ page }) => {
265268 const canvas = page.locator('#diagram-canvas');
266269267267- // Add a rectangle and select it
270270+ // Add a rectangle, switch to select, then select it at center
268271 await page.click('#tool-rectangle');
269272 await canvas.click({ position: { x: 300, y: 300 } });
270270- await canvas.click({ position: { x: 300, y: 300 } });
273273+ await page.click('#tool-select');
274274+ await canvas.click({ position: { x: 360, y: 340 } });
271275 await expect(page.locator('#props-panel')).toBeVisible();
272276273277 // Click on an empty area of the canvas
···294298 await canvas.click({ position: { x: 300, y: 300 } });
295299 await expect(page.locator('.diagram-shape')).toHaveCount(1);
296300297297- // Select the shape
298298- await canvas.click({ position: { x: 300, y: 300 } });
301301+ // Switch to select mode and select the shape at center
302302+ await page.click('#tool-select');
303303+ await canvas.click({ position: { x: 360, y: 340 } });
299304 await expect(page.locator('.diagram-shape.selected')).toHaveCount(1);
300305301306 // Delete it
···309314 test('delete with keyboard (Delete key) removes selected shape', async ({ page }) => {
310315 const canvas = page.locator('#diagram-canvas');
311316312312- // Add and select a rectangle
317317+ // Add a rectangle, switch to select, then select it at center
313318 await page.click('#tool-rectangle');
314319 await canvas.click({ position: { x: 300, y: 300 } });
315315- await canvas.click({ position: { x: 300, y: 300 } });
320320+ await page.click('#tool-select');
321321+ await canvas.click({ position: { x: 360, y: 340 } });
316322 await expect(page.locator('.diagram-shape')).toHaveCount(1);
317323318324 // Press Delete key
···323329 test('delete with Backspace key removes selected shape', async ({ page }) => {
324330 const canvas = page.locator('#diagram-canvas');
325331326326- // Add and select a rectangle
332332+ // Add a rectangle, switch to select, then select it at center
327333 await page.click('#tool-rectangle');
328334 await canvas.click({ position: { x: 300, y: 300 } });
329329- await canvas.click({ position: { x: 300, y: 300 } });
335335+ await page.click('#tool-select');
336336+ await canvas.click({ position: { x: 360, y: 340 } });
330337 await expect(page.locator('.diagram-shape')).toHaveCount(1);
331338332339 // Press Backspace key
···337344 test('delete does nothing when no shape is selected', async ({ page }) => {
338345 const canvas = page.locator('#diagram-canvas');
339346340340- // Add a rectangle but do not select it (click empty space after)
347347+ // Add a rectangle, switch to select, click empty space to ensure nothing is selected
341348 await page.click('#tool-rectangle');
342349 await canvas.click({ position: { x: 300, y: 300 } });
350350+ await page.click('#tool-select');
343351 await canvas.click({ position: { x: 50, y: 50 } });
344352 await expect(page.locator('.diagram-shape')).toHaveCount(1);
345353 await expect(page.locator('.diagram-shape.selected')).toHaveCount(0);