From d52eee99cffaa3f11b9043ff09a034ba8b384394 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 21 Dec 2025 02:41:38 +0900 Subject: [PATCH 01/10] feat: add sitemap configuration to Docusaurus blog and docs, and update dependencies --- apps/blog/docusaurus.config.ts | 6 ++++++ apps/blog/package.json | 1 + apps/docs/docusaurus.config.ts | 6 ++++++ pnpm-lock.yaml | 3 +++ 4 files changed, 16 insertions(+) diff --git a/apps/blog/docusaurus.config.ts b/apps/blog/docusaurus.config.ts index d45e2a263c..68baefce62 100644 --- a/apps/blog/docusaurus.config.ts +++ b/apps/blog/docusaurus.config.ts @@ -49,6 +49,12 @@ const config: Config = { theme: { customCss: "./src/css/custom.css", }, + sitemap: { + changefreq: "monthly", + priority: 0.5, + lastmod: "date", + ignorePatterns: ["/tags/**", "/page/**"], + }, } satisfies Preset.Options, ], ], diff --git a/apps/blog/package.json b/apps/blog/package.json index 4adbc2ad75..72bfb792a7 100644 --- a/apps/blog/package.json +++ b/apps/blog/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@docusaurus/core": "3.7.0", + "@docusaurus/plugin-sitemap": "3.7.0", "@docusaurus/preset-classic": "3.7.0", "@docusaurus/theme-mermaid": "3.7.0", "@mdx-js/react": "^3.1.0", diff --git a/apps/docs/docusaurus.config.ts b/apps/docs/docusaurus.config.ts index ea6a78d1a9..d36344d74d 100644 --- a/apps/docs/docusaurus.config.ts +++ b/apps/docs/docusaurus.config.ts @@ -144,6 +144,12 @@ const config: Config = { trackingID: "G-T0S919XJ07", anonymizeIP: true, }, + sitemap: { + changefreq: "weekly", + priority: 0.5, + lastmod: "date", + ignorePatterns: ["/tags/**"], + }, }, ], ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 374ff121a9..b80af7767b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,6 +123,9 @@ importers: '@docusaurus/core': specifier: 3.7.0 version: 3.7.0(@mdx-js/react@3.1.0(@types/react@19.1.3)(react@19.2.1))(acorn@8.15.0)(esbuild@0.25.4)(eslint@9.27.0(jiti@2.4.2))(lightningcss@1.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.8.3) + '@docusaurus/plugin-sitemap': + specifier: 3.7.0 + version: 3.7.0(@mdx-js/react@3.1.0(@types/react@19.1.3)(react@19.2.1))(acorn@8.15.0)(esbuild@0.25.4)(eslint@9.27.0(jiti@2.4.2))(lightningcss@1.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.8.3) '@docusaurus/preset-classic': specifier: 3.7.0 version: 3.7.0(@algolia/client-search@5.20.2)(@mdx-js/react@3.1.0(@types/react@19.1.3)(react@19.2.1))(@types/react@19.1.3)(acorn@8.15.0)(esbuild@0.25.4)(eslint@9.27.0(jiti@2.4.2))(lightningcss@1.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(search-insights@2.17.3)(typescript@5.8.3) From 963734465ca30fde3a7df446410304ac2ee6c336 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 21 Dec 2025 02:44:53 +0900 Subject: [PATCH 02/10] update blog logo --- apps/blog/docusaurus.config.ts | 8 +++++--- apps/blog/package.json | 1 + apps/blog/static/img/navbar-logo-dark.svg | 9 +++++++++ apps/blog/static/img/navbar-logo.svg | 9 +++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 apps/blog/static/img/navbar-logo-dark.svg create mode 100644 apps/blog/static/img/navbar-logo.svg diff --git a/apps/blog/docusaurus.config.ts b/apps/blog/docusaurus.config.ts index 68baefce62..ff9a71ce9f 100644 --- a/apps/blog/docusaurus.config.ts +++ b/apps/blog/docusaurus.config.ts @@ -63,9 +63,11 @@ const config: Config = { image: "img/social-card.png", navbar: { logo: { - alt: "Grida", - src: "img/logo.svg", - srcDark: "img/logo-on-dark.svg", + alt: "Grida Logo", + src: "img/navbar-logo.svg", + srcDark: "img/navbar-logo-dark.svg", + href: "https://grida.co/", + target: "_self", }, items: [ { diff --git a/apps/blog/package.json b/apps/blog/package.json index 72bfb792a7..3fa3983506 100644 --- a/apps/blog/package.json +++ b/apps/blog/package.json @@ -5,6 +5,7 @@ "scripts": { "docusaurus": "docusaurus", "start": "docusaurus start", + "dev": "docusaurus start", "build": "docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", diff --git a/apps/blog/static/img/navbar-logo-dark.svg b/apps/blog/static/img/navbar-logo-dark.svg new file mode 100644 index 0000000000..caec2b8ed9 --- /dev/null +++ b/apps/blog/static/img/navbar-logo-dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/blog/static/img/navbar-logo.svg b/apps/blog/static/img/navbar-logo.svg new file mode 100644 index 0000000000..e433221089 --- /dev/null +++ b/apps/blog/static/img/navbar-logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + From 6a1c251300f19f64516a12c1da4f0b0a0a9182b0 Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 21 Dec 2025 04:02:33 +0900 Subject: [PATCH 03/10] chore: ui --- editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx | 2 +- editor/scaffolds/sidecontrol/chunks/scale-tool.tsx | 2 +- editor/scaffolds/sidecontrol/controls/blend-mode.tsx | 2 +- editor/scaffolds/sidecontrol/controls/color-picker.tsx | 2 +- editor/scaffolds/sidecontrol/controls/font-size.tsx | 2 +- .../scaffolds/sidecontrol/sidecontrol-document-properties.tsx | 4 ++-- editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx b/editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx index f78445bf2a..e7dbd49c15 100644 --- a/editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx +++ b/editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx @@ -389,7 +389,7 @@ export function ChunkPaints({ {title} {isCanvasBackend && ( - diff --git a/editor/scaffolds/sidecontrol/chunks/scale-tool.tsx b/editor/scaffolds/sidecontrol/chunks/scale-tool.tsx index 861fc89747..2f9147eca1 100644 --- a/editor/scaffolds/sidecontrol/chunks/scale-tool.tsx +++ b/editor/scaffolds/sidecontrol/chunks/scale-tool.tsx @@ -181,7 +181,7 @@ export function ScaleToolSection({ diff --git a/editor/scaffolds/sidecontrol/controls/color-picker.tsx b/editor/scaffolds/sidecontrol/controls/color-picker.tsx index 9a77365598..2836c54158 100644 --- a/editor/scaffolds/sidecontrol/controls/color-picker.tsx +++ b/editor/scaffolds/sidecontrol/controls/color-picker.tsx @@ -65,7 +65,7 @@ export function ColorPicker32F({ title={isSupported ? "eye dropper" : "eye dropper not supported"} disabled={!isSupported} variant="ghost" - size="xs" + size="icon" className="text-muted-foreground" onClick={handleEyeDropperPick} > diff --git a/editor/scaffolds/sidecontrol/controls/font-size.tsx b/editor/scaffolds/sidecontrol/controls/font-size.tsx index 6a2d996633..f68cf47449 100644 --- a/editor/scaffolds/sidecontrol/controls/font-size.tsx +++ b/editor/scaffolds/sidecontrol/controls/font-size.tsx @@ -20,7 +20,7 @@ export function FontSizeControl({ onValueCommit?: (change: editor.api.NumberChange) => void; }) { return ( -
+
- @@ -188,7 +188,7 @@ function PropertyDefinitionBlock({ @@ -1841,7 +1841,7 @@ function SectionEffects({ node_id }: { node_id: string }) { Effects - From ea25a6fdc318a34bb5d5183803527caa303c714a Mon Sep 17 00:00:00 2001 From: Universe Date: Sun, 21 Dec 2025 04:02:39 +0900 Subject: [PATCH 04/10] feat: enhance corner radius control with preset options and improved UI --- .../sidecontrol/controls/corner-radius.tsx | 118 ++++++++++++++++-- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/editor/scaffolds/sidecontrol/controls/corner-radius.tsx b/editor/scaffolds/sidecontrol/controls/corner-radius.tsx index ca4ad9ff93..2dc07f2418 100644 --- a/editor/scaffolds/sidecontrol/controls/corner-radius.tsx +++ b/editor/scaffolds/sidecontrol/controls/corner-radius.tsx @@ -8,10 +8,61 @@ import { CornerBottomRightIcon, CornerBottomLeftIcon, CornersIcon, + ChevronDownIcon, } from "@radix-ui/react-icons"; import grida from "@grida/schema"; import { Toggle } from "@/components/ui/toggle"; import { cn } from "@/components/lib/utils"; +import { + Select, + SelectContent, + SelectItem, +} from "@/components/ui-editor/select"; +import * as SelectPrimitive from "@radix-ui/react-select"; + +/** + * Tailwind CSS border-radius preset values (uniform shorthands only). + * + * @see https://tailwindcss.com/docs/border-radius + */ +const twradius = { + "rounded-none": { + "border-radius": 0, + name: "none", + }, + "rounded-xs": { + "border-radius": 2, + name: "xs", + }, + "rounded-sm": { + "border-radius": 4, + name: "sm", + }, + "rounded-md": { + "border-radius": 6, + name: "md", + }, + "rounded-lg": { + "border-radius": 8, + name: "lg", + }, + "rounded-xl": { + "border-radius": 12, + name: "xl", + }, + "rounded-2xl": { + "border-radius": 16, + name: "2xl", + }, + "rounded-3xl": { + "border-radius": 24, + name: "3xl", + }, + "rounded-4xl": { + "border-radius": 32, + name: "4xl", + }, +} as const; function isUniform(value: grida.program.nodes.i.IRectangularCornerRadius) { const _tl = value.rectangular_corner_radius_top_left; @@ -109,22 +160,65 @@ export function CornerRadius4Control({ onValueCommit?.(newCorners as cg.CornerRadius4); }; + const hasPreset = useMemo(() => { + if (mode !== "all" || uniformValue === undefined) return false; + return Object.values(twradius).some( + (preset) => preset["border-radius"] === uniformValue + ); + }, [mode, uniformValue]); + return (
{/* First Row: Uniform Input + Toggle Button */}
- +
+ + {mode === "all" && ( +
+ +
+ )} +
Date: Sun, 21 Dec 2025 04:40:33 +0900 Subject: [PATCH 05/10] implement resize handle visibility logic for small nodes --- editor/grida-canvas-react/ui-config.ts | 17 ++ .../grida-canvas-react/viewport/surface.tsx | 210 ++++++------------ editor/grida-canvas/ASSERTIONS.md | 12 +- 3 files changed, 98 insertions(+), 141 deletions(-) diff --git a/editor/grida-canvas-react/ui-config.ts b/editor/grida-canvas-react/ui-config.ts index af50fc87da..8cc8547f23 100644 --- a/editor/grida-canvas-react/ui-config.ts +++ b/editor/grida-canvas-react/ui-config.ts @@ -32,6 +32,23 @@ export const MIN_NODE_OVERLAY_GAP_VISIBLE_UI_SIZE = 100; */ export const MIN_NODE_OVERLAY_PADDING_VISIBLE_UI_SIZE = 100; +/** + * Minimum node size in UI space (pixels) required to display resize handles. + * When nodes are smaller than this threshold (zoomed out), resize handles are hidden + * to prioritize translate-drag region, making it easier to drag thin nodes like text. + * + * @remarks + * The value should theoretically be calculated as: `(knob_width * n) + (n - 1) * gap` + * where: + * - `knob_width` is the physical size of each resize knob + * - `n` is the number of knobs on a single axis (usually 2 for left/right or top/bottom, or 3 for left/center/right or top/center/bottom) + * - `gap` is the spacing between knobs + * + * This ensures handles don't collapse or conflict with each other before they disappear. + * Only consider a single axis (width or height) when calculating this value. + */ +export const MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE = 12; + /** * Border width in pixels for the dropzone highlight indication. * This controls the visual thickness of the border shown when dragging nodes diff --git a/editor/grida-canvas-react/viewport/surface.tsx b/editor/grida-canvas-react/viewport/surface.tsx index 14f2e55183..f96426e3ce 100644 --- a/editor/grida-canvas-react/viewport/surface.tsx +++ b/editor/grida-canvas-react/viewport/surface.tsx @@ -75,6 +75,7 @@ import { MIN_NODE_OVERLAY_GAP_VISIBLE_UI_SIZE, MIN_NODE_OVERLAY_PADDING_VISIBLE_UI_SIZE, MIN_RESIZE_HANDLE_SIZE_FOR_DIAGONAL_PRIORITY_UI_SIZE, + MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE, DROPZONE_BORDER_WIDTH, } from "../ui-config"; import { @@ -1046,6 +1047,13 @@ function SelectionGroupOverlay({ const nsweZIndex = show_side_handles_as_important ? 22 : 11; const diagonalZIndex = 21; + // Calculate viewport size for resize handle visibility + const rect_ui_width = size[0] * scaleX; + const rect_ui_height = size[1] * scaleY; + const show_resize_handles = + rect_ui_width >= MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE && + rect_ui_height >= MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE; + return ( <> - { - editor.surface.surfaceStartScaleGesture(ids, "n"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "s"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "e"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "w"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "n"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "s"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "e"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "w"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "nw"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "ne"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "sw"); - }} - /> - { - editor.surface.surfaceStartScaleGesture(ids, "se"); - }} - /> + {show_resize_handles && ( + <> + { + editor.surface.surfaceStartScaleGesture(ids, "n"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "s"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "e"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "w"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "nw"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "ne"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "sw"); + }} + /> + { + editor.surface.surfaceStartScaleGesture(ids, "se"); + }} + /> + + )} {/* */} = MIN_NODE_OVERLAY_CORNER_RADIUS_VISIBLE_UI_SIZE && rect_ui_height >= MIN_NODE_OVERLAY_CORNER_RADIUS_VISIBLE_UI_SIZE; + const show_resize_handles = + rect_ui_width >= MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE && + rect_ui_height >= MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE; + const show_aspect_ratio_guide = gesture.type === "scale" && gesture.selection.includes(node_id) && @@ -1303,7 +1287,7 @@ function NodeOverlay({ > {focused && !readonly && ( <> - {is_resizable_node && ( + {is_resizable_node && show_resize_handles && ( <> {node.type === "line" ? ( <> @@ -1323,22 +1307,6 @@ function NodeOverlay({ }} onDoubleClick={handleSideDoubleClickHorizontal} /> - { - editor.surface.surfaceStartScaleGesture(node_id, "e"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(node_id, "w"); - }} - disabled={true} - /> ) : ( <> @@ -1374,38 +1342,6 @@ function NodeOverlay({ }} onDoubleClick={handleSideDoubleClickHorizontal} /> - { - editor.surface.surfaceStartScaleGesture(node_id, "n"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(node_id, "s"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(node_id, "e"); - }} - disabled={true} - /> - { - editor.surface.surfaceStartScaleGesture(node_id, "w"); - }} - disabled={true} - />