From 1aedd641ba3dd30f069252bd3f008e23cb526c13 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 19:15:44 +0900 Subject: [PATCH 01/23] add upscale icon --- .../starterkit-icons/upscale.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx diff --git a/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx b/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx new file mode 100644 index 0000000000..b4bb39b835 --- /dev/null +++ b/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; + +export const UpscaleIcon = ({ ...props }: React.SVGProps) => { + return ( + + + + ); +}; From a367646e26cd8c55e93b2c8c80cfc36da7faad25 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 20:12:04 +0900 Subject: [PATCH 02/23] update vn package configuration --- .../grida-canvas-vn/__mocks__/svg-pathdata.ts | 34 ------------------- packages/grida-canvas-vn/__tests__/vn.test.ts | 9 ++--- packages/grida-canvas-vn/jest.config.ts | 17 ++++++++++ packages/grida-canvas-vn/package.json | 26 +++++++++----- packages/grida-canvas-vn/tsconfig.json | 10 ++++-- packages/grida-canvas-vn/vn.ts | 27 ++++++++------- 6 files changed, 61 insertions(+), 62 deletions(-) delete mode 100644 packages/grida-canvas-vn/__mocks__/svg-pathdata.ts create mode 100644 packages/grida-canvas-vn/jest.config.ts diff --git a/packages/grida-canvas-vn/__mocks__/svg-pathdata.ts b/packages/grida-canvas-vn/__mocks__/svg-pathdata.ts deleted file mode 100644 index c89b9a04c5..0000000000 --- a/packages/grida-canvas-vn/__mocks__/svg-pathdata.ts +++ /dev/null @@ -1,34 +0,0 @@ -export const SVGCommand = {} as any; -export function encodeSVGPath(commands: any[]): string { - // Simple implementation for testing - let result = ""; - for (const cmd of commands) { - switch (cmd.type) { - case 1: // MOVE_TO - result += `M${cmd.x} ${cmd.y} `; - break; - case 2: // LINE_TO - result += `L${cmd.x} ${cmd.y} `; - break; - case 3: // CURVE_TO - result += `C${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y} `; - break; - case 4: // CLOSE_PATH - result += "Z "; - break; - } - } - return result.trim(); -} -export class SVGPathData { - static MOVE_TO = 1; - static LINE_TO = 2; - static CURVE_TO = 3; - static CLOSE_PATH = 4; - - constructor(d?: string) {} - toAbs() { - return this; - } - commands: any[] = []; -} diff --git a/packages/grida-canvas-vn/__tests__/vn.test.ts b/packages/grida-canvas-vn/__tests__/vn.test.ts index 8fb2a00603..9ba8868a0c 100644 --- a/packages/grida-canvas-vn/__tests__/vn.test.ts +++ b/packages/grida-canvas-vn/__tests__/vn.test.ts @@ -532,11 +532,8 @@ describe("getLoopPathData", () => { // The path should start with M (move to), have L (line to) commands, and end with Z (close path) expect(pathData).toMatch( - /^M\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+Z$/ + /^M\d+\.?\d*\s*\d+\.?\d*(?:\s*L\d+\.?\d*\s*\d+\.?\d*){3}\s*[zZ]$/ ); - - // Verify the actual path data format - expect(pathData).toBe("M10 0 L5 10 L0 0 L10 0 Z"); }); it("should generate path data for a rectangle loop", () => { @@ -554,7 +551,7 @@ describe("getLoopPathData", () => { // Should generate a closed rectangular path expect(pathData).toMatch( - /^M\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+L\d+\.?\d*\s+\d+\.?\d*\s+Z$/ + /^M\d+\.?\d*\s*\d+\.?\d*(?:\s*L\d+\.?\d*\s*\d+\.?\d*){4}\s*[zZ]$/ ); }); @@ -593,6 +590,6 @@ describe("getLoopPathData", () => { // Verify the actual path data format includes a curve expect(pathData).toContain("C"); - expect(pathData).toContain("Z"); + expect(pathData).toMatch(/[zZ]/); }); }); diff --git a/packages/grida-canvas-vn/jest.config.ts b/packages/grida-canvas-vn/jest.config.ts new file mode 100644 index 0000000000..68a96fbe31 --- /dev/null +++ b/packages/grida-canvas-vn/jest.config.ts @@ -0,0 +1,17 @@ +import type { Config } from "jest"; + +const config: Config = { + // Needed because `svg-pathdata` is ESM-only ("type": "module") + preset: "ts-jest/presets/default-esm", + testEnvironment: "node", + testMatch: ["**/*.test.ts"], + collectCoverageFrom: [ + "**/*.ts", + "!**/*.d.ts", + "!**/node_modules/**", + "!dist/**", + ], + extensionsToTreatAsEsm: [".ts"], +}; + +export default config; diff --git a/packages/grida-canvas-vn/package.json b/packages/grida-canvas-vn/package.json index e2bb450ed4..7d4c90afcb 100644 --- a/packages/grida-canvas-vn/package.json +++ b/packages/grida-canvas-vn/package.json @@ -1,19 +1,29 @@ { "name": "@grida/vn", "description": "Vector Network Spec", + "version": "0.0.0", "private": true, "scripts": { - "test": "jest" + "dev": "tsup index.ts --format cjs,esm --dts --watch", + "build": "tsup index.ts --format cjs,esm --dts", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "NODE_OPTIONS=--experimental-vm-modules jest" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } }, "dependencies": { "@grida/cmath": "workspace:*", "svg-pathdata": "^7.2.0" - }, - "jest": { - "preset": "ts-jest", - "moduleNameMapper": { - "^@grida/cmath$": "/../grida-cmath/index.ts", - "^svg-pathdata$": "/__mocks__/svg-pathdata.ts" - } } } diff --git a/packages/grida-canvas-vn/tsconfig.json b/packages/grida-canvas-vn/tsconfig.json index 2f98042715..59ac2e289c 100644 --- a/packages/grida-canvas-vn/tsconfig.json +++ b/packages/grida-canvas-vn/tsconfig.json @@ -1,5 +1,11 @@ { "compilerOptions": { - "esModuleInterop": true - } + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2020", + "noImplicitAny": true, + "strict": true + }, + "exclude": ["dist", "node_modules"] } diff --git a/packages/grida-canvas-vn/vn.ts b/packages/grida-canvas-vn/vn.ts index 106ff4411f..cf4e170ece 100644 --- a/packages/grida-canvas-vn/vn.ts +++ b/packages/grida-canvas-vn/vn.ts @@ -2308,12 +2308,12 @@ export namespace vn { const end: Vector2 = [command.x, command.y]; const cubicControl1: Vector2 = [ - lastPoint[0] + ((2 / 3) * (control[0] - lastPoint[0])), - lastPoint[1] + ((2 / 3) * (control[1] - lastPoint[1])), + lastPoint[0] + (2 / 3) * (control[0] - lastPoint[0]), + lastPoint[1] + (2 / 3) * (control[1] - lastPoint[1]), ]; const cubicControl2: Vector2 = [ - end[0] + ((2 / 3) * (control[0] - end[0])), - end[1] + ((2 / 3) * (control[1] - end[1])), + end[0] + (2 / 3) * (control[0] - end[0]), + end[1] + (2 / 3) * (control[1] - end[1]), ]; const ta: Vector2 = [ @@ -2346,12 +2346,12 @@ export namespace vn { : [lastPoint[0], lastPoint[1]]; const cubicControl1: Vector2 = [ - lastPoint[0] + ((2 / 3) * (control[0] - lastPoint[0])), - lastPoint[1] + ((2 / 3) * (control[1] - lastPoint[1])), + lastPoint[0] + (2 / 3) * (control[0] - lastPoint[0]), + lastPoint[1] + (2 / 3) * (control[1] - lastPoint[1]), ]; const cubicControl2: Vector2 = [ - end[0] + ((2 / 3) * (control[0] - end[0])), - end[1] + ((2 / 3) * (control[1] - end[1])), + end[0] + (2 / 3) * (control[0] - end[0]), + end[1] + (2 / 3) * (control[1] - end[1]), ]; const ta: Vector2 = [ @@ -2375,7 +2375,8 @@ export namespace vn { const { rX, rY, xRot, lArcFlag, sweepFlag, x, y } = command; if (lastPoint) { - const [x1, y1] = lastPoint; + let currentPoint: Vector2 = lastPoint; + const [x1, y1] = currentPoint; // Convert arc to cubic Bézier curves const bezierCurves = cmath.bezier.a2c( @@ -2407,16 +2408,18 @@ export namespace vn { previousIndex, endIndex, [ - controlPoint1[0] - lastPoint[0], - controlPoint1[1] - lastPoint[1], + controlPoint1[0] - currentPoint[0], + controlPoint1[1] - currentPoint[1], ], // ta (relative to start) [controlPoint2[0] - endPoint[0], controlPoint2[1] - endPoint[1]] // tb (relative to end) ); // Update lastPoint and previousIndex for the next curve - lastPoint = endPoint; + currentPoint = endPoint; previousIndex = endIndex; } + + lastPoint = currentPoint; } lastQuadraticControl = null; break; From 80ee2bac0fa132a076b13e11799de602157f1995 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 20:22:24 +0900 Subject: [PATCH 03/23] tests config --- .../reducers/__tests__/history.test.ts | 3 - .../__tests__/image-paint-clipboard.test.ts | 13 ----- .../reducers/__tests__/vector-cut.test.ts | 2 - .../__tests__/vector-self-remove.test.ts | 12 ---- editor/jest.config.js | 2 +- editor/jest.setup.ts | 56 +++++++++++++++++++ 6 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 editor/jest.setup.ts diff --git a/editor/grida-canvas/reducers/__tests__/history.test.ts b/editor/grida-canvas/reducers/__tests__/history.test.ts index e610553c07..876d269bc4 100644 --- a/editor/grida-canvas/reducers/__tests__/history.test.ts +++ b/editor/grida-canvas/reducers/__tests__/history.test.ts @@ -1,6 +1,3 @@ -jest.mock("@grida/vn", () => ({}), { virtual: true }); -jest.mock("svg-pathdata", () => ({}), { virtual: true }); - import reducer, { type ReducerContext } from "../index"; import { DocumentHistoryManager } from "../../history-manager"; import { editor } from "@/grida-canvas"; diff --git a/editor/grida-canvas/reducers/__tests__/image-paint-clipboard.test.ts b/editor/grida-canvas/reducers/__tests__/image-paint-clipboard.test.ts index e9e238ba82..32776bf3ab 100644 --- a/editor/grida-canvas/reducers/__tests__/image-paint-clipboard.test.ts +++ b/editor/grida-canvas/reducers/__tests__/image-paint-clipboard.test.ts @@ -1,19 +1,6 @@ import documentReducer from "../document.reducer"; import grida from "@grida/schema"; -jest.mock("@grida/vn", () => { - class VectorNetworkEditor { - constructor(_net: any) {} - } - return { - __esModule: true, - default: { VectorNetworkEditor }, - VectorNetworkEditor, - }; -}); - -jest.mock("svg-pathdata", () => ({}), { virtual: true }); - jest.mock("../surface.reducer", () => ({ __esModule: true, default: jest.fn(), diff --git a/editor/grida-canvas/reducers/__tests__/vector-cut.test.ts b/editor/grida-canvas/reducers/__tests__/vector-cut.test.ts index 357562f34d..2c94691c37 100644 --- a/editor/grida-canvas/reducers/__tests__/vector-cut.test.ts +++ b/editor/grida-canvas/reducers/__tests__/vector-cut.test.ts @@ -1,7 +1,5 @@ import documentReducer from "../document.reducer"; -jest.mock("svg-pathdata", () => ({}), { virtual: true }); - jest.mock("../surface.reducer", () => ({ __esModule: true, default: jest.fn(), diff --git a/editor/grida-canvas/reducers/__tests__/vector-self-remove.test.ts b/editor/grida-canvas/reducers/__tests__/vector-self-remove.test.ts index 007ed8f01a..59fa137228 100644 --- a/editor/grida-canvas/reducers/__tests__/vector-self-remove.test.ts +++ b/editor/grida-canvas/reducers/__tests__/vector-self-remove.test.ts @@ -1,11 +1,3 @@ -jest.mock("@/grida-canvas", () => ({ - editor: { config: {} }, -})); - -jest.mock("@grida/cmath", () => ({}), { virtual: true }); -jest.mock("@grida/schema", () => ({}), { virtual: true }); -jest.mock("svg-pathdata", () => ({}), { virtual: true }); - jest.mock("../methods", () => ({ self_optimizeVectorNetwork: jest.fn(), self_try_remove_node: jest.fn((draft: any, id: string) => { @@ -14,10 +6,6 @@ jest.mock("../methods", () => ({ self_revert_tool: jest.fn(), })); -jest.mock("../tools/gesture", () => ({ - getInitialCurveGesture: jest.fn(), -})); - import surfaceReducer from "../surface.reducer"; describe("surface reducer - vector self remove", () => { diff --git a/editor/jest.config.js b/editor/jest.config.js index 3bfed5d3a6..3f29d6fb4b 100644 --- a/editor/jest.config.js +++ b/editor/jest.config.js @@ -11,7 +11,7 @@ const config = { coverageProvider: "v8", testEnvironment: "node", // Add more setup options before each test is run - // setupFilesAfterEnv: ['/jest.setup.ts'], + setupFilesAfterEnv: ["/jest.setup.ts"], moduleNameMapper: { // ... "^@/(.*)$": "/$1", diff --git a/editor/jest.setup.ts b/editor/jest.setup.ts new file mode 100644 index 0000000000..e8b1d1bbfc --- /dev/null +++ b/editor/jest.setup.ts @@ -0,0 +1,56 @@ +// Jest runs in a CJS runtime here, but `svg-pathdata` is ESM-only. +// Mock it so packages that import it (e.g. `@grida/vn`) can still be loaded in tests. +jest.mock("svg-pathdata", () => { + const SVGCommand = {} as any; + + function encodeSVGPath(commands: any[]): string { + // Minimal encoder used for unit tests that don't care about exact SVG formatting. + let result = ""; + for (const cmd of commands) { + switch (cmd.type) { + case 1: // MOVE_TO + result += `M${cmd.x} ${cmd.y}`; + break; + case 2: // LINE_TO + result += `L${cmd.x} ${cmd.y}`; + break; + case 3: // CURVE_TO + result += `C${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`; + break; + case 4: // CLOSE_PATH + result += "Z"; + break; + } + } + return result; + } + + class SVGPathData { + static MOVE_TO = 1; + static LINE_TO = 2; + static CURVE_TO = 3; + static CLOSE_PATH = 4; + + // `@grida/vn` uses these too + static HORIZ_LINE_TO = 5; + static VERT_LINE_TO = 6; + static QUAD_TO = 7; + static SMOOTH_QUAD_TO = 8; + static ARC = 9; + + commands: any[] = []; + + constructor(_d?: string) {} + + toAbs() { + return this; + } + } + + return { + __esModule: true, + SVGCommand, + encodeSVGPath, + SVGPathData, + }; +}); From de57faa54a5f7d4c18252a50c1b3f073f79bb1f9 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 20:26:33 +0900 Subject: [PATCH 04/23] update cursor assets. --- .../public/assets/css-cursors-grida/README.md | 40 +++++++++++------- .../ew-resize-64-x32y32-000000.png | Bin 0 -> 1733 bytes .../ew-resize-64-x32y32-000000.svg | 22 ++++++++++ .../ew-resize-scale-64-x32y32-000000.png | Bin 0 -> 2372 bytes .../ew-resize-scale-64-x32y32-000000.svg | 22 ++++++++++ .../nesw-resize-64-x32y32-000000.png | Bin 0 -> 1864 bytes .../nesw-resize-64-x32y32-000000.svg | 22 ++++++++++ .../nesw-resize-scale-64-x32y32-000000.png | Bin 0 -> 2645 bytes .../nesw-resize-scale-64-x32y32-000000.svg | 22 ++++++++++ .../ns-resize-64-x32y32-000000.png | Bin 0 -> 1873 bytes .../ns-resize-64-x32y32-000000.svg | 22 ++++++++++ .../ns-resize-scale-64-x32y32-000000.png | Bin 0 -> 2672 bytes .../ns-resize-scale-64-x32y32-000000.svg | 22 ++++++++++ .../nwse-resize-64-x32y32-000000.png | Bin 0 -> 1840 bytes .../nwse-resize-64-x32y32-000000.svg | 22 ++++++++++ .../nwse-resize-scale-64-x32y32-000000.png | Bin 0 -> 2608 bytes .../nwse-resize-scale-64-x32y32-000000.svg | 22 ++++++++++ 17 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 editor/public/assets/css-cursors-grida/ew-resize-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/ew-resize-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/ew-resize-scale-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/ew-resize-scale-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/nesw-resize-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/nesw-resize-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/nesw-resize-scale-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/nesw-resize-scale-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/ns-resize-scale-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/ns-resize-scale-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/nwse-resize-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/nwse-resize-64-x32y32-000000.svg create mode 100644 editor/public/assets/css-cursors-grida/nwse-resize-scale-64-x32y32-000000.png create mode 100644 editor/public/assets/css-cursors-grida/nwse-resize-scale-64-x32y32-000000.svg diff --git a/editor/public/assets/css-cursors-grida/README.md b/editor/public/assets/css-cursors-grida/README.md index 2af0ba0274..428a1264ed 100644 --- a/editor/public/assets/css-cursors-grida/README.md +++ b/editor/public/assets/css-cursors-grida/README.md @@ -1,32 +1,42 @@ # Grida Custom Cursors -License: Public Domain +License: Public Domain Original Author: Grida -Naming: +## Naming -## [`name`]-[`size`]-[x`X`y`Y`]-[`fill`].[`ext`] +`[name]-[size]-x[X]y[Y]-[fill].[ext]` -- `name` - the name of the cursor -- `size` - the size of the cursor (always square) -- `xXyY` - the x and y hot spot of the cursor -- `fill` - the fill color of the cursor in hex format (without #) -- `ext` - the extension of the cursor +- **name**: cursor name +- **size**: cursor image size (currently always square) +- **xXyY**: hotspot coordinates in the _image_ (x, y) +- **fill**: fill color in hex (without `#`) +- **ext**: file extension -## Examples +## Cursors -- `default-64-x28y28-000000.png` +| name | size | hotspot (image) | fill | png | svg | +| ------------------- | ---: | --------------: | -------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | +| `default` | `64` | `x28 y28` | `000000` | [`default-64-x28y28-000000.png`](./default-64-x28y28-000000.png) | [`default-64-x28y28-000000.svg`](./default-64-x28y28-000000.svg) | +| `ew-resize` | `64` | `x32 y32` | `000000` | [`ew-resize-64-x32y32-000000.png`](./ew-resize-64-x32y32-000000.png) | [`ew-resize-64-x32y32-000000.svg`](./ew-resize-64-x32y32-000000.svg) | +| `ew-resize-scale` | `64` | `x32 y32` | `000000` | [`ew-resize-scale-64-x32y32-000000.png`](./ew-resize-scale-64-x32y32-000000.png) | [`ew-resize-scale-64-x32y32-000000.svg`](./ew-resize-scale-64-x32y32-000000.svg) | +| `ns-resize` | `64` | `x32 y32` | `000000` | [`ns-resize-64-x32y32-000000.png`](./ns-resize-64-x32y32-000000.png) | [`ns-resize-64-x32y32-000000.svg`](./ns-resize-64-x32y32-000000.svg) | +| `ns-resize-scale` | `64` | `x32 y32` | `000000` | [`ns-resize-scale-64-x32y32-000000.png`](./ns-resize-scale-64-x32y32-000000.png) | [`ns-resize-scale-64-x32y32-000000.svg`](./ns-resize-scale-64-x32y32-000000.svg) | +| `nesw-resize` | `64` | `x32 y32` | `000000` | [`nesw-resize-64-x32y32-000000.png`](./nesw-resize-64-x32y32-000000.png) | [`nesw-resize-64-x32y32-000000.svg`](./nesw-resize-64-x32y32-000000.svg) | +| `nesw-resize-scale` | `64` | `x32 y32` | `000000` | [`nesw-resize-scale-64-x32y32-000000.png`](./nesw-resize-scale-64-x32y32-000000.png) | [`nesw-resize-scale-64-x32y32-000000.svg`](./nesw-resize-scale-64-x32y32-000000.svg) | +| `nwse-resize` | `64` | `x32 y32` | `000000` | [`nwse-resize-64-x32y32-000000.png`](./nwse-resize-64-x32y32-000000.png) | [`nwse-resize-64-x32y32-000000.svg`](./nwse-resize-64-x32y32-000000.svg) | +| `nwse-resize-scale` | `64` | `x32 y32` | `000000` | [`nwse-resize-scale-64-x32y32-000000.png`](./nwse-resize-scale-64-x32y32-000000.png) | [`nwse-resize-scale-64-x32y32-000000.svg`](./nwse-resize-scale-64-x32y32-000000.svg) | ## Usage +When using `url-set()` (1x/2x), the hotspot coordinates must match the _rendered_ cursor size. If you provide a `64px` cursor image as `2x`, the CSS hotspot is `(hotspot_in_image / 2)`. + ```css cursor: url-set( - url("https://grida.co/assets/css-cursors-grida/default-64-x28y28-000000.png") - 2x, - url("https://grida.co/assets/css-cursors-grida/default-64-x28y28-000000.png") - 1x + url("/assets/css-cursors-grida/default-64-x28y28-000000.png") 2x, + url("/assets/css-cursors-grida/default-64-x28y28-000000.png") 1x ) - /* (28/2) = (14) */ 14 14, + /* (28/2) = 14 */ 14 14, default; ``` diff --git a/editor/public/assets/css-cursors-grida/ew-resize-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/ew-resize-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..5acd3872c29c1997103e4660fcbdeed4d51eacee GIT binary patch literal 1733 zcmV;$20HnPP)CFC(sTSywx5KLcF(B`eskOUtRAA)%-A&|Tz zHa{fz)=+7vv=2qaLfVRmRq9`(R*nC^d;5LY89OfP?&@kIWIh<~y>n;Ip7YI_bI#lq z!oa}5z`(%3z`(%3z`(%3z`(%3AO={L!Ykad-ixJTO)USKvT#`CDYT> zQ(IeGKboDL{R#cn($dn)$;ruk_4W1pF~)P^#0e*j4FTJD8h-8CwY-s$ks8jWjQRQb z=RG|=Uu0xtr0_juh-IQ)9dtM6>TDH?t`#j0X z$!~CMC>awI6W`srbEh;jGc%c9Cdqsy-e4Oq-8u=%@4|}RREmy{j#^b!RaRPBngxTc zuC6Y-e_&wXI#;o*tjrZGkbQjD(b4fG$8t__aWP%Rs;H<~ks*nwd+^{vG2SIo2QAKQ zEwoj4<>uxl;F{jw-~Y98RpQ2t8@Aybu3C9{x&3ZzZ0vhH??8(W67AsjNlHq}8Xq73 zg=0BKmq8-2`YR+XEG+bNc6L^#rlzLTtEiJ$-WFR!1P!OlQBkf`b8~Y!l~4-7{cR<6 zrB%b~Dgv4k5)$4-pEpR+I0ibt;~0+RoKUj(8&94*S+%v=+uP4lC+a5S!@9CgB*TU7 z?vEZlI-u`!y5?HS1`~j7(t_uFnIUuq9a&jfNx1I9_Gu1BfNH_p0>s^~tE)ReIn>o! zXX;(kcnYCFMt8cqzR$7u!Ya820R__7RRo~Ys74)hBA7kUpCT75W&-T= z#;A+nR26QYM@s;Wl=*!X0qold0U|_v;6hR7n6m^Y%L2p?xBr8}8Qr5*ELsBiJF;*0 zMhFnvwjfZ0{LX+%-6IhYwwQ1@9AbKU+6ko-(cWPnR$pv3u3WidE9vogVscq{fsk?v zk(s>I-9aP5AYwHIcXEJHCj?IL87m`V#n8|YS8PQw8B0ibK*Rvl4fb_zZmu6XyHG(o zAv^4xumoVTQZmri)^?vaw{PDTa5WJN5Cm5}GuVO@0j0%8fx1B;J=*tueSJT|V*^g7 zbK``nqDK}e*+-8a{a|o#@K+URQ17pq_eF#lfWF`?5XTq**IjoE;5gPG3e*w(C1^WY zAjKL4d9h1WC|=-lxeh;m{P;Q!H!su=!XqLQ5tIfM0lN3uX^`)tC4gC|wzhV~aJc(q zLqo$icz2j`n6`0sTd|hNth0IWV97}m+-~=~XU?4Ytg*52TfG>%bm>ym9uT4qAQ@4m zK^o5AVjZFkr!I#M9r~EMQOA^&lni=qs6*F=Nx&tZo{*TBm=hl#UkGnFa_ZEn3dDvV zbRw9FA`nmzq7sagq!cIug3kgLd?nu1#C{5U>wK>imyqKiw9tSBm>%41w!gTkJ#x--rA6PoF-07N@fYO9PF+c=6)r9K%==;(Gw)v35^G+js8VxlgC2rvBDt zfQb5o2M>ON_wR8Ge^Y%JiN)}k*dh@pPo9ixYHD)hH0DK1f=i|a0)f<$l9C + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/ew-resize-scale-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/ew-resize-scale-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..3db39dce1ce1b1708ce1e9b47a88db689ff6fafc GIT binary patch literal 2372 zcmV-K3A^@*P) zRYe#-XZP;Iz58<8ORA>sC3Q?m&h zAV7cs0RjXF5FkK+009C7_`kunS`PN)$&>B<`}f-yE?jWs*4EZGu%0w&l4IMp(->in z!@t;+V?&O;Y11YP@*A2WtF>WsvY2Ej5{a~#KYxDY(4j-GA31X5wW6Y;?DF#RjA6ru zrLs@5@=#JxTzRu+&+bThYu2pkj<#^OZr##RH%4CTfD?d5Qn>Ka($dVfZQJIZJ$rV- zg9i_;ISTjh-~V&<>eW3+f?8Bka(ngal}7T;pFcm|(QxhBweMD}Sn)i%<)EZBlGv63 z2b9GtZRGCUxpVN%n>T-ziSFLL+c|dZnEM;h;@mZ z(OL=%3v;L#MMBG2gzo29SUy=N`!az=ix$O9u&%ByW`Y+lUfcz9eJLj==Lx(|i=!ns z;*((1#nAS9ckkY((|IaO1{}4v2|Ph`-t9 z2l(yh1qB7Ul;<@m)s{w*PoF;hg`XU5gEA%yEl&q6GcPZ%Es3P!nnHs7Ar-|ncd{!3 z&?L#eOn~N2?U*1L?5F$fmMvR`(iIrJ%|Y4Lu3fwE-o1NANq;VG+_-TunkM+H&%ulj zbc+)wPJE!dg_Ufebkv@XpW*fE*Y_85_wsXULyyj01nFABw)Q=q3#n<7beb*Qx;axpe8$JX(Xc4JRbQR&0frELl<|i%&F=7eRVqe*%{DTbnj* zB52DrCgb`((1Cr}S9dQVm?XGEFnGt40foCWbrFku8a!i*5>8s zCP97#T)uqycRZIC7Z+!l1gN$W?izjsSUx5%ZlPNZ=PD%7_2>>Naq?e1J1|fov3Xa7 zibiSfCYYeao2U-~W%PBE0F>;^%*;;glZXH*%sIT`gD69+1-Z(0JU^+F>uUuz%_KWJ zy9mtv%s+}Rvo_7$^wDGq;38E{5JLdl8cTrX6CY@imU0=ff|)aC_9e+)0XRAd<%ds! zv4i{*&4_|@v#05K`FV2)z$;s|b#4j)mU0Wat3duQuCi;_u8(tba|_TGlaj}n!^x8; zQ?b{Fkh*r|^PW9>+;#|y_JLVhh}2edkdl%Tj@A3c2du$%cQ&BSae5^2q3$7)0jK%2P>q;PDwK$Bv?*s)_{ z=EuCiQgK0mp#3TXJQFU<2XYYUfYa6c676M6Aqievjs(hN3Ba=uV7ecJV54>cuk**; zxpSjIT2l0*M~|*ePfvdaZFzC;B@{b_=-lJfsZ(3cebut8q%yHY7T}Qwkc_0aK{MjZ z)~EkR&s)Zf88d{t8^`s{2Azi)QmS%;lA4;DS8m+6an{S^y)rSvJgNB*Q09>drl@T& zE&*QWk3@$vWPx8oq0dBWqes7qd%nFQjG3RVX+O6x;{$z^ zK%m=*=Mv>=F^Leh19D2S75~V-5`UzaSzn^V8SfG{Y}oJ>-c@F0Wfhb3cu7zc%0x18 zGAl$->KY1#YL+iwJ_pIsIX{#4I;oK?w(F+2dGqE;oQ!20hDf|h!XgrX2;M4DhUw>t z4rgc1oY^;W#iy5lW98J9d2ICBr;>n1?eQe~%&M{RHo7P`Z(99)i?($tPKkx((tv znm05&rm86VB8YhBz<~oBbRy!#2V99ApD|+wC)JyQ$a-`K5CT#;S=1#Uyn5QSX%j?- zdG^rn90{8Hx^?U3;oSffYP}PQM$K3ga+-22CM1wO6}1Z(95Fd&7PSi&ESRdh1*5Wg zCMOt3;YK`&aQn+wS8RS)VtmJRuFKM;OUDbr`q{(W^O58KP+3_y1n>ILWqAWllc(|; zbra-6Li8$JK2|W9z;iiDFO&heh@9&cA0+Z4|2_okd6u_5NnA>BtW26NqW5Qx^Uul7 z%g=`n9m?dtFXf|-@H(BY+bSPSBsQq)rCdz1J?gHoh(5!I4}bgM!GphexUGZS`)gWG z+&ws!fDp+VIB;M!E@l1_7Z8x_>C>lw3ahF`Us_{{x^dP&lC;`~64x5!nw(cnc#cp5 zm{7kFBSzFgOWVaSvG^5uUqQ3K8pB3X_L`4iyGh_ZavgUzv}AhLDUepA=RFf(2Vl=iy0TmJ^c$Ak!p4=U1kX2wT1k#7N3@@8qPMKM__r2FuYgx{SQ8S|uan=s|EsN95Bm(-W$q$X~)ENr88jJi3pM9yP$ q^Z)?@1PBlyK!5-N0t5)~-^Aa$ndzC)Wa6#>0000 + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/nesw-resize-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/nesw-resize-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..45c08ffaaca103f7d781bac459231b734933c7b8 GIT binary patch literal 1864 zcmV-O2ej9Ry9twF2mNys!AaNJ4hKW1jG-_`R}__wmUR;u?w|RO zFxaCLu`(ZMVT!IWP}hkIPSh%^Sm(Q~eK*a%?|6=6A&E&C$&KC*4(HyR+uQSdp6~a& zR|pd(OqeiX!h{JECQO)k24N8fhuiIzYuvtlyPTa)r=0ih-RmZJY_V9};wgiLg0Eh^ zIv&T?qM{;uetv#(PEJnR(W6Hf;#wNdG26Cn+ZHEcz>t9;g;kcOFxJx2(nJcFot>TD z+S)p=zrX*(!NI{FT`t$4TVZ&3_=h7$jx45fl%iq>T3}jFE#8J`H7GE=!;Sl- zz`n-Hl`G}F@RV+x=V2s>Fg(C2A_!z_PVumM($%Y1-*-41ZM<;p+BI?DzyWdM#0fbd z)DxeTnVBhPgn>+y;!*xEJh(`EN=i!J)YMczElAfEeykj5?AfzNdcc`8XTD5IN-AJD zsU5-4h|A5*P2IV3=Q17NA`Ia<;6YLW9UUEYIG;@gh!{wM@mX0}8LL*Us?rL8D~Bw6 zMMZ@aespwnz-F_(oR*fBvt-EPPwUXJxCwT(5OpThF znrafN^78VqDDeLV3PMZM74O}<_qjK(pPZaL&x(zf!tL>%d$m@A4hLKc7!KGm*sZMcemQ@_97}yM6(8_73S))R@dypj}kmR!E5n6wyNjl!^IajHa5PC=13Q6 z{_}XoZ(UtoUqJY!v_9@h6OR-bRY-1s`0(L16p&2{;UjZyYQU$t7TZ(zJm-T>j}A{k~X?jK$9qqe!+D_5?3{iN;7TL~LCZuFTG)G;k8 zb2y%(l4LGM8eKo?rLS6O5^Hh9|7i-wE{zC4V*KRlLJ)d1%c5_1oyTKepVPWAcHMX$v-7LU%F0>@ zT!X(^25VPhE~CX+_Tt#V>W`cvS|2)u^$)q&-Me?&vHfM>%Ue~QZoD0 zsZ)KscI~=^+r|*bdW@rgKY`cZlej*KHrNzAXc{j^UGNN-3VJ@;`1_xNtlfnwTWL{v zLpj^j82p6gMb_-9lFMd%GjAyNX!WpmY=7FKYIpgO1LiCY2m5psST#1-vC-f0#Qe8B z{-Epkcq&Adt53DSf$+=;Y{G;I6DCZUFk!+(jN)H>o>yLDrt|3l0000 + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/nesw-resize-scale-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/nesw-resize-scale-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..2c362724801edf05bc00017c9b3459b94ee35622 GIT binary patch literal 2645 zcmV-b3aa&qP)6|P}=vPwq(f?>-O#2_VX}{hU)6-F@`3%2b4S)#l;;vc5EUf_7A68li*XQPT2&{ zo;`aIv5>k=Fx!F!3kJr?9AN?+156+TiF4=9Rbeb0Eoy%dH_*Wq6%}P(ym)cBEZ#{l8wsWckYFKb zIPe)U4>FQyZf>r@eLhW1e9qSnsw7nt5?{+jl0c;;M}leV!x1e0fa*`FbB=)HN;8kZ zuJL7hdU`SbJJnSTK$kyn-n@6Qr#}~>h9g+~fvCB&iNvW*N;8vyi9JhAO&!hcRP~#I zE8yeaj=pT&x^+t-rH4+UNicjxNCz0I@mXP((msPAgHuvc$_fh$v-n=+2541GLwgNv z($uL_7e9OUtc44vBt!Xwa7`Sen3I?QBFF1sz*sWCO*?@{M|zo1&(fVccdjrOA8iw9 z6EqW!iC;iu_*cAR4Br)r4;U#9S^M_w3!?Aw`Fx!dCQQgvUjd8)1ri<3pFiL3;b>1I z(CjTKDREykNG(7tP(WuW5{%U5)i4gw)QGur=T;z`9)&PF*-aWS0L1u# z&!0d4ITIWBf{2;G1W29*v38Hp*zS=cH}>@ObmKC>cfk~5su6^RrDJ<(7?R!-M(sR0 zCdlDl4Q+V0wzl?9vYidJ=PO47B##ASAe z>lD?$?|O%s%Z|m4BuG9N+tu6AGJ%mFL86rr0CE3MN=ll70!fh$gH>FJrz3-hO@kdz zkgN`B_pe*GZhb^%FzLagem>2UEqM}HU}kj!IHg6aR;?O~jBM7;n>T;PV1zL_p7%K!!2hD8m?K2W1da(L zB^Hgq+^x7DO>K-@07QwBs0$Y^{8F|dZb7mwF%dEId@||&{rjI_Y9@(Qy6zW>2{Z=` z56c8I36khhG-C7S&A&hr^t#r-_}D)e3mf|uwwu3%ioyi6PjO6IJ%7twz(`R&!UQ&l zPk4a|RB@$4T_IK?NYRLnj*e!iKTBx^n$a>qtcHcmuY7pD&gm#w=JcNl}i3MKj@d z!UQ!~LIX@-7z-wRzpk$Cb8865JB0CI%-_JgcQD3;D=rC;UBn~-n>KB#5a|e^lG>VM z;h11Ke1htCmNpa6zYUK*6G75u$s7?ov}68VUeoT)QrL+TCq4t?-lBk19IHV7?Ck7( zRCTKP(!+-jzmSEQsu_+67z@P&B|ZVS)PzznVJfaGpb?eGA>LoOaN);fgsBZw|21pY zRMIr{&hqjZz=hJeAjW91|WseE0$rOh#xAP9|hBrY9qCJ&rbx ze&*V>Yd?PQ;K3Kv44{7b^5q}WY^Yg!+kN@qqF73N!6LTo4nXIk5BUh%trR zn%>(B(cSoZ|eGi*`m~rca242?(q{q^d_~!-I~-o~4myXM0JlVNLfc`ro> zcP~B0nKnfc;9Bsx9lXUOiy%9go3d;bfrx$xJbAWQ*BLK3;r3gttJ5)5Fr z@FDhnV`F126oK|XA~qc{^g?*T@|JV|H*n1YIG z4;B8(hYv%2>Cx2G)NuCf**~MW@+q>qpJ2{RRCLBMtJ87*h#lBOqGl{t`BC;xhng1R zM5k=~_U$VW#C*UXkssKB5I}iYKSBFH5J$9JzkdA!O6he64jgE#udjcM;Hm=* z?eKcNU1+>+M|#{befsoX9HjQS=7UE|S8thb(?0p}0fygWxe7;7w?a8Ssx>mllY95> z-8N;)l%GS&Kf;_JfXHd+%US$HZSmWFup?m~s9hX`tRv!{pR1&CuCDa>+!E|W zy8!v1*#`;wVfQvMFbz9=g{|`$9!m$H)&Zz@0L%)aSv;*ZO6IOXOcKyrlt;e# + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..24a16b33ea2d4edd7f0e5482821c83da46fa238e GIT binary patch literal 1873 zcmV-X2d?;uP)nY%EoFJ?ZOz7Hl&N1NGgqqF|E<=3=x~SaHU4#!bPz$ zu?t&`(ln5e0*Q+PA+!O)0urFS-|yEjGq>M&xo4b!Fo1W)naTZbPkYHG^h z;NVqd6~@QMZ>FWCCF52bDCUH~^=%=8ot>TEOQx%K z+SJtKXI>Jmf#5y3PUSOiDl+H>GC)nbTrNjxX=%PR^^A;+H3ERy`;hJGw=k?jMXT^4L`t)ga z?Cb06n46npSk?Xah9t>=#(Y<{pPrs}U}M87kHZEaCFbtVgpXvvglqK?Tnl`b^9nxu>~{Mi1T$~7 zT4$jA+5G%`_v6Qpy+UV%B(b0nSoB1_&k7;fQSP!~{ramsQuFdNOkF|Vug+n~2w0B~ zg7cy}i(&~SKWY#F8JHU{HL5|n2UuQwoig0Mf z=TwY4Js!`d{vJY~a`NQKZgh*5u?zGd7w~FHLxgVySD@AcSq@|S3t{a~Wt*6pnfdkE zvu6kSF0o=2x+_fg7qW5#VnM`#0%w!OV%g7m0?E?h1z_vy z7SnrwZJL^zGBfSP+MnkHQd#l3z?PWrvN1~(kq#CwA%M5BhcBY`*yv!xL%hZkC@(L+ zj5+oQ;zXnuMzwXi3%j|4BO@bCWB|RsEen9PL;@}I7kn3-YDktqD4?DLh>wqdmjw|S z(zfCV11rMlClp|#ltsr%2dhMSRhan-)@mG^o4EYArKLr6hOG08O$tCBnn2jAK#kxd z%(+A~n7;&b0qwX;CJGr}AePUPA$EL35a#7!36|1|ii*C3CB4J;dQ`Q_X$5qKT(?-i zxhRd7%A7z15kP?ByNPEHupc0zsW&J=D_~DAAt7Nu%=i;o9Nnlx%zB?JhT{4tXn(*# ziRco5qV)(_&4m+t@9y2ZKWr2h1H;~}j(Eqn9+Z`xVFL34SV0~#x_{OS zi!$uBx3^c~`mj73AY$OioWD=NkS4=hphH|8_INKSCF;)zX^>&j#e}Azq2U+l5Si6tH3!rOQAWwa2<#Ui-eVxK7bX@ zFKKdel4k&2D0F<4o}Qiqp(NGS)x9`(?p!4*K|_m+i{toy4&)J$zW3iE%*+j(E8D|# z#(0c}K}TrzNO&?duWt|MahR|b7+?iHSOK-FY8PQ{4jH129U)YW+$eu7Wo2c#VIj-7 zPsA!rnq%Yt3UGfPr=9(=Ya5-|<@^=!m(7)z2dWJiFkrxdfiU8KVMcK3#n;6K00000 LNkvXXu0mjfg{pZM literal 0 HcmV?d00001 diff --git a/editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.svg b/editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.svg new file mode 100644 index 0000000000..15de873a7c --- /dev/null +++ b/editor/public/assets/css-cursors-grida/ns-resize-64-x32y32-000000.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/ns-resize-scale-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/ns-resize-scale-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..9e735d41a1894de79424efdf1e80c46b772cf29a GIT binary patch literal 2672 zcmV-$3Xk=PP)FJnuLIunIviv(8)ANgsO}9LxnU0ZAGbArHVf+?Wjl<0)C-Pg#b}O zQGbvYQ59%`3PD7xvQ;f0K!hE{rY2DmNPr{|XSH9y@A;nej>ic}Y{#~gPr7=~&(HQd z=brPOd(OF5Y6268CLDT14Nf&quq=xQ2aY1NbL-Zv_BoEufcM5tDdX}(S}?U_$r9(i zd-q%&9UZQoo}L6=^Z9%Zm&@gAZEf|Hm6Z*YmX>;t9zALi4u}0QOT{y^@Xpz@XD8<6 z<)z=bb7x^^XXoz*1_tUajo#kgJ6&B}ryo9i__LgxoQ&e);^f7P7rR7paSLts&gs*q zySH!OUQ8jacYumHc<|t?f`Wone3n)cpGx5DY2S7qKYn~>e}8`i1$_PbwT0`}{Q2{( ztgI|*$eKBGrnP+ea_iZ%XSNb}Z*6Vuck!OB1g*%pgA+cTgk!G95 z9!*bA&!Y>)4Ic>UVNBY$Z{NotxJC*i0#bMpJP=#9Y_ad5qU4^lXU{GndD040Jb<&W zO+TlhpABYv)y?gf;bSDsJLae)j&H8C+Y58q!ZGYv%A2@Je(5wZ4WM~RY zFhcM;-G-nWtzEnJBNAdPTY-aq%7qL}es!RrRD{4JJQ?fPum6&QBt!}!(w#ChGqbs% zQn9!;&@+=VY0{+067%WDN)w+coEjnwk}CxASy}_(^?Ez@?%n$duBFCYSk!)*z!|!I z`}XI7f||r9Bo}OmumoK~tUx%Ty}kYK42vXLDm+${U=!bzDN`ox-@m^iFzksRn|v_b z5R(t&4`a>nBV5ZMA*xsaXP+~InaRaN+G!kDb4owBIs3z{0%e%OnuKVEMZA6wBRT3; zpnseTl(gNucMlu(#HU9xL|8rJgONg%`Q*uyZMc@HqMavb-%n!L!>s)y&}>ISd}@fC zz_bE!r$vhv4HDwIb?ZK0+N`1(5vTyBt*ch8Di56fcZI!o4U;>}uqct@%$YMk<`P+r zV^!-bU^wDKxZUpQ$m2W3XE8g24{t7mM>|YH+_`h79YMqmPM$ou6iUg8DjDk7pPrnY zJoV9|M}M}0U@Wt*f>?ox?(Xg;DE$K}K>0an%m|0}2Aoc34{CW|T)K3rr=+B0USeWm z8ft#su#WfP5^Aids;aV&jg5^DP#>flc2Sch9cfV_ztW$ zUg7myjDDq}SU8nPz#IU_ufZLPlai83aX$C*<;w@H5OL+ol|SOUKH+!ryKz1lBTEXW zD#S?)O9&wsXg=Tr(XH^T$(aaOdB=_&`?S2gyb1!OkM#R!?kJrIDeM`; zx%~mWwvT*`b6&T}59eN-_rlcQqG!+!!8@i-o!Wi<`gIB}BpJSk4mW5G2ve^5`g%8% z;C}Pw4K0FF+l+15Jr9Q^=kR9>@7tfn{WKphjsuwO9%Pq2r~~vZT)5B+Uh(N>N!Spv zRg@Nz99i+jOg?NlR$bmz$gGMO5(}Ida4& z3X4ZLh8otFdx3*JLKOo|(+Nx^yqLRZJd5QlJRM*t9= z{H8_16Cxx561;|U5}pqQP@4qNE|+*8b|MU$0yLpfXnQRnVGtSJcd%;v37EY|jSlTl z#GfiEDo!vA8fyThqPg@921_?Yn40RYPznpAt*x!Uu|fp;gI_S~)5VWOE`N<^5i&x~ z1}Qvuejx8pK=PBfV8MbPzIye_^YrP{eq@8c51c_3GSnFB0a6>3VvC@qrRCRn zpSeYJE5q9FSrN{YEibI%n3dpA3L<8=h&4jlJ3z?Z0Ts=XnDhhItXWeg52*r*no`vr z%LrCdH*11;oe|ZxITfG_lhe$apbD5hR&!Md<*;B45~RuB(g{sY1T#4!d-m*ECtMKN-sZKDw#Ed(8mw2ZUi|}p>l?V1 zuh$$=&l+tjK;dEv&gWqDc9Rb(;m`D2=@||;f;Cix4`^X?=g$2;uDze1pPv>p4N}0~ z7=c>-G%QjUivWT9eN2)JHl+0x7(I+;NHu2vBV5bDh@FiRcs2*OvgyrC-$VuIFQyP9 z4U55uPx!#&@idcXaIFv{y74~{md*uemL#Xj%E}+~_4U0qXWR^HLXv6(VNbAZ*)qoc z85miyWcnrsSB0$MU$}7Lw?bsW2+<6S=01X8`-;$ZJA>Jur7bKuW;aSnUI2U@4_txP z)nhZ1nPHK_QBZSxtZ5zQy(?C%_zad+g6rwwXof@#mOnR0)C-gu!NTIMUAwHI5G?Fn zyLPQchdq1CtcvwwDb15%5$4KmEVEv`cu{BiH5FlQj1E+#nrMpW=hhnKLH=64~sl+U}cj_|a#we>2$*@wIbU^qm4Ma4b=lrS)F-n<@|b0^B> zZ5YiEya__rBa3XnZH-V&6G-y~LSK7%dAay}M1>!BSpWY!jom-c`Mey-7tWafS4fy4 eZE`h%DB*uah^LxSZSc1M0000 + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/nwse-resize-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/nwse-resize-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..3d9206cb688b529cce270f76d31ba4ada4dca82f GIT binary patch literal 1840 zcmV-02haG4P)p^(6ZA8JCfAru>ZSTJg^ z4-%o2^kv2jS;R)oXjws!Ia^Nc&6j-Fv)}LXJaTrsPVWu8=d|;KKj)sO+wuJ0e$W5^ zf36TVY}l}2!-fqTHf-3iVIwf;y6#X?US1xKB}$laU6>qUB1llVLx&DI04*aUBVyC0 zO)HKaJLXAFPLAe%PEL-isHjLgc<`VyhqH#axuCnY7tc|4w|yu3VDNlA&0YJ$CdGypG`FJEq?0w+$KSdsyJg{eZ!&dv@YqFyF1VBP$jz!Zpzidxaw z*!Zy`KM{lhDC7^~$dMx(83n=$X~0m8jg3uqI-LdB1xBa<$Sh#xnG%X9L4<`^i(PIq z4C;@;Q#zEN3JVJxNB(wj4Y@4>1-vd`1OX%be)jCyAz_8^mvE2=A|fJIpFe;8OL~wj zF9PsBd4qWR^y%Mw_UtJGsjgzdWSP2vJb+7DadGi`f%1S-espxSf5(m;l`fZSJyPl= zNrKiuVPT;&E-o$!FaO%u*jOJG;+`)8P=5RN?Vlo*@=2(SD65so-fGb8cIVu>b?X$B zV!vIyE&l+F$8Qud_j_udiQY zY_k;}J$m#Nz81@fFheBJ3#{I<+S=Oc`GTzM*c;FgssZw+JpRwWT45T3%1biLVjHcV zcl78{Z~vfh>C&Z-7#+sc2??kGw?m{s8k8&S?d`qj>oF={Sy{=jFJRa!<$Ye7oZ(Ad}4_Xp+LwQDVih6?=K>tuUe zqx=j(RL)24-@kvY(eu9LHTi`=8$FEsReWB^(?CT5xd69oB)e~fyo=9o;hGCc6_V|J zl~?U5$d_<)!o#)}dCvVY18ZqCjGFo^q&&lZDaupG z2gIE_cfMsB$OsJmw#YD+TbhdsQ`Fo~ZCT0yYLDl4mg;#WBv%51*WYlT4v8>j1*tDE z3RlY0^ZWMg3rKlk-~j?VOTL^TgbOP`Mu2ymPQqJ=?H3cZy`DL9<`c<1j6Cyqku-qJ z*(7Xbr-D&l7`DTE_wM~b&XGltFg=*7tOKg5s@AJ84rY?Xu9H9n{YA26M2R3HU|Gon z=LnAe{{C?E04cn`d-twa0>y<37X%_mpcD0M-MTdy#g-GiVu@BdJTWmbe#@3EMJ#6M zMINMx03$#ccmR{>THGf=w2&7?i-9SC!_!!JF8k`$tKTaQV8g`i&0igecb!gTq%uQ51hQsi~<^^bntePu8(1lJ!Nv2&KimRUrkupkYtf z;86|dgieTiZf=et?S`U}FbMNI5~;8*H#av8sWE)|^yw}TX%Et95If88?%lg55nXd4 zK!iMGSi$jkHu7DRQ8erp8s>mb%n{reySoYW4UC#Fq|FRZrC_uX6jo!{2_~^VBVG{9 zM~3V!-5j2>eqg%bKAGnc#vfOR=YZKs!(q72e}-Ya=ab+O<5|T24%Yi^kqsL*Y}l}2 e!-fqT=D|Ojiq|r^C=<;90000 + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/public/assets/css-cursors-grida/nwse-resize-scale-64-x32y32-000000.png b/editor/public/assets/css-cursors-grida/nwse-resize-scale-64-x32y32-000000.png new file mode 100644 index 0000000000000000000000000000000000000000..382de5e3b2ea5ccd047bb160dfaae29a653b042b GIT binary patch literal 2608 zcmV-03eWY4P)o({b3b~MG{}X`WQQ?W2-VP7=>E<%LtZI zsr3QHqEZ#nA}Oe`p)%mBQJe`r!iYvAYTo8ya_0Na-7B*_xye0=bFU}E{a|z6=j^ri zx7J>3?W0rz2_%p}0tqCLz}E=NveZ~XK#dX*(#P$N9XouHF6&14jQz0>Ex2#t!i9n9 z)2F9QoH#KpH#avUJ3D(k&X3Q}&rioTgcd|gs;sQ^i?YT%D9DNxE0VCAvUl&^Nli^n zpLBI~{kN~LuLajEZeP86RomR${MTEzZY`Yvm>V>8WdLYdhM|&`_b7K}GnLmq41**0IrkUUk7Fc0#os5=;PFAy%96?^<))aK(#*IVT zzUeb4Fa=kJBKRBu-%y3)2`U_)yn6(3)GfC8x~3Q!SjpX?BvJ9q9tAQ1RIzB3uu({->F7Yyrcd_JEF z27~?B?T44H=Q(uVzIN%-C3_3{51ez0ix)2{FhU(ZeAqrtPfvg4_xoG$t#%yt9zT9O zZlPi{Z)>4r78LQizP|n%Pb^!u%)US&1Ym_44Q|}HVQ;5Rn>HEW=>*HVF#3Hs)vvsQ z;^Jb?0{M#qu~|UDXbLGQDc`~A z?^19UGw6w>yv_R}MQ9>CI~vXABQ?_xIHS>>J9n1R55%;fVhX?j1<0oDQE+3leWEbu zv)K4K@)X|m_HKk%2IWmZAkpZZJ9qwo;~Z_Ou?vkUm=-j^kLeFaT?94O7;;BC^3pgh z3M3j8rGN?l!EqtKVObtrZXecD)kw`;kX42sU_5~1$&zp>FMw3!6v=FoX{{9n(H2#e z;RwyVK!D@vj0fBVM6rvT4WQ8J$LJ?tzI^#72%cs&G&HD9n>N|>Eh{S9-sq$s&~z;>wQ{!>ukyf^)SRCA$l( zT+!T=SJoI9C^2G0hWI1txY)ep`(~5XW!^K?>M~*es9`MDRe4fsafesyBb91xp@JdQb6j)(9LnKul z=g*)2^U|eD|AEnOEGQ^wMbOow!iw??e7b0vN^{_X2=*t_EI`{57E+*)g*f-)UAuOD za`o!fGpPGVoib4nt=qP3+s|2q;Q}k?|B=)K;%Xygf-k)I$E+|3Hk*V3E33^xBr?q4 zk`X6xg_w=(2w|ji!I08<^XAO~JEp>!=Fx0WZ@gb!U0u6%>(>9lte!)8FY%rhgu>nM z6@B=OtI>aFPQp+Inc-*X3iAkH8VfL%hnXZJq~&@cTPl?UCD~~=gzdvV7i>E*<+Wk- z+aP@Bj2Sa}U@rW>z*E$B16vWY45q?$Ju-k*-fUe_E@G;y)7?q>e}Wv|!z@!j_s#ez zTYXh3r%)VqDkMbvX61=n3QeE#wtu6K35|(>^6!9Ul-k_ddIAX~kU# + + + + + + + + + + + + + + + + + + + + + From 572957bcba21e11a4c60bcaf62142ed8b755f183 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 20:32:59 +0900 Subject: [PATCH 05/23] scale tool --- editor/components/cursor/cursor-data.ts | 25 +++++++++++ .../playground/uxhost-toolbar.tsx | 1 + .../starterkit-icons/upscale.tsx | 1 + .../starterkit-toolbar/index.tsx | 8 ++-- .../starterkit-toolbar/utils.ts | 5 +++ editor/grida-canvas-react/provider.tsx | 1 + .../grida-canvas-react/viewport/hotkeys.tsx | 9 ++++ .../grida-canvas-react/viewport/surface.tsx | 24 +++++++++-- .../viewport/ui/surface-vector-editor.tsx | 4 +- editor/grida-canvas/editor.i.ts | 41 +++++++++++++++++++ editor/grida-canvas/reducers/methods/tool.ts | 9 +++- 11 files changed, 118 insertions(+), 10 deletions(-) diff --git a/editor/components/cursor/cursor-data.ts b/editor/components/cursor/cursor-data.ts index e49cfeaf9c..a3827a94ae 100644 --- a/editor/components/cursor/cursor-data.ts +++ b/editor/components/cursor/cursor-data.ts @@ -77,6 +77,31 @@ export namespace cursors { return `data:image/svg+xml;base64,${btoa(svgData)}`; }; + /** + * Scale tool (K) cursor set — uses designed resize cursors. + * + * Files live under `editor/public/assets/css-cursors-grida/`. + */ + const _ew_resize_scale_png_url = + "/assets/css-cursors-grida/ew-resize-scale-64-x32y32-000000.png"; + const _ns_resize_scale_png_url = + "/assets/css-cursors-grida/ns-resize-scale-64-x32y32-000000.png"; + const _nesw_resize_scale_png_url = + "/assets/css-cursors-grida/nesw-resize-scale-64-x32y32-000000.png"; + const _nwse_resize_scale_png_url = + "/assets/css-cursors-grida/nwse-resize-scale-64-x32y32-000000.png"; + + export const resize_handle_scale_cursor_map = { + nw: pngsetcss(_nwse_resize_scale_png_url, 32, 32, "auto"), + n: pngsetcss(_ns_resize_scale_png_url, 32, 32, "auto"), + ne: pngsetcss(_nesw_resize_scale_png_url, 32, 32, "auto"), + e: pngsetcss(_ew_resize_scale_png_url, 32, 32, "auto"), + se: pngsetcss(_nwse_resize_scale_png_url, 32, 32, "auto"), + s: pngsetcss(_ns_resize_scale_png_url, 32, 32, "auto"), + sw: pngsetcss(_nesw_resize_scale_png_url, 32, 32, "auto"), + w: pngsetcss(_ew_resize_scale_png_url, 32, 32, "auto"), + } as const; + export const resize_handle_cursor_map = { nw: "nwse-resize", n: "ns-resize", diff --git a/editor/grida-canvas-hosted/playground/uxhost-toolbar.tsx b/editor/grida-canvas-hosted/playground/uxhost-toolbar.tsx index 915e7ae490..9757b21d09 100644 --- a/editor/grida-canvas-hosted/playground/uxhost-toolbar.tsx +++ b/editor/grida-canvas-hosted/playground/uxhost-toolbar.tsx @@ -69,6 +69,7 @@ export function PlaygroundToolbar() { options={[ { value: "cursor", label: "Cursor", shortcut: "V" }, { value: "hand", label: "Hand tool", shortcut: "H" }, + { value: "scale", label: "Scale tool", shortcut: "K" }, ]} onValueChange={(v) => { editor.surface.surfaceSetTool( diff --git a/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx b/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx index b4bb39b835..2c16a070ac 100644 --- a/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx +++ b/editor/grida-canvas-react-starter-kit/starterkit-icons/upscale.tsx @@ -6,6 +6,7 @@ export const UpscaleIcon = ({ ...props }: React.SVGProps) => { xmlns="http://www.w3.org/2000/svg" width="24" height="24" + viewBox="0 0 24 24" fill="none" {...props} > diff --git a/editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx b/editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx index 9ab89b94a6..bcc12d0732 100644 --- a/editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx +++ b/editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx @@ -14,13 +14,12 @@ import { StarIcon, } from "@radix-ui/react-icons"; import { BrushIcon, LassoIcon, PenToolIcon, TriangleIcon } from "lucide-react"; +import { UpscaleIcon } from "../starterkit-icons/upscale"; import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, - DropdownMenuCheckboxItem, - DropdownMenuItem, DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; @@ -148,6 +147,7 @@ export default function Toolbar() { options={[ { value: "cursor", label: "Cursor", shortcut: "V" }, { value: "hand", label: "Hand tool", shortcut: "H" }, + { value: "scale", label: "Scale tool", shortcut: "K" }, ]} onValueChange={(v) => { editor.surface.surfaceSetTool( @@ -258,7 +258,7 @@ export function ToolsGroup({ {options.map((option) => ( {/*
*/} - + {option.label} {option.shortcut && ( @@ -283,6 +283,8 @@ export function ToolIcon({ switch (type) { case "cursor": return ; + case "scale": + return ; case "hand": return ; case "container": diff --git a/editor/grida-canvas-react-starter-kit/starterkit-toolbar/utils.ts b/editor/grida-canvas-react-starter-kit/starterkit-toolbar/utils.ts index 0e3f0fae9c..f1359ed60d 100644 --- a/editor/grida-canvas-react-starter-kit/starterkit-toolbar/utils.ts +++ b/editor/grida-canvas-react-starter-kit/starterkit-toolbar/utils.ts @@ -2,6 +2,7 @@ import type { editor } from "@/grida-canvas"; export type ToolbarToolType = | "cursor" + | "scale" | "hand" | "rectangle" | "ellipse" @@ -27,6 +28,8 @@ export function toolmode_to_toolbar_value( case "cursor": case "zoom": return "cursor"; + case "scale": + return "scale"; case "hand": return "hand"; case "insert": @@ -56,6 +59,8 @@ export function toolbar_value_to_cursormode( switch (tt) { case "cursor": return { type: "cursor" }; + case "scale": + return { type: "scale" }; case "hand": return { type: "hand" }; case "container": diff --git a/editor/grida-canvas-react/provider.tsx b/editor/grida-canvas-react/provider.tsx index 0a661a5eb7..18680d99f1 100644 --- a/editor/grida-canvas-react/provider.tsx +++ b/editor/grida-canvas-react/provider.tsx @@ -453,6 +453,7 @@ export function useEventTargetCSSCursor() { } switch (tool.type) { case "cursor": + case "scale": return cursors.default_png.css; case "hand": return "grab"; diff --git a/editor/grida-canvas-react/viewport/hotkeys.tsx b/editor/grida-canvas-react/viewport/hotkeys.tsx index e5a1b3354d..858c305435 100644 --- a/editor/grida-canvas-react/viewport/hotkeys.tsx +++ b/editor/grida-canvas-react/viewport/hotkeys.tsx @@ -232,6 +232,11 @@ export const keybindings_sheet = [ description: "Select tool", keys: ["v"], }, + { + name: "scale", + description: "Scale tool (parametric scaling)", + keys: ["k"], + }, { name: "lasso", description: "Lasso tool (vector mode)", @@ -897,6 +902,10 @@ export function useEditorHotKeys() { editor.surface.surfaceSetTool({ type: "cursor" }); }); + useHotkeys("k", () => { + editor.surface.surfaceSetTool({ type: "scale" }); + }); + useHotkeys("q", () => { if (content_edit_mode?.type === "vector") { editor.surface.surfaceSetTool({ type: "lasso" }); diff --git a/editor/grida-canvas-react/viewport/surface.tsx b/editor/grida-canvas-react/viewport/surface.tsx index 269226ad78..e86b826ce1 100644 --- a/editor/grida-canvas-react/viewport/surface.tsx +++ b/editor/grida-canvas-react/viewport/surface.tsx @@ -1016,7 +1016,8 @@ function SelectionGroupOverlay({ const { style, ids, boundingSurfaceRect, size, distribution } = groupdata; - const enabled = !readonly && tool.type === "cursor"; + const enabled = + !readonly && (tool.type === "cursor" || tool.type === "scale"); const bind = useSurfaceGesture( { @@ -1199,7 +1200,8 @@ function NodeOverlay({ const tool = useToolState(); // enable overlay dragging only when the cursor tool is active and editable - const enabled = !readonly && tool.type === "cursor"; + const enabled = + !readonly && (tool.type === "cursor" || tool.type === "scale"); const bind = useSurfaceGesture( { @@ -1531,6 +1533,7 @@ function LayerOverlayResizeHandle({ onDoubleClick?: () => void; disabled?: boolean; }) { + const tool = useToolState(); const bind = useSurfaceGesture( { onPointerDown: ({ event }) => { @@ -1559,7 +1562,16 @@ function LayerOverlayResizeHandle({ {...bind()} anchor={anchor} zIndex={zIndex} - transform={disabled ? { pointerEvents: "none" } : undefined} + transform={ + tool.type === "scale" + ? { + cursor: cursors.resize_handle_scale_cursor_map[anchor], + ...(disabled ? { pointerEvents: "none" } : undefined), + } + : disabled + ? { pointerEvents: "none" } + : undefined + } /> ); } @@ -1579,6 +1591,7 @@ function LayerOverlayResizeSide({ onDoubleClick?: () => void; disabled?: boolean; }) { + const tool = useToolState(); const bind = useSurfaceGesture( { onPointerDown: ({ event }) => { @@ -1628,7 +1641,10 @@ function LayerOverlayResizeSide({ style={{ position: "absolute", background: "transparent", - cursor: cursors.resize_handle_cursor_map[anchor], + cursor: + tool.type === "scale" + ? cursors.resize_handle_scale_cursor_map[anchor] + : cursors.resize_handle_cursor_map[anchor], touchAction: "none", zIndex, ...positionalStyle, diff --git a/editor/grida-canvas-react/viewport/ui/surface-vector-editor.tsx b/editor/grida-canvas-react/viewport/ui/surface-vector-editor.tsx index 64ee646577..2640e4608c 100644 --- a/editor/grida-canvas-react/viewport/ui/surface-vector-editor.tsx +++ b/editor/grida-canvas-react/viewport/ui/surface-vector-editor.tsx @@ -100,7 +100,9 @@ export function SurfaceVectorEditor({ )}
- {tool.type === "cursor" && } + {(tool.type === "cursor" || tool.type === "scale") && ( + + )} {/* Render all segments */} {segments.map((s, i) => { diff --git a/editor/grida-canvas/editor.i.ts b/editor/grida-canvas/editor.i.ts index 568612bc55..e8390e8f33 100644 --- a/editor/grida-canvas/editor.i.ts +++ b/editor/grida-canvas/editor.i.ts @@ -672,6 +672,14 @@ export namespace editor.state { | { type: "cursor"; } + | { + /** + * Scale tool (K) — parametric scaling. + * + * Note: this is a tool mode, distinct from the transform gesture type `"scale"`. + */ + type: "scale"; + } | { type: "hand"; } @@ -1799,6 +1807,39 @@ export namespace editor.gesture { readonly initial_snapshot: editor.state.IMinimalDocumentState; readonly initial_rects: cmath.Rectangle[]; readonly direction: cmath.CardinalDirection; + + /** + * Gesture mode. + * - `resize`: regular resize behavior (default) + * - `parametric`: Scale tool (K) — parameter-space scaling + */ + readonly mode?: "resize" | "parametric"; + + /** + * Initial selection bounding rectangle (union of `initial_rects`). + * Used as the reference for parametric scaling. + */ + readonly initial_bounding_rect?: cmath.Rectangle; + + /** + * Affected node ids for parametric scaling (selection + descendants). + */ + readonly affected_ids?: string[]; + + /** + * Initial absolute rectangles (canvas space) cached at gesture start. + * Keyed by node id. + */ + readonly initial_abs_rects_by_id?: Record; + + /** + * For nodes whose parent is outside `affected_ids`, this caches the parent's + * initial absolute rect (canvas space), keyed by node id. + */ + readonly initial_external_parent_abs_rects_by_id?: Record< + string, + cmath.Rectangle + >; }; export type GestureInsertAndResize = Omit & { diff --git a/editor/grida-canvas/reducers/methods/tool.ts b/editor/grida-canvas/reducers/methods/tool.ts index 95c6763b55..dd147234a8 100644 --- a/editor/grida-canvas/reducers/methods/tool.ts +++ b/editor/grida-canvas/reducers/methods/tool.ts @@ -8,12 +8,16 @@ import grida from "@grida/schema"; const VECTOR_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = [ "cursor", + "scale", "hand", "bend", "path", "lasso", ]; -const TEXT_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = ["cursor"]; +const TEXT_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = [ + "cursor", + "scale", +]; const BITMAP_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = [ "brush", "eraser", @@ -21,6 +25,7 @@ const BITMAP_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = [ ]; const NO_CONTENT_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = [ "cursor", + "scale", "hand", "zoom", "insert", @@ -29,7 +34,7 @@ const NO_CONTENT_EDIT_MODE_VALID_TOOL_MODES: editor.state.ToolModeType[] = [ ]; // when reverting a tool while no content edit mode is active, path is invalid const NO_CONTENT_EDIT_MODE_VALID_REVERT_TOOL_MODES: editor.state.ToolModeType[] = - ["cursor", "hand", "zoom", "insert", "draw"]; + ["cursor", "scale", "hand", "zoom", "insert", "draw"]; function validToolsForContentEditMode( mode: editor.state.ContentEditModeState["type"] | undefined, From 3b4d8b64707e56c43938c574937d8185d2db9aa3 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 20:56:46 +0900 Subject: [PATCH 06/23] update normal resize cursor --- editor/components/cursor/cursor-data.ts | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/editor/components/cursor/cursor-data.ts b/editor/components/cursor/cursor-data.ts index a3827a94ae..e67069fb1a 100644 --- a/editor/components/cursor/cursor-data.ts +++ b/editor/components/cursor/cursor-data.ts @@ -42,6 +42,15 @@ export namespace cursors { css: pngsetcss(_default_png_url, 28, 28), }; + const _ew_resize_png_url = + "/assets/css-cursors-grida/ew-resize-64-x32y32-000000.png"; + const _ns_resize_png_url = + "/assets/css-cursors-grida/ns-resize-64-x32y32-000000.png"; + const _nesw_resize_png_url = + "/assets/css-cursors-grida/nesw-resize-64-x32y32-000000.png"; + const _nwse_resize_png_url = + "/assets/css-cursors-grida/nwse-resize-64-x32y32-000000.png"; + export const default_svg = { data: ( width: number = 32, @@ -103,15 +112,15 @@ export namespace cursors { } as const; export const resize_handle_cursor_map = { - nw: "nwse-resize", - n: "ns-resize", - ne: "nesw-resize", - e: "ew-resize", - se: "nwse-resize", - s: "ns-resize", - sw: "nesw-resize", - w: "ew-resize", - }; + nw: pngsetcss(_nwse_resize_png_url, 32, 32, "nwse-resize"), + n: pngsetcss(_ns_resize_png_url, 32, 32, "ns-resize"), + ne: pngsetcss(_nesw_resize_png_url, 32, 32, "nesw-resize"), + e: pngsetcss(_ew_resize_png_url, 32, 32, "ew-resize"), + se: pngsetcss(_nwse_resize_png_url, 32, 32, "nwse-resize"), + s: pngsetcss(_ns_resize_png_url, 32, 32, "ns-resize"), + sw: pngsetcss(_nesw_resize_png_url, 32, 32, "nesw-resize"), + w: pngsetcss(_ew_resize_png_url, 32, 32, "ew-resize"), + } as const; function pngsetcss( url: string, From 065e516082f4c8f939573165c02b9f01731ee12e Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 14 Dec 2025 21:21:34 +0900 Subject: [PATCH 07/23] chore: compact label --- editor/scaffolds/sidecontrol/ui/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/scaffolds/sidecontrol/ui/index.tsx b/editor/scaffolds/sidecontrol/ui/index.tsx index 89e9c1c07c..791931b88c 100644 --- a/editor/scaffolds/sidecontrol/ui/index.tsx +++ b/editor/scaffolds/sidecontrol/ui/index.tsx @@ -48,7 +48,7 @@ export function PropertyLineLabel({ return (