mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: resolve 41 bugs and wire 21 unimplemented features in image editor
Canvas rendering: - Fix Konva filter application order (filters before cache) - Implement 6 missing filters (motionBlur, radialBlur, surfaceBlur, vignette, grain, sharpen) - Implement exposure, vibrance, warmth adjustments as custom Konva filters - Apply layer blend modes via globalCompositeOperation - Apply object effects (drop shadow, outer glow, stroke) to all shapes - Mount SmartGuidesOverlay during move tool drag - Clip pixel grid to visible viewport (200-line cap for performance) Store logic: - resizeImage now scales all objects proportionally (points, radii, fontSize) - rotate/flip/trim handle line/arrow points arrays and center-based objects - applyCrop creates cropped source image via offscreen canvas - invertSelection creates mask from bounds when no mask exists - cutObjects uses single atomic set() to prevent race conditions - sendToBack respects layer ordering in multi-layer documents - Add batchNudge() and commitHistory() for undoable nudge operations - Add updateLayerThumbnail() method Tool hooks: - Fix clone stamp/dodge/burn perf (toDataURL only on mouseUp, not every move) - Fix magic wand zoom/pixelRatio with explicit stage.toCanvas() viewport - Fix eyedropper sampling with unzoomed canvas export - Fix selection tool stale closure via isDrawingRef - Implement polygonal lasso (click-to-place vertices, double-click to close) - Implement selection subtract mode (geometric and mask-based) - Implement gradient live preview during drag - Fix transform/move tool to persist changes and handle ellipse/polygon/star UI wiring: - Mount rulers and guidelines in editor page - Wire histogram with live canvas imageData - Wire autosave recovery with blob-to-dataURL conversion - Wire fill dialog to Shift+Backspace shortcut - Wire eyedropper and transform options to options bar - Fix history panel undo/redo button reactive state via useSyncExternalStore - Fix layer row name click to select layer (timer-based click/dblclick) - Fix zoom animation coordinate drift with progressive store sync - Fix copy merged to use Konva stage composite export Tests: - 49 new unit tests (store fixes + konva filters) - 8 new E2E test files with 39 test cases
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { createNewDocument, expect, test } from "./helpers";
|
||||
|
||||
test.describe("Editor Rulers and Guides", () => {
|
||||
test.beforeEach(async ({ editorPage: page }) => {
|
||||
await createNewDocument(page);
|
||||
});
|
||||
|
||||
test("rulers are hidden by default", async ({ editorPage: page }) => {
|
||||
// The ruler canvases render only when rulersVisible is true.
|
||||
// By default rulers are hidden, so the ruler-specific canvases
|
||||
// (with cursor-col-resize / cursor-row-resize) should not be present.
|
||||
const horizontalRuler = page.locator("canvas.cursor-col-resize");
|
||||
const verticalRuler = page.locator("canvas.cursor-row-resize");
|
||||
|
||||
await expect(horizontalRuler).toHaveCount(0);
|
||||
await expect(verticalRuler).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("Ctrl+R toggles ruler visibility", async ({ editorPage: page }) => {
|
||||
// Initially hidden
|
||||
const horizontalRuler = page.locator("canvas.cursor-col-resize");
|
||||
await expect(horizontalRuler).toHaveCount(0);
|
||||
|
||||
// Press Ctrl+R to show rulers
|
||||
await page.keyboard.press("Control+r");
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Now they should appear
|
||||
await expect(page.locator("canvas.cursor-col-resize")).toBeVisible();
|
||||
await expect(page.locator("canvas.cursor-row-resize")).toBeVisible();
|
||||
|
||||
// Press Ctrl+R again to hide
|
||||
await page.keyboard.press("Control+r");
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator("canvas.cursor-col-resize")).toHaveCount(0);
|
||||
await expect(page.locator("canvas.cursor-row-resize")).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("horizontal ruler appears at top edge", async ({ editorPage: page }) => {
|
||||
// Enable rulers
|
||||
await page.keyboard.press("Control+r");
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const horizontalRuler = page.locator("canvas.cursor-col-resize");
|
||||
await expect(horizontalRuler).toBeVisible();
|
||||
|
||||
// Ruler should have a fixed height of 20px (RULER_SIZE)
|
||||
const box = await horizontalRuler.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.height).toBe(20);
|
||||
|
||||
// Ruler should stretch to full width (w-full class)
|
||||
expect(box!.width).toBeGreaterThan(100);
|
||||
});
|
||||
|
||||
test("vertical ruler appears at left edge", async ({ editorPage: page }) => {
|
||||
// Enable rulers
|
||||
await page.keyboard.press("Control+r");
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const verticalRuler = page.locator("canvas.cursor-row-resize");
|
||||
await expect(verticalRuler).toBeVisible();
|
||||
|
||||
// Ruler should have a fixed width of 20px (RULER_SIZE)
|
||||
const box = await verticalRuler.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.width).toBe(20);
|
||||
|
||||
// Ruler should stretch to fill the available height
|
||||
expect(box!.height).toBeGreaterThan(100);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user