-
Notifications
You must be signed in to change notification settings - Fork 128
feat: Add canvas layout sync API for screenInstances (position, label, add/remove) #365
Description
Summary
The SDK can read project canvas layout via get_project → screenInstances, but provides no first-class API to write/sync screen placement, labels, or canvas membership back to Stitch. Integrators building infinite-canvas UIs must call undocumented REST endpoints directly — and those PATCH operations often fail with API keys, requiring OAuth instead.
Current state
Reading canvas data (works)
get_project returns screenInstances with placement metadata:
const raw = await stitch.callTool("get_project", { name: "projects/{projectId}" }); // raw.screenInstances[]: // { id, sourceScreen, label, x, y, width, height, hidden, type }
The README mentions project.data.screenInstances for apply_design_system, but there is no documented SDK method to update canvas state.
Writing canvas data (missing / broken for API keys)
There is no SDK method or MCP tool such as:
update_screen_instance— move/resize a screen on the canvassync_screen_to_canvas— add a screen to the project canvas with placementupdate_project_canvas— batch updatescreenInstancesupdate_screen_label— rename the canvas label for a screen
Integrators currently call the REST API directly:
PATCH https://stitch.googleapis.com/v1/projects/{projectId}?updateMask=screenInstances
Body: { "screenInstances": [...] }
Header: X-Goog-Api-Key: {STITCH_API_KEY}
This fails for many API-key users with:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token",
"status": "UNAUTHENTICATED"
}
}Concrete gaps
| Operation | Workaround today | Problem |
|---|---|---|
| Sync screen position (x, y, width, height) | Direct REST PATCH | OAuth-only for many accounts; not in SDK |
| Rename screen label on canvas | Direct REST PATCH screenInstances[].label |
Same |
| Add screen to canvas | Push new instance to screenInstances |
Same; some projects use screen IDs as instance IDs, breaking apply_design_system |
| Normalize invalid instance IDs | Manual UUID assignment + PATCH | Required before design system apply; no SDK helper |
| Remove screen from canvas (without delete) | Filter screenInstances + PATCH |
Same OAuth issue |
| Bidirectional layout sync | Read via get_project, write via REST |
No unified SDK abstraction |
Requested API
SDK methods on Project
interface ScreenInstance { id: string; // canvas instance id (UUID, not screen id) sourceScreen: string; // "projects/{projectId}/screens/{screenId}" label?: string; x: number; y: number; width: number; height: number; hidden?: boolean; } // Read (may already exist via project.data — document it) const layout = await project.getCanvasLayout(): Promise<ScreenInstance[]>; // Write await project.updateCanvasLayout(instances: ScreenInstance[]): Promise<void>; // Convenience helpers await project.addScreenToCanvas(screenId: string, placement: { x, y, width, height, label? }): Promise<ScreenInstance>; await project.updateScreenPlacement(screenId: string, placement: Partial<{ x, y, width, height, label }>): Promise<void>; await project.removeScreenFromCanvas(screenId: string): Promise<void>;
All methods should work with STITCH_API_KEY (BYOK).
MCP tools
| Tool | Description |
|---|---|
update_project_canvas |
Replace or patch screenInstances for a project |
update_screen_instance |
Update placement/label for one canvas instance |
Documentation
- Document the
screenInstancesschema in the README API reference - Clarify the difference between screen resource ID (
screens/{id}) and canvas instance ID (screenInstances/{uuid}) - Document which operations require OAuth vs API key
Expected behavior
import { stitch } from "@google/stitch-sdk"; const project = stitch.project("1234567890"); const screen = await project.generate("A login page", "MOBILE"); // Place on canvas at specific coordinates await project.addScreenToCanvas(screen.screenId, { x: 100, y: 200, width: 390, height: 844, label: "Login" }); // User drags screen in external canvas UI → sync back to Stitch await project.updateScreenPlacement(screen.screenId, { x: 300, y: 200 }); // Rename label visible on Stitch canvas await project.updateScreenPlacement(screen.screenId, { label: "Sign In" }); const layout = await project.getCanvasLayout(); expect(layout.find(i => i.sourceScreen.endsWith(screen.screenId))).toMatchObject({ x: 300, y: 200, label: "Sign In" });
Use case
We built an infinite-canvas UI designer on top of Stitch where:
- Users arrange screens on a local canvas (pan/zoom/drag)
- Screen positions are persisted locally and should sync to Stitch so the Stitch web UI shows the same layout
- Renaming a screen locally should update the Stitch canvas label
- New screens generated via SDK should appear on the Stitch project canvas (some projects only list screens in
screenInstances, not vialist_screens)
Without SDK canvas sync, we maintain a fragile REST workaround that fails for API-key-only users and requires manual fixes in the Stitch web UI.
Environment
@google/stitch-sdk^0.3.5- Auth:
STITCH_API_KEY(BYOK) - Node.js 22
Related
This complements #364 (delete project/screen). Canvas sync covers the update path for project screenInstances; delete covers resource removal.
Happy to share integration details or help test a fix.