mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
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
73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
import { createNewDocument, expect, test } from "./helpers";
|
|
|
|
test.describe("Editor Fill Dialog", () => {
|
|
test.beforeEach(async ({ editorPage: page }) => {
|
|
await createNewDocument(page);
|
|
});
|
|
|
|
test("Shift+Backspace opens fill dialog", async ({ editorPage: page }) => {
|
|
// Press Shift+Backspace to open the fill dialog
|
|
await page.keyboard.press("Shift+Backspace");
|
|
await page.waitForTimeout(500);
|
|
|
|
// The fill dialog should appear
|
|
const dialog = page.locator("div[role='dialog'][aria-label='Fill']");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// It should have the "Fill" heading
|
|
await expect(dialog.getByText("Fill", { exact: true })).toBeVisible();
|
|
});
|
|
|
|
test("fill dialog has color options", async ({ editorPage: page }) => {
|
|
// Open the fill dialog
|
|
await page.keyboard.press("Shift+Backspace");
|
|
await page.waitForTimeout(500);
|
|
|
|
const dialog = page.locator("div[role='dialog'][aria-label='Fill']");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Contents dropdown should be visible
|
|
await expect(dialog.getByText("Contents")).toBeVisible();
|
|
const contentsSelect = dialog.locator("select");
|
|
await expect(contentsSelect).toBeVisible();
|
|
|
|
// Check the available fill options
|
|
const options = contentsSelect.locator("option");
|
|
const texts = await options.allTextContents();
|
|
expect(texts).toContain("Foreground Color");
|
|
expect(texts).toContain("Background Color");
|
|
expect(texts).toContain("Color...");
|
|
expect(texts).toContain("White");
|
|
expect(texts).toContain("Black");
|
|
expect(texts).toContain("50% Gray");
|
|
|
|
// Opacity slider should be visible
|
|
await expect(dialog.getByText("Opacity")).toBeVisible();
|
|
const opacityRange = dialog.locator("input[type='range']");
|
|
await expect(opacityRange).toBeVisible();
|
|
|
|
// Preview swatch should be visible
|
|
await expect(dialog.getByText("Preview:")).toBeVisible();
|
|
|
|
// OK and Cancel buttons should be present
|
|
await expect(dialog.locator("button").filter({ hasText: "OK" })).toBeVisible();
|
|
await expect(dialog.locator("button").filter({ hasText: "Cancel" })).toBeVisible();
|
|
});
|
|
|
|
test("fill dialog can be closed with Escape", async ({ editorPage: page }) => {
|
|
// Open the fill dialog
|
|
await page.keyboard.press("Shift+Backspace");
|
|
await page.waitForTimeout(500);
|
|
|
|
const dialog = page.locator("div[role='dialog'][aria-label='Fill']");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Press Escape to close
|
|
await page.keyboard.press("Escape");
|
|
await page.waitForTimeout(300);
|
|
|
|
// Dialog should be gone
|
|
await expect(dialog).not.toBeVisible();
|
|
});
|
|
});
|