From 87e8a8e314c52e870bda790ca31ad45a495d1e74 Mon Sep 17 00:00:00 2001 From: newblacc Date: Mon, 30 Mar 2026 20:56:46 +0200 Subject: [PATCH] fix: preserve textAlign/verticalAlign/containerId through REST round-trip ElementSharedFieldsSchema in server.ts was silently stripping textAlign, verticalAlign, and containerId on every POST/PATCH, causing bound text inside containers to lose centering after a sync round-trip. - Add missing fields to ElementSharedFieldsSchema (server.ts) - Add textAlign?, verticalAlign?, containerId? to ServerElement (types.ts) - Set textAlign: "center", verticalAlign: "top" on MCP subtitle elements (index.ts) Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 3 +++ src/index.ts | 2 ++ src/server.ts | 4 ++++ src/types.ts | 4 ++++ 4 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8c0c63..61d10ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Title/subtitle auto-injection for WS-delivered container elements: `handleCanvasChange()` now called explicitly after `element_created` updates scene (Excalidraw's `CaptureUpdateAction.NEVER` suppresses the `onChange` callback) - Curved arrow control points remain deformable after sync round-trip (merge strategy preserves Excalidraw internals) - E2E: `curved arrow stays deformable after sync round-trip` regression test passing +- `textAlign`, `verticalAlign`, `containerId` added to `ElementSharedFieldsSchema` in `server.ts` — Zod was silently stripping these fields on every REST round-trip, causing bound text to lose centering after sync +- `ServerElement` type updated with `textAlign?`, `verticalAlign?`, `containerId?` to match schema +- Subtitle element in MCP `create_element` now sets `textAlign: "center"` and `verticalAlign: "top"` - E2E: `new container arrival auto-injects title and subtitle text` now reliably passes with the double-WS fix ## [1.0.1] - 2026-03-29 diff --git a/src/index.ts b/src/index.ts index 880921c..e47df03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1159,6 +1159,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) text: effectiveSubtitle, fontSize: resolvedSubtitleSize, fontFamily: resolvedSubtitleFont, + textAlign: 'center', + verticalAlign: 'top', strokeColor: elementProps.strokeColor ?? '#1e1e1e', opacity: elementProps.opacity ?? 100, roughness: elementProps.roughness ?? USER_PREFS.roughness, diff --git a/src/server.ts b/src/server.ts index db90b36..5f97d8c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -487,6 +487,10 @@ const ElementSharedFieldsSchema = z.object({ endBinding: z.any().nullable().optional(), boundElements: z.any().nullable().optional(), elbowed: z.boolean().optional(), + // Text alignment properties (required for bound text inside containers) + textAlign: z.string().optional(), + verticalAlign: z.string().optional(), + containerId: z.string().nullable().optional(), // Image element properties fileId: z.string().optional(), status: z.string().optional(), diff --git a/src/types.ts b/src/types.ts index cf6aeaf..0a705b4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -143,6 +143,10 @@ export interface ServerElement extends Omit { end?: { id: string }; startBinding?: ExcalidrawBinding | null; endBinding?: ExcalidrawBinding | null; + // Text alignment (bound text inside containers) + textAlign?: string; + verticalAlign?: string; + containerId?: string | null; // Image element properties fileId?: string; status?: string;