From f5bd2425b6491be58b43c398ac1343e4d8249fe6 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sat, 6 Jun 2026 11:34:33 +0300 Subject: [PATCH] feat(kiwi): scaffold standalone Kiwi package --- bun.lock | 20 +- oxlint.json | 3 +- package.json | 8 +- packages/kiwi/README.md | 20 + packages/kiwi/package.json | 55 + packages/kiwi/scripts/smoke-dist.ts | 17 + packages/kiwi/src/fig/index.ts | 2 + packages/kiwi/src/fig/protocol.ts | 237 + packages/kiwi/src/fig/schema.ts | 7 + packages/kiwi/src/fig/schema/fig.kiwi | 5623 ++++++++++++++++++ packages/kiwi/src/global.d.ts | 4 + packages/kiwi/src/index.ts | 2 + packages/kiwi/src/schema-runtime/bb.ts | 252 + packages/kiwi/src/schema-runtime/binary.ts | 102 + packages/kiwi/src/schema-runtime/index.ts | 12 + packages/kiwi/src/schema-runtime/js.ts | 339 ++ packages/kiwi/src/schema-runtime/parser.ts | 287 + packages/kiwi/src/schema-runtime/schema.ts | 24 + packages/kiwi/src/schema-runtime/util.ts | 10 + packages/kiwi/src/schema-runtime/validate.ts | 88 + packages/kiwi/tests/fig-schema.test.ts | 22 + packages/kiwi/tests/protocol.test.ts | 56 + packages/kiwi/tests/schema-runtime.test.ts | 55 + packages/kiwi/tsconfig.json | 14 + packages/kiwi/tsconfig.test.json | 7 + packages/kiwi/tsdown.config.ts | 56 + 26 files changed, 7311 insertions(+), 11 deletions(-) create mode 100644 packages/kiwi/README.md create mode 100644 packages/kiwi/package.json create mode 100644 packages/kiwi/scripts/smoke-dist.ts create mode 100644 packages/kiwi/src/fig/index.ts create mode 100644 packages/kiwi/src/fig/protocol.ts create mode 100644 packages/kiwi/src/fig/schema.ts create mode 100644 packages/kiwi/src/fig/schema/fig.kiwi create mode 100644 packages/kiwi/src/global.d.ts create mode 100644 packages/kiwi/src/index.ts create mode 100644 packages/kiwi/src/schema-runtime/bb.ts create mode 100644 packages/kiwi/src/schema-runtime/binary.ts create mode 100644 packages/kiwi/src/schema-runtime/index.ts create mode 100644 packages/kiwi/src/schema-runtime/js.ts create mode 100644 packages/kiwi/src/schema-runtime/parser.ts create mode 100644 packages/kiwi/src/schema-runtime/schema.ts create mode 100644 packages/kiwi/src/schema-runtime/util.ts create mode 100644 packages/kiwi/src/schema-runtime/validate.ts create mode 100644 packages/kiwi/tests/fig-schema.test.ts create mode 100644 packages/kiwi/tests/protocol.test.ts create mode 100644 packages/kiwi/tests/schema-runtime.test.ts create mode 100644 packages/kiwi/tsconfig.json create mode 100644 packages/kiwi/tsconfig.test.json create mode 100644 packages/kiwi/tsdown.config.ts diff --git a/bun.lock b/bun.lock index 3a7e47b51..7505b7256 100644 --- a/bun.lock +++ b/bun.lock @@ -105,6 +105,7 @@ }, "dependencies": { "@open-pencil/core": "workspace:*", + "@open-pencil/dom-css": "workspace:*", "agentfmt": "^0.1.3", "canvaskit-wasm": "^0.40.0", "citty": "^0.1.6", @@ -173,6 +174,15 @@ "@open-pencil/core": "workspace:*", }, }, + "packages/kiwi": { + "name": "@open-pencil/kiwi", + "version": "0.13.2", + "devDependencies": { + "@types/bun": "^1.3.14", + "tsdown": "^0.21.7", + "typescript": "~5.8.3", + }, + }, "packages/mcp": { "name": "@open-pencil/mcp", "version": "0.13.2", @@ -782,6 +792,8 @@ "@open-pencil/dom-css": ["@open-pencil/dom-css@workspace:packages/dom-css"], + "@open-pencil/kiwi": ["@open-pencil/kiwi@workspace:packages/kiwi"], + "@open-pencil/mcp": ["@open-pencil/mcp@workspace:packages/mcp"], "@open-pencil/vue": ["@open-pencil/vue@workspace:packages/vue"], @@ -1114,7 +1126,7 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/culori": ["@types/culori@4.0.1", "", {}, "sha512-43M51r/22CjhbOXyGT361GZ9vncSVQ39u62x5eJdBQFviI8zWp2X5jzqg7k4M6PVgDQAClpy2bUe2dtwEgEDVQ=="], @@ -1366,7 +1378,7 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -2952,8 +2964,6 @@ "@multiformats/dns/@libp2p/interface": ["@libp2p/interface@3.1.0", "", { "dependencies": { "@multiformats/dns": "^1.0.6", "@multiformats/multiaddr": "^13.0.1", "main-event": "^1.0.1", "multiformats": "^13.4.0", "progress-events": "^1.0.1", "uint8arraylist": "^2.4.8" } }, "sha512-RE7/XyvC47fQBe1cHxhMvepYKa5bFCUyFrrpj8PuM0E7JtzxU7F+Du5j4VXbg2yLDcToe0+j8mB7jvwE2AThYw=="], - "@open-pencil/dom-css/@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], - "@open-pencil/mcp/@types/node": ["@types/node@22.19.15", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg=="], "@rollup/plugin-node-resolve/@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], @@ -3170,8 +3180,6 @@ "@multiformats/dns/@libp2p/interface/@multiformats/multiaddr": ["@multiformats/multiaddr@13.0.1", "", { "dependencies": { "@chainsafe/is-ip": "^2.0.1", "multiformats": "^13.0.0", "uint8-varint": "^2.0.1", "uint8arrays": "^5.0.0" } }, "sha512-XToN915cnfr6Lr9EdGWakGJbPT0ghpg/850HvdC+zFX8XvpLZElwa8synCiwa8TuvKNnny6m8j8NVBNCxhIO3g=="], - "@open-pencil/dom-css/@types/bun/bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], - "@open-pencil/mcp/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "@vue-macros/common/ast-kit/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], diff --git a/oxlint.json b/oxlint.json index d4a995247..3fb9f7c00 100644 --- a/oxlint.json +++ b/oxlint.json @@ -418,7 +418,8 @@ }, { "files": [ - "**/kiwi/schema-runtime/**" + "**/kiwi/schema-runtime/**", + "**/kiwi/src/schema-runtime/**" ], "rules": { "typescript/no-explicit-any": "off", diff --git a/package.json b/package.json index 137c634d7..e5a7053b7 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "build": "bun run build:packages && bun run lint && vite build", "preview": "vite preview", "tauri": "tauri", - "lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/", - "lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ tests/ scripts/ tools/", - "format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ src/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ tests scripts/ tools/", + "lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/kiwi/src/", + "lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ tests/ scripts/ tools/", + "format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ src/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ tests scripts/ tools/", "format:check": "bun run format && status=$(git status --porcelain -uall) && test -z \"$status\" || (echo \"$status\" && exit 1)", "check": "bun run build:packages && bun run lint && tsgo --noEmit && bun run check:vue && bun run check:i18n && bun run check:packages && bun run check:arch && bun run test:type-shapes && bun run test:tools && bun run test:dupes", "check:i18n": "bun tools/i18n/src/check-locales.ts", @@ -31,7 +31,7 @@ "test:tools": "bun tools/test.ts", "test:dupes": "jscpd packages/core/src packages/cli/src packages/dom-css/src src --min-lines 5 --min-tokens 50 --format typescript --threshold 0", "test:packages": "bun tools/package-quality/src/check-metadata.ts && bun tools/package-quality/src/smoke.ts", - "build:packages": "bun --filter @open-pencil/core build && bun --filter @open-pencil/dom-css build && bun --filter @open-pencil/vue build && bun --filter @open-pencil/mcp build && bun --filter @open-pencil/cli build", + "build:packages": "bun --filter @open-pencil/kiwi build && bun --filter @open-pencil/core build && bun --filter @open-pencil/dom-css build && bun --filter @open-pencil/vue build && bun --filter @open-pencil/mcp build && bun --filter @open-pencil/cli build", "open-pencil": "bun packages/cli/src/index.ts", "docs:dev": "bun --filter @open-pencil/docs dev", "docs:build": "bun --filter @open-pencil/docs build", diff --git a/packages/kiwi/README.md b/packages/kiwi/README.md new file mode 100644 index 000000000..2f86c4f83 --- /dev/null +++ b/packages/kiwi/README.md @@ -0,0 +1,20 @@ +# @open-pencil/kiwi + +Kiwi schema runtime utilities for OpenPencil. + +This package is the future home for scene-graph-agnostic Kiwi parsing, validation, and binary codec helpers. The first split keeps `.fig` import/export policy in `@open-pencil/core` while package-local tests prove the pure Kiwi runtime can build and run independently. + +## Checks + +```sh +cd packages/kiwi +bun run check +``` + +Package scripts: + +- `bun run test` — package-local Bun tests for schema runtime and Figma schema guards +- `bun run typecheck` — type-checks `src`, tests, and package scripts +- `bun run build` — builds the distributable `dist` entrypoints +- `bun run smoke:dist` — imports built output and exercises the public API +- `bun run check` — runs typecheck, tests, build, and dist smoke in sequence diff --git a/packages/kiwi/package.json b/packages/kiwi/package.json new file mode 100644 index 000000000..55645e9d0 --- /dev/null +++ b/packages/kiwi/package.json @@ -0,0 +1,55 @@ +{ + "name": "@open-pencil/kiwi", + "version": "0.13.2", + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "bun": "./src/index.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./schema-runtime": { + "types": "./dist/src/schema-runtime/index.d.ts", + "bun": "./src/schema-runtime/index.ts", + "import": "./dist/schema-runtime.js", + "default": "./dist/schema-runtime.js" + }, + "./fig": { + "types": "./dist/src/fig/index.d.ts", + "bun": "./src/fig/index.ts", + "import": "./dist/fig.js", + "default": "./dist/fig.js" + } + }, + "main": "./dist/index.js", + "types": "./dist/src/index.d.ts", + "sideEffects": false, + "files": [ + "dist", + "README.md" + ], + "scripts": { + "build": "bunx tsdown --config tsdown.config.ts && bunx tsc --emitDeclarationOnly -p tsconfig.json", + "check": "bun run typecheck && bun run test && bun run build && bun run smoke:dist", + "prepack": "bun run build", + "smoke:dist": "bun scripts/smoke-dist.ts", + "test": "bun test tests", + "typecheck": "bunx tsc --noEmit -p tsconfig.test.json" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/open-pencil/open-pencil.git", + "directory": "packages/kiwi" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, + "devDependencies": { + "@types/bun": "^1.3.14", + "tsdown": "^0.21.7", + "typescript": "~5.8.3" + } +} diff --git a/packages/kiwi/scripts/smoke-dist.ts b/packages/kiwi/scripts/smoke-dist.ts new file mode 100644 index 000000000..3ad53687a --- /dev/null +++ b/packages/kiwi/scripts/smoke-dist.ts @@ -0,0 +1,17 @@ +import { figmaSchema, getKiwiMessageType, parseSchema, validateSchema } from '../dist/index.js' + +const schema = parseSchema(` +message Smoke { + uint id = 1; +} +`) +validateSchema(schema) +validateSchema(figmaSchema) + +if (figmaSchema.definitions.length !== 605) { + throw new Error(`Unexpected Figma schema definition count: ${figmaSchema.definitions.length}`) +} + +if (getKiwiMessageType(new Uint8Array([1, 3, 0])) !== 3) { + throw new Error('Failed to read Kiwi message type from built dist') +} diff --git a/packages/kiwi/src/fig/index.ts b/packages/kiwi/src/fig/index.ts new file mode 100644 index 000000000..432bc292f --- /dev/null +++ b/packages/kiwi/src/fig/index.ts @@ -0,0 +1,2 @@ +export { default as figmaSchema } from './schema' +export * from './protocol' diff --git a/packages/kiwi/src/fig/protocol.ts b/packages/kiwi/src/fig/protocol.ts new file mode 100644 index 000000000..d1c40f7a3 --- /dev/null +++ b/packages/kiwi/src/fig/protocol.ts @@ -0,0 +1,237 @@ +/** + * Figma Multiplayer Protocol + * + * This module handles the low-level WebSocket communication with Figma's + * multiplayer server. The protocol uses: + * + * - Kiwi binary serialization (schema-based, like Protocol Buffers) + * - Zstd compression for all messages + * - Session-based authentication via cookies + * + * Message types (from Figma's schema): + * 0 = JOIN_START - Server sends session info + * 1 = NODE_CHANGES - Create/update/delete nodes + * 2 = USER_CHANGES - User presence updates + * 3 = JOIN_END - Initial sync complete + * 4 = SIGNAL - Various metadata (reconnect info, etc.) + * 5 = STYLE - Style updates + * ...and more + * + * Wire format: + * All messages are Zstd-compressed Kiwi-encoded binary data. + * Zstd magic bytes: 0x28 0xB5 0x2F 0xFD + */ + +export const MESSAGE_TYPES = { + JOIN_START: 0, + NODE_CHANGES: 1, + USER_CHANGES: 2, + JOIN_END: 3, + SIGNAL: 4, + STYLE: 5, + STYLE_SET: 6, + JOIN_START_SKIP_RELOAD: 7, + NOTIFY_SHOULD_UPGRADE: 8, + UPGRADE_DONE: 9, + UPGRADE_REFRESH: 10, + SCENE_GRAPH_QUERY: 11, + SCENE_GRAPH_REPLY: 12, + DIFF: 13, + CLIENT_BROADCAST: 14 +} as const + +export const NODE_TYPES = { + NONE: 0, + DOCUMENT: 1, + CANVAS: 2, + GROUP: 3, + FRAME: 4, + BOOLEAN_OPERATION: 5, + VECTOR: 6, + STAR: 7, + LINE: 8, + ELLIPSE: 9, + RECTANGLE: 10, + REGULAR_POLYGON: 11, + ROUNDED_RECTANGLE: 12, + TEXT: 13, + SLICE: 14, + SYMBOL: 15, + INSTANCE: 16, + STICKY: 17, + SHAPE_WITH_TEXT: 18, + CONNECTOR: 19, + CODE_BLOCK: 20, + WIDGET: 21, + STAMP: 22, + MEDIA: 23, + HIGHLIGHT: 24, + SECTION: 25, + SECTION_OVERLAY: 26, + WASHI_TAPE: 27, + VARIABLE: 28 +} as const + +export const NODE_PHASES = { + CREATED: 0, + REMOVED: 1 +} as const + +export const BLEND_MODES = { + PASS_THROUGH: 0, + NORMAL: 1, + DARKEN: 2, + MULTIPLY: 3, + LINEAR_BURN: 4, + COLOR_BURN: 5, + LIGHTEN: 6, + SCREEN: 7, + LINEAR_DODGE: 8, + COLOR_DODGE: 9, + OVERLAY: 10, + SOFT_LIGHT: 11, + HARD_LIGHT: 12, + DIFFERENCE: 13, + EXCLUSION: 14, + HUE: 15, + SATURATION: 16, + COLOR: 17, + LUMINOSITY: 18 +} as const + +export const PAINT_TYPES = { + SOLID: 0, + GRADIENT_LINEAR: 1, + GRADIENT_RADIAL: 2, + GRADIENT_ANGULAR: 3, + GRADIENT_DIAMOND: 4, + IMAGE: 5, + EMOJI: 6, + VIDEO: 7 +} as const + +/** + * Zstd magic bytes + */ +export const ZSTD_MAGIC = new Uint8Array([0x28, 0xb5, 0x2f, 0xfd]) + +// ============================================================================ +// Kiwi Binary Format Constants +// ============================================================================ + +/** + * Kiwi uses field numbers to identify message fields. + * Field 1 with value = message type indicates the message kind. + */ +export const KIWI = { + /** First byte of valid Kiwi messages (field number 1) */ + MESSAGE_MARKER: 1, + + /** Field number for sessionID in JOIN_START message */ + SESSION_ID_FIELD: 2, + + /** Varint continuation bit (MSB set = more bytes follow) */ + VARINT_CONTINUE_BIT: 0x80, + + /** Varint value mask (lower 7 bits contain data) */ + VARINT_VALUE_MASK: 0x7f, + + /** Bits per varint byte */ + VARINT_BITS_PER_BYTE: 7 +} as const + +/** + * Valid session ID range (based on observed Figma behavior) + */ +export const SESSION_ID = { + MIN: 10000, + MAX: 1000000 +} as const + +/** + * Parse a varint from a Uint8Array at given position + * Returns [value, newPosition] + */ +export function parseVarint(data: Uint8Array, pos: number): [number, number] { + let value = 0 + let shift = 0 + + while (pos < data.length) { + const byte = data[pos] + pos++ + value |= (byte & KIWI.VARINT_VALUE_MASK) << shift + + if (!(byte & KIWI.VARINT_CONTINUE_BIT)) { + break + } + shift += KIWI.VARINT_BITS_PER_BYTE + } + + return [value, pos] +} + +/** + * Check if data is a valid Kiwi message + */ +export function isKiwiMessage(data: Uint8Array): boolean { + return data.length >= 2 && data[0] === KIWI.MESSAGE_MARKER +} + +/** + * Get message type from Kiwi message + */ +export function getKiwiMessageType(data: Uint8Array): number | null { + if (!isKiwiMessage(data)) return null + return data[1] ?? null +} + +/** + * fig-wire header magic (first 8 bytes of some messages) + */ +export const FIG_WIRE_MAGIC = 'fig-wire' + +/** + * Check if data is Zstd-compressed + */ +export function isZstdCompressed(data: Uint8Array): boolean { + return ( + data.length >= 4 && data[0] === 0x28 && data[1] === 0xb5 && data[2] === 0x2f && data[3] === 0xfd + ) +} + +/** + * Check if data has fig-wire header + */ +export function hasFigWireHeader(data: Uint8Array): boolean { + if (data.length < 8) return false + const header = new TextDecoder().decode(data.slice(0, 8)) + return header === FIG_WIRE_MAGIC +} + +/** + * Skip fig-wire header and find zstd data + * Header format: "fig-wire" (8 bytes) + version (4 bytes LE) + zstd data + */ +export function skipFigWireHeader(data: Uint8Array): Uint8Array { + if (!hasFigWireHeader(data)) return data + // Skip 8 bytes header + 4 bytes version + return data.slice(12) +} + +/** + * Current multiplayer protocol version + */ +export const PROTOCOL_VERSION = 151 + +/** + * Build WebSocket URL for Figma multiplayer + */ +export function buildMultiplayerUrl(fileKey: string, trackingId?: string): string { + const params = new URLSearchParams({ + role: 'editor', + version: String(PROTOCOL_VERSION), + recentReload: '0', + tracking_session_id: trackingId || `ws-${Date.now()}` + }) + return `wss://www.figma.com/api/multiplayer/${fileKey}?${params}` +} diff --git a/packages/kiwi/src/fig/schema.ts b/packages/kiwi/src/fig/schema.ts new file mode 100644 index 000000000..4d99d6272 --- /dev/null +++ b/packages/kiwi/src/fig/schema.ts @@ -0,0 +1,7 @@ +import { parseSchema, validateSchema } from '../schema-runtime' +import schemaText from './schema/fig.kiwi?raw' + +const schema = parseSchema(schemaText) +validateSchema(schema) + +export default schema diff --git a/packages/kiwi/src/fig/schema/fig.kiwi b/packages/kiwi/src/fig/schema/fig.kiwi new file mode 100644 index 000000000..dab55c8bc --- /dev/null +++ b/packages/kiwi/src/fig/schema/fig.kiwi @@ -0,0 +1,5623 @@ +enum MessageType { + JOIN_START = 0; + NODE_CHANGES = 1; + USER_CHANGES = 2; + JOIN_END = 3; + SIGNAL = 4; + STYLE = 5; + STYLE_SET = 6; + JOIN_START_SKIP_RELOAD = 7; + NOTIFY_SHOULD_UPGRADE = 8; + UPGRADE_DONE = 9; + UPGRADE_REFRESH = 10; + SCENE_GRAPH_QUERY = 11; + SCENE_GRAPH_REPLY = 12; + DIFF = 13; + CLIENT_BROADCAST = 14; + JOIN_START_JOURNALED = 15; + STREAM_START = 16; + STREAM_END = 17; + INTERACTIVE_SLIDE_CHANGE = 18; + RECONNECT_SCENE_GRAPH_QUERY = 19; + RECONNECT_SCENE_GRAPH_REPLY = 20; + JOIN_END_INCREMENTAL_RECONNECT = 21; + NODE_STATUS_CHANGE = 22; + CLIENT_RENDERED = 23; + BUZZ_APPROVAL_CHANGE = 24; +} + +enum Axis { + X = 0; + Y = 1; +} + +enum Access { + READ_ONLY = 0; + READ_WRITE = 1; +} + +enum NodePhase { + CREATED = 0; + REMOVED = 1; +} + +enum WindingRule { + NONZERO = 0; + ODD = 1; +} + +enum NodeType { + NONE = 0; + DOCUMENT = 1; + CANVAS = 2; + GROUP = 3; + FRAME = 4; + BOOLEAN_OPERATION = 5; + VECTOR = 6; + STAR = 7; + LINE = 8; + ELLIPSE = 9; + RECTANGLE = 10; + REGULAR_POLYGON = 11; + ROUNDED_RECTANGLE = 12; + TEXT = 13; + SLICE = 14; + SYMBOL = 15; + INSTANCE = 16; + STICKY = 17; + SHAPE_WITH_TEXT = 18; + CONNECTOR = 19; + CODE_BLOCK = 20; + WIDGET = 21; + STAMP = 22; + MEDIA = 23; + HIGHLIGHT = 24; + SECTION = 25; + SECTION_OVERLAY = 26; + WASHI_TAPE = 27; + VARIABLE = 28; + TABLE = 29; + TABLE_CELL = 30; + VARIABLE_SET = 31; + SLIDE = 32; + ASSISTED_LAYOUT = 33; + INTERACTIVE_SLIDE_ELEMENT = 34; + VARIABLE_OVERRIDE = 35; + MODULE = 36; + SLIDE_GRID = 37; + SLIDE_ROW = 38; + RESPONSIVE_SET = 39; + CODE_COMPONENT = 40; + TEXT_PATH = 41; + CODE_INSTANCE = 42; + CODE_LIBRARY = 43; + CODE_FILE = 44; + CODE_LAYER = 45; + BRUSH = 46; + MANAGED_STRING = 47; + TRANSFORM = 48; + CMS_RICH_TEXT = 49; + REPEATER = 50; + JSX = 51; + EMBEDDED_PROTOTYPE = 52; + REACT_FIBER = 53; + RESPONSIVE_NODE_SET = 54; + WEBPAGE = 55; + KEYFRAME = 56; + KEYFRAME_TRACK = 57; + ANIMATION_PRESET_INSTANCE = 58; + CODE_EMBED = 59; + BINARY_FILE = 60; + SPEC_BLOCK = 61; + TOOL_INSTANCE = 62; + CUSTOM_EFFECT_INSTANCE = 63; + NATIVE_CODE_LAYER_INSTANCE = 64; +} + +enum ShapeWithTextType { + SQUARE = 0; + ELLIPSE = 1; + DIAMOND = 2; + TRIANGLE_UP = 3; + TRIANGLE_DOWN = 4; + ROUNDED_RECTANGLE = 5; + PARALLELOGRAM_RIGHT = 6; + PARALLELOGRAM_LEFT = 7; + ENG_DATABASE = 8; + ENG_QUEUE = 9; + ENG_FILE = 10; + ENG_FOLDER = 11; + TRAPEZOID = 12; + PREDEFINED_PROCESS = 13; + SHIELD = 14; + DOCUMENT_SINGLE = 15; + DOCUMENT_MULTIPLE = 16; + MANUAL_INPUT = 17; + HEXAGON = 18; + CHEVRON = 19; + PENTAGON = 20; + OCTAGON = 21; + STAR = 22; + PLUS = 23; + ARROW_LEFT = 24; + ARROW_RIGHT = 25; + SUMMING_JUNCTION = 26; + OR = 27; + SPEECH_BUBBLE = 28; + INTERNAL_STORAGE = 29; +} + +enum BlendMode { + PASS_THROUGH = 0; + NORMAL = 1; + DARKEN = 2; + MULTIPLY = 3; + LINEAR_BURN = 4; + COLOR_BURN = 5; + LIGHTEN = 6; + SCREEN = 7; + LINEAR_DODGE = 8; + COLOR_DODGE = 9; + OVERLAY = 10; + SOFT_LIGHT = 11; + HARD_LIGHT = 12; + DIFFERENCE = 13; + EXCLUSION = 14; + HUE = 15; + SATURATION = 16; + COLOR = 17; + LUMINOSITY = 18; +} + +enum PaintType { + SOLID = 0; + GRADIENT_LINEAR = 1; + GRADIENT_RADIAL = 2; + GRADIENT_ANGULAR = 3; + GRADIENT_DIAMOND = 4; + IMAGE = 5; + EMOJI = 6; + VIDEO = 7; + PATTERN = 8; + NOISE = 9; + CUSTOM = 10; +} + +enum ImageScaleMode { + STRETCH = 0; + FIT = 1; + FILL = 2; + TILE = 3; +} + +enum EffectType { + INNER_SHADOW = 0; + DROP_SHADOW = 1; + FOREGROUND_BLUR = 2; + BACKGROUND_BLUR = 3; + REPEAT = 4; + SYMMETRY = 5; + GRAIN = 6; + NOISE = 7; + GLASS = 8; + CUSTOM = 9; +} + +enum TextCase { + ORIGINAL = 0; + UPPER = 1; + LOWER = 2; + TITLE = 3; + SMALL_CAPS = 4; + SMALL_CAPS_FORCED = 5; +} + +enum TextDecoration { + NONE = 0; + UNDERLINE = 1; + STRIKETHROUGH = 2; +} + +enum TextDecorationStyle { + SOLID = 0; + DOTTED = 1; + WAVY = 2; +} + +enum LeadingTrim { + NONE = 0; + CAP_HEIGHT = 1; +} + +enum NumberUnits { + RAW = 0; + PIXELS = 1; + PERCENT = 2; +} + +enum ConstraintType { + MIN = 0; + CENTER = 1; + MAX = 2; + STRETCH = 3; + SCALE = 4; + FIXED_MIN = 5; + FIXED_MAX = 6; +} + +enum StrokeAlign { + CENTER = 0; + INSIDE = 1; + OUTSIDE = 2; + OFFSET = 3; +} + +enum StrokeCap { + NONE = 0; + ROUND = 1; + SQUARE = 2; + ARROW_LINES = 3; + ARROW_EQUILATERAL = 4; + DIAMOND_FILLED = 5; + TRIANGLE_FILLED = 6; + HIGHLIGHT = 7; + WASHI_TAPE_1 = 8; + WASHI_TAPE_2 = 9; + WASHI_TAPE_3 = 10; + WASHI_TAPE_4 = 11; + WASHI_TAPE_5 = 12; + WASHI_TAPE_6 = 13; + CIRCLE_FILLED = 14; + ERD_ZERO_OR_ONE = 15; + ERD_EXACTLY_ONE = 16; + ERD_ZERO_OR_MORE = 17; + ERD_ONE_OR_MORE = 18; + ERD_ONE = 19; + ERD_MANY = 20; +} + +enum StrokeJoin { + MITER = 0; + BEVEL = 1; + ROUND = 2; +} + +enum BooleanOperation { + UNION = 0; + INTERSECT = 1; + SUBTRACT = 2; + XOR = 3; +} + +enum TextAlignHorizontal { + LEFT = 0; + CENTER = 1; + RIGHT = 2; + JUSTIFIED = 3; +} + +enum TextAlignVertical { + TOP = 0; + CENTER = 1; + BOTTOM = 2; +} + +enum MouseCursor { + DEFAULT = 0; + CROSSHAIR = 1; + EYEDROPPER = 2; + HAND = 3; + PAINT_BUCKET = 4; + PEN = 5; + PENCIL = 6; + MARKER = 7; + ERASER = 8; + HIGHLIGHTER = 9; + LASSO = 10; +} + +enum VectorMirror { + NONE = 0; + ANGLE = 1; + ANGLE_AND_LENGTH = 2; +} + +enum DashMode { + CLIP = 0; + STRETCH = 1; +} + +enum ImageType { + PNG = 0; + JPEG = 1; + SVG = 2; + PDF = 3; + MP4 = 4; + GIF = 5; +} + +enum ExportConstraintType { + CONTENT_SCALE = 0; + CONTENT_WIDTH = 1; + CONTENT_HEIGHT = 2; +} + +enum LayoutGridType { + MIN = 0; + CENTER = 1; + STRETCH = 2; + MAX = 3; +} + +enum LayoutGridPattern { + STRIPES = 0; + GRID = 1; +} + +enum TextAutoResize { + NONE = 0; + WIDTH_AND_HEIGHT = 1; + HEIGHT = 2; +} + +enum TextTruncation { + DISABLED = 0; + ENDING = 1; +} + +enum StyleSetType { + PERSONAL = 0; + TEAM = 1; + CUSTOM = 2; + FREQUENCY = 3; + TEMPORARY = 4; +} + +enum StyleSetContentType { + SOLID = 0; + GRADIENT = 1; + IMAGE = 2; +} + +enum StackMode { + NONE = 0; + HORIZONTAL = 1; + VERTICAL = 2; + GRID = 3; +} + +enum StackAlign { + MIN = 0; + CENTER = 1; + MAX = 2; + BASELINE = 3; +} + +enum StackCounterAlign { + MIN = 0; + CENTER = 1; + MAX = 2; + STRETCH = 3; + AUTO = 4; + BASELINE = 5; +} + +enum StackJustify { + MIN = 0; + CENTER = 1; + MAX = 2; + SPACE_EVENLY = 3; + SPACE_BETWEEN = 4; +} + +enum GridChildAlign { + AUTO = 0; + MIN = 1; + CENTER = 2; + MAX = 3; +} + +enum GridAutoTracks { + NONE = 0; + ROWS = 1; +} + +enum StackSize { + FIXED = 0; + RESIZE_TO_FIT = 1; + RESIZE_TO_FIT_WITH_IMPLICIT_SIZE = 2; +} + +enum StackPositioning { + AUTO = 0; + ABSOLUTE = 1; +} + +enum StackWrap { + NO_WRAP = 0; + WRAP = 1; +} + +enum StackCounterAlignContent { + AUTO = 0; + SPACE_BETWEEN = 1; +} + +enum ConnectionType { + NONE = 0; + INTERNAL_NODE = 1; + URL = 2; + BACK = 3; + CLOSE = 4; + SET_VARIABLE = 5; + UPDATE_MEDIA_RUNTIME = 6; + CONDITIONAL = 7; + SET_VARIABLE_MODE = 8; + OBJECT_ANIMATION = 9; + UPDATE_ANIMATION_TIMELINE_STATE = 10; +} + +enum InteractionType { + ON_CLICK = 0; + AFTER_TIMEOUT = 1; + MOUSE_IN = 2; + MOUSE_OUT = 3; + ON_HOVER = 4; + MOUSE_DOWN = 5; + MOUSE_UP = 6; + ON_PRESS = 7; + NONE = 8; + DRAG = 9; + ON_KEY_DOWN = 10; + ON_VOICE = 11; + ON_MEDIA_HIT = 12; + ON_MEDIA_END = 13; + MOUSE_ENTER = 14; + MOUSE_LEAVE = 15; +} + +enum TransitionType { + INSTANT_TRANSITION = 0; + DISSOLVE = 1; + FADE = 2; + SLIDE_FROM_LEFT = 3; + SLIDE_FROM_RIGHT = 4; + SLIDE_FROM_TOP = 5; + SLIDE_FROM_BOTTOM = 6; + PUSH_FROM_LEFT = 7; + PUSH_FROM_RIGHT = 8; + PUSH_FROM_TOP = 9; + PUSH_FROM_BOTTOM = 10; + MOVE_FROM_LEFT = 11; + MOVE_FROM_RIGHT = 12; + MOVE_FROM_TOP = 13; + MOVE_FROM_BOTTOM = 14; + SLIDE_OUT_TO_LEFT = 15; + SLIDE_OUT_TO_RIGHT = 16; + SLIDE_OUT_TO_TOP = 17; + SLIDE_OUT_TO_BOTTOM = 18; + MOVE_OUT_TO_LEFT = 19; + MOVE_OUT_TO_RIGHT = 20; + MOVE_OUT_TO_TOP = 21; + MOVE_OUT_TO_BOTTOM = 22; + MAGIC_MOVE = 23; + SMART_ANIMATE = 24; + SCROLL_ANIMATE = 25; +} + +enum EasingType { + IN_CUBIC = 0; + OUT_CUBIC = 1; + INOUT_CUBIC = 2; + LINEAR = 3; + IN_BACK_CUBIC = 4; + OUT_BACK_CUBIC = 5; + INOUT_BACK_CUBIC = 6; + CUSTOM_CUBIC = 7; + SPRING = 8; + GENTLE_SPRING = 9; + CUSTOM_SPRING = 10; + SPRING_PRESET_ONE = 11; + SPRING_PRESET_TWO = 12; + SPRING_PRESET_THREE = 13; + HOLD = 14; +} + +enum ScrollDirection { + NONE = 0; + HORIZONTAL = 1; + VERTICAL = 2; + BOTH = 3; +} + +enum ScrollContractedState { + EXPANDED = 0; + CONTRACTED = 1; +} + +struct GUID { + uint sessionID; + uint localID; +} + +struct Color { + float r; + float g; + float b; + float a; +} + +struct Vector { + float x; + float y; +} + +struct Rect { + float x; + float y; + float w; + float h; +} + +struct ColorStop { + Color color; + float position; +} + +message ColorStopVar { + Color color = 1; + VariableData colorVar = 2; + float position = 3; +} + +struct Matrix { + float m00; + float m01; + float m02; + float m10; + float m11; + float m12; +} + +struct ParentIndex { + GUID guid; + string position; +} + +struct Number { + float value; + NumberUnits units; +} + +struct FontName { + string family; + string style; + string postscript; +} + +enum FontVariantNumericFigure { + NORMAL = 0; + LINING = 1; + OLDSTYLE = 2; +} + +enum FontVariantNumericSpacing { + NORMAL = 0; + PROPORTIONAL = 1; + TABULAR = 2; +} + +enum FontVariantNumericFraction { + NORMAL = 0; + DIAGONAL = 1; + STACKED = 2; +} + +enum FontVariantCaps { + NORMAL = 0; + SMALL = 1; + ALL_SMALL = 2; + PETITE = 3; + ALL_PETITE = 4; + UNICASE = 5; + TITLING = 6; +} + +enum FontVariantPosition { + NORMAL = 0; + SUB = 1; + SUPER = 2; +} + +enum FontStyle { + NORMAL = 0; + ITALIC = 1; +} + +enum SemanticWeight { + NORMAL = 0; + BOLD = 1; +} + +enum SemanticItalic { + NORMAL = 0; + ITALIC = 1; +} + +enum CodeSnapshotState { + INITIAL = 0; + SNAPSHOTTING = 1; + OK = 2; + SNAPSHOT_ERROR = 3; + LLM_IN_PROGRESS = 4; +} + +enum SnapshotCaptureMode { + FULL = 0; + PARTIAL = 1; +} + +message CodeSourceInfo { + string originReferenceId = 1; + GUID originNodeId = 2; + GUID linkedSnapshotId = 3; + SnapshotCaptureMode captureMode = 4; + string sourceBlobRef = 5; + string sourceElementId = 6; +} + +enum CodeObjectType { + WEB_LAYER = 0; + WEB_INTERACTION = 1; + NATIVE_LAYER = 2; + ANIMATION_PRESET = 3; + TOOL = 4; + CUSTOM_EFFECT = 5; + PLUGIN = 6; + WEB_LAYER_GENERIC = 7; + CUSTOM_FILL = 8; +} + +message CustomToolArtifactRef { + string customToolId = 1; + string customToolVersion = 2; + string publishedCustomToolId = 3; + string publishedCustomToolVersionId = 4; +} + +enum LockMode { + NONE = 0; + ALL = 1; + BACKGROUND_ONLY = 2; +} + +enum OpenTypeFeature { + PCAP = 0; + C2PC = 1; + CASE = 2; + CPSP = 3; + TITL = 4; + UNIC = 5; + ZERO = 6; + SINF = 7; + ORDN = 8; + AFRC = 9; + DNOM = 10; + NUMR = 11; + LIGA = 12; + CLIG = 13; + DLIG = 14; + HLIG = 15; + RLIG = 16; + AALT = 17; + CALT = 18; + RCLT = 19; + SALT = 20; + RVRN = 21; + VERT = 22; + SWSH = 23; + CSWH = 24; + NALT = 25; + CCMP = 26; + STCH = 27; + HIST = 28; + SIZE = 29; + ORNM = 30; + ITAL = 31; + RAND = 32; + DTLS = 33; + FLAC = 34; + MGRK = 35; + SSTY = 36; + KERN = 37; + FWID = 38; + HWID = 39; + HALT = 40; + TWID = 41; + QWID = 42; + PWID = 43; + JUST = 44; + LFBD = 45; + OPBD = 46; + RTBD = 47; + PALT = 48; + PKNA = 49; + LTRA = 50; + LTRM = 51; + RTLA = 52; + RTLM = 53; + ABRV = 54; + ABVM = 55; + ABVS = 56; + VALT = 57; + VHAL = 58; + BLWF = 59; + BLWM = 60; + BLWS = 61; + AKHN = 62; + CJCT = 63; + CFAR = 64; + CPCT = 65; + CURS = 66; + DIST = 67; + EXPT = 68; + FALT = 69; + FINA = 70; + FIN2 = 71; + FIN3 = 72; + HALF = 73; + HALN = 74; + HKNA = 75; + HNGL = 76; + HOJO = 77; + INIT = 78; + ISOL = 79; + JP78 = 80; + JP83 = 81; + JP90 = 82; + JP04 = 83; + LJMO = 84; + LOCL = 85; + MARK = 86; + MEDI = 87; + MED2 = 88; + MKMK = 89; + NLCK = 90; + NUKT = 91; + PREF = 92; + PRES = 93; + VPAL = 94; + PSTF = 95; + PSTS = 96; + RKRF = 97; + RPHF = 98; + RUBY = 99; + SMPL = 100; + TJMO = 101; + TNAM = 102; + TRAD = 103; + VATU = 104; + VJMO = 105; + VKNA = 106; + VKRN = 107; + VRTR = 108; + VRT2 = 109; + SS01 = 110; + SS02 = 111; + SS03 = 112; + SS04 = 113; + SS05 = 114; + SS06 = 115; + SS07 = 116; + SS08 = 117; + SS09 = 118; + SS10 = 119; + SS11 = 120; + SS12 = 121; + SS13 = 122; + SS14 = 123; + SS15 = 124; + SS16 = 125; + SS17 = 126; + SS18 = 127; + SS19 = 128; + SS20 = 129; + CV01 = 130; + CV02 = 131; + CV03 = 132; + CV04 = 133; + CV05 = 134; + CV06 = 135; + CV07 = 136; + CV08 = 137; + CV09 = 138; + CV10 = 139; + CV11 = 140; + CV12 = 141; + CV13 = 142; + CV14 = 143; + CV15 = 144; + CV16 = 145; + CV17 = 146; + CV18 = 147; + CV19 = 148; + CV20 = 149; + CV21 = 150; + CV22 = 151; + CV23 = 152; + CV24 = 153; + CV25 = 154; + CV26 = 155; + CV27 = 156; + CV28 = 157; + CV29 = 158; + CV30 = 159; + CV31 = 160; + CV32 = 161; + CV33 = 162; + CV34 = 163; + CV35 = 164; + CV36 = 165; + CV37 = 166; + CV38 = 167; + CV39 = 168; + CV40 = 169; + CV41 = 170; + CV42 = 171; + CV43 = 172; + CV44 = 173; + CV45 = 174; + CV46 = 175; + CV47 = 176; + CV48 = 177; + CV49 = 178; + CV50 = 179; + CV51 = 180; + CV52 = 181; + CV53 = 182; + CV54 = 183; + CV55 = 184; + CV56 = 185; + CV57 = 186; + CV58 = 187; + CV59 = 188; + CV60 = 189; + CV61 = 190; + CV62 = 191; + CV63 = 192; + CV64 = 193; + CV65 = 194; + CV66 = 195; + CV67 = 196; + CV68 = 197; + CV69 = 198; + CV70 = 199; + CV71 = 200; + CV72 = 201; + CV73 = 202; + CV74 = 203; + CV75 = 204; + CV76 = 205; + CV77 = 206; + CV78 = 207; + CV79 = 208; + CV80 = 209; + CV81 = 210; + CV82 = 211; + CV83 = 212; + CV84 = 213; + CV85 = 214; + CV86 = 215; + CV87 = 216; + CV88 = 217; + CV89 = 218; + CV90 = 219; + CV91 = 220; + CV92 = 221; + CV93 = 222; + CV94 = 223; + CV95 = 224; + CV96 = 225; + CV97 = 226; + CV98 = 227; + CV99 = 228; +} + +struct ExportConstraint { + ExportConstraintType type; + float value; +} + +struct GUIDMapping { + GUID from; + GUID to; +} + +struct Blob { + byte[] bytes; +} + +message Image { + byte[] hash = 1; + string name = 2; + uint dataBlob = 3; +} + +message Video { + byte[] hash = 1; + string s3Url = 2; +} + +message PasteSource { + string srcFile = 1; + GUID srcNode = 2; +} + +struct FilterColorAdjust { + float tint; + float shadows; + float highlights; + float detail; + float exposure; + float vignette; + float temperature; + float vibrance; +} + +message PaintFilterMessage { + float tint = 1; + float shadows = 2; + float highlights = 3; + float detail = 4; + float exposure = 5; + float vignette = 6; + float temperature = 7; + float vibrance = 8; + float contrast = 9; + float brightness = 10; +} + +message Paint { + PaintType type = 1; + Color color = 2; + float opacity = 3; + bool visible = 4; + BlendMode blendMode = 5; + ColorStop[] stops = 6; + Matrix transform = 7; + Image image = 8; + Image imageThumbnail = 9; + Image animatedImage = 16; + uint animationFrame = 17; + ImageScaleMode imageScaleMode = 10; + bool imageShouldColorManage = 22; + float rotation = 11; + float scale = 12; + FilterColorAdjust filterColorAdjust = 13; + PaintFilterMessage paintFilter = 14; + uint[] emojiCodePoints = 15; + Video video = 18; + uint originalImageWidth = 19; + uint originalImageHeight = 20; + VariableData opacityVar = 38; + VariableData colorVar = 21; + VariableData imageVar = 31; + ColorStopVar[] stopsVar = 23; + string thumbHashBase64 = 24; + byte[] thumbHash = 25; + GUID sourceNodeId = 26; + float spacing = 27; + Vector patternSpacing = 37; + PatternTileType patternTileType = 28; + PatternAlignment verticalAlignment = 29; + PatternAlignment horizontalAlignment = 30; + GUID id = 32; + string altText = 33; + NoiseType noiseType = 34; + float density = 35; + Vector noiseSize = 36; + CodeComponentId customEffectId = 39; + ComponentPropAssignment[] componentPropAssignments = 40; +} + +enum NoiseType { + MULTITONE = 0; + MONOTONE = 1; + DUOTONE = 2; +} + +enum PatternTileType { + RECTANGULAR = 0; + HORIZONTAL_HEXAGONAL = 1; + VERTICAL_HEXAGONAL = 2; +} + +enum PatternAlignment { + START = 0; + CENTER = 1; + END = 2; +} + +message FontMetaData { + FontName key = 1; + float fontLineHeight = 2; + byte[] fontDigest = 3; + FontStyle fontStyle = 4; + int fontWeight = 5; +} + +message FontVariation { + uint axisTag = 1; + string axisName = 2; + float value = 3; +} + +message TextData { + string characters = 1; + uint[] characterStyleIDs = 2; + NodeChange[] styleOverrideTable = 3; + TextLineData[] lines = 12; + uint layoutVersion = 8; + FontName[] fallbackFonts = 10; + float minContentHeight = 17; + Vector layoutSize = 4; + Baseline[] baselines = 5; + Glyph[] glyphs = 6; + Decoration[] decorations = 7; + Blockquote[] blockquotes = 16; + FontMetaData[] fontMetaData = 9; + HyperlinkBox[] hyperlinkBoxes = 11; + int truncationStartIndex = 13; + float truncatedHeight = 14; + float[] logicalIndexToCharacterOffsetMap = 15; + MentionBox[] mentionBoxes = 18; + DerivedTextLineData[] derivedLines = 19; +} + +message DerivedTextData { + Vector layoutSize = 1; + Baseline[] baselines = 2; + Glyph[] glyphs = 3; + Decoration[] decorations = 4; + Blockquote[] blockquotes = 5; + FontMetaData[] fontMetaData = 6; + HyperlinkBox[] hyperlinkBoxes = 7; + int truncationStartIndex = 8; + float truncatedHeight = 9; + float[] logicalIndexToCharacterOffsetMap = 10; + MentionBox[] mentionBoxes = 11; + DerivedTextLineData[] derivedLines = 12; +} + +message HyperlinkBox { + Rect bounds = 1; + string url = 2; + GUID guid = 3; + CMSItemPageTarget cmsTarget = 5; + bool openInNewTab = 6; + int hyperlinkID = 4; +} + +message MentionBox { + Rect bounds = 1; + uint startIndex = 2; + uint endIndex = 3; + bool isValid = 4; + uint mentionKey = 5; +} + +message Baseline { + Vector position = 1; + float width = 2; + float lineY = 3; + float lineHeight = 4; + float lineAscent = 7; + float ignoreLeadingTrim = 8; + uint firstCharacter = 5; + uint endCharacter = 6; +} + +message Glyph { + uint commandsBlob = 1; + Vector position = 2; + uint styleID = 3; + float fontSize = 4; + uint firstCharacter = 5; + float advance = 6; + uint[] emojiCodePoints = 7; + EmojiImageSet emojiImageSet = 8; + float rotation = 9; +} + +message Decoration { + Rect[] rects = 1; + uint styleID = 2; +} + +message Blockquote { + Rect verticalBar = 1; + Rect quoteMarkBounds = 2; + uint styleID = 3; +} + +message VectorData { + uint vectorNetworkBlob = 1; + Vector normalizedSize = 2; + NodeChange[] styleOverrideTable = 3; +} + +message TextPathStart { + float tValue = 1; + bool forward = 2; +} + +message GUIDPath { + GUID[] guids = 1; +} + +message SymbolData { + GUID symbolID = 1; + NodeChange[] symbolOverrides = 2; + float uniformScaleFactor = 3; +} + +message GUIDPathMapping { + GUID id = 1; + GUIDPath path = 2; +} + +message DerivedBreakpointData { + NodeChange[] overrides = 1; +} + +message NodeGenerationData { + NodeChange[] overrides = 1; + bool useFineGrainedSyncing = 2; + NodeChange[] diffOnlyRemovals = 3; +} + +message DerivedImmutableFrameData { + NodeChange[] overrides = 1; + uint version = 2; +} + +message JsxData { + NodeChange[] overrides = 1; +} + +message DerivedJsxData { + NodeChange[] overrides = 1; +} + +message AssetIdMap { + AssetIdEntry[] entries = 1; +} + +message AssetIdEntry { + string assetKey = 1; + AssetId assetId = 2; +} + +message AssetRef { + string key = 1; + string version = 2; +} + +message AssetId { + GUID guid = 1; + AssetRef assetRef = 2; + StateGroupId stateGroupId = 3; + StyleId styleId = 4; + SymbolId symbolId = 5; + VariableID variableId = 6; + VariableSetID variableSetId = 7; +} + +message StateGroupId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message StyleId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message SymbolId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message VariableID { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message VariableOverrideId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message VariableSetID { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message ModuleId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message ResponsiveSetId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message WebpageId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message ThemeID { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message CodeLibraryId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message CodeFileId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message CodeComponentId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message CanvasNodeId { + GUID guid = 1; + SymbolId symbolId = 2; + StateGroupId stateGroupId = 3; +} + +struct IndexRange { + uint startIndex; + uint endIndexExclusive; +} + +struct CollaborativeTextOpID { + uint sessionID; + uint counterID; +} + +enum CollaborativeTextOpType { + INSERT = 0; + DELETE = 1; +} + +message CollaborativeTextStrippedOpRunWithIDs { + CollaborativeTextOpID firstId = 1; + uint runLength = 2; + CollaborativeTextOpID[] parentIds = 3; + CollaborativeTextOpID[] rebasedOnOpIds = 4; +} + +message CollaborativeTextStrippedOpRunWithLoc { + CollaborativeTextOpType type = 1; + IndexRange range = 2; + bool rangeShouldBeIteratedInReverse = 3; + IndexRange contentBytesInBuffer = 4; + IndexRange rebasedRange = 5; +} + +message CollaborativeTextOpRun { + CollaborativeTextOpID id = 1; + CollaborativeTextOpID[] parentIds = 2; + CollaborativeTextOpType type = 3; + IndexRange range = 4; + bool rangeShouldBeIteratedInReverse = 5; + string content = 6; + CollaborativeTextOpID[] rebasedOnOpIds = 7; + IndexRange rebasedRange = 8; +} + +message CollaborativePlainText { + CollaborativeTextStrippedOpRunWithIDs[] historyOpsWithIds = 1; + CollaborativeTextStrippedOpRunWithLoc[] historyOpsWithLoc = 2; + byte[] historyStringContentBuffer = 3; + CollaborativeTextOpRun[] changesToAppend = 4; +} + +message CollaborativeTextSelection { + GUID node = 1; + uint field = 2; + IndexRange selectedRange = 3; + bool caretAtFront = 4; + CollaborativeTextOpID[] textVersion = 5; +} + +message ResponsiveTextStyleVariant { + float minWidth = 1; + NodeChange fields = 2; + VariableData variableFontSize = 3; + VariableData variableLineHeight = 4; + VariableData variableLetterSpacing = 5; + VariableData variableParagraphSpacing = 6; + string name = 7; +} + +enum FlappType { + POLL = 0; + EMBED = 1; + FACEPILE = 2; + ALIGNMENT = 3; + YOUTUBE = 4; +} + +message SlideThemeProps { + string themeVersion = 1; + VariableSetID variableSetId = 2; + StyleId[] textStyleIds = 3; + bool isTextColorManuallySelected = 4; + bool isBorderColorManuallySelected = 5; + AssetRef subscribedThemeRef = 6; + uint schemaVersion = 7; + bool isGeneratedFromDesign = 8; +} + +message SlideThemeMap { + SlideThemeMapEntry[] entries = 1; +} + +message SlideThemeMapEntry { + ThemeID themeId = 1; + SlideThemeProps themeProps = 2; +} + +message SharedSymbolReference { + string fileKey = 1; + GUID symbolID = 2; + string versionHash = 3; + GUIDPathMapping[] guidPathMappings = 4; + byte[] bytes = 5; + GUIDMapping[] libraryGUIDToSubscribingGUID = 6; + string componentKey = 7; + GUIDPathMapping[] unflatteningMappings = 8; + bool isUnflattened = 9; +} + +message SharedComponentMasterData { + string componentKey = 1; + GUIDPathMapping[] publishingGUIDPathToTeamLibraryGUID = 2; + bool isUnflattened = 3; +} + +message InstanceOverrideStash { + GUIDPath overridePathOfSwappedInstance = 1; + string componentKey = 2; + NodeChange[] overrides = 3; +} + +message InstanceOverrideStashV2 { + GUIDPath overridePathOfSwappedInstance = 1; + GUID localSymbolID = 2; + NodeChange[] overrides = 3; +} + +message ImportedCodeFileEntry { + CodeFileId codeFileId = 1; +} + +message ImportedCodeFiles { + ImportedCodeFileEntry[] entries = 1; +} + +enum BlurOpType { + NORMAL = 0; + PROGRESSIVE = 1; +} + +enum RepeatType { + LINEAR = 0; + RADIAL = 1; +} + +enum UnitType { + PIXELS = 0; + RELATIVE = 1; +} + +enum RepeatOrder { + FORWARD = 0; + REVERSE = 1; +} + +enum EffectAxis { + X = 0; + Y = 1; + X_AND_Y = 2; +} + +message Effect { + EffectType type = 1; + Vector offset = 3; + float radius = 4; + bool visible = 5; + BlendMode blendMode = 6; + float spread = 7; + bool showShadowBehindNode = 8; + VariableData radiusVar = 9; + VariableData colorVar = 10; + VariableData spreadVar = 11; + VariableData xVar = 12; + VariableData yVar = 13; + uint count = 14; + RepeatType repeatType = 15; + EffectAxis axis = 16; + UnitType unitType = 17; + RepeatOrder order = 18; + BlurOpType blurOpType = 19; + Vector startOffset = 20; + Vector endOffset = 28; + float startRadius = 21; + Color color = 2; + Color secondaryColor = 24; + Vector noiseSize = 22; + uint seed = 29; + bool clipToShape = 23; + float density = 25; + NoiseType noiseType = 26; + float opacity = 27; + float refractionRadius = 30; + float specularAngle = 31; + float specularIntensity = 32; + float bevelSize = 33; + float chromaticAberration = 34; + float reflectionDistance = 35; + float refractionIntensity = 36; + VariableData refractionRadiusVar = 37; + VariableData specularAngleVar = 38; + VariableData specularIntensityVar = 39; + VariableData chromaticAberrationVar = 40; + VariableData splayVar = 41; + VariableData refractionIntensityVar = 42; + CodeComponentId customEffectId = 43; + ComponentPropAssignment[] componentPropAssignments = 44; + VariableData startRadiusVar = 45; + VariableData startOffsetXVar = 46; + VariableData startOffsetYVar = 47; + VariableData endOffsetXVar = 48; + VariableData endOffsetYVar = 49; + VariableData noiseSizeXVar = 50; + VariableData noiseSizeYVar = 51; + VariableData densityVar = 52; + VariableData effectOpacityVar = 53; + VariableData secondaryColorVar = 54; + GUID id = 55; +} + +enum TransformModifierType { + REPEAT = 0; + SYMMETRY = 1; + SKEW = 2; +} + +message TransformModifier { + TransformModifierType type = 1; + Vector offset = 2; + bool visible = 3; + uint count = 4; + RepeatType repeatType = 5; + EffectAxis axis = 6; + UnitType unitType = 7; + RepeatOrder order = 8; + float skewX = 9; + float skewY = 10; +} + +enum TransformSchemaType { + NONE = 0; + FIXED_ORDER = 1; + FREEFORM = 2; +} + +struct Matrix4f { + float m00; + float m01; + float m02; + float m03; + float m10; + float m11; + float m12; + float m13; + float m20; + float m21; + float m22; + float m23; + float m30; + float m31; + float m32; + float m33; +} + +message FixedOrderTransform3d { + float perspective = 1; + float translateZ = 2; + float rotateX = 3; + float rotateY = 4; + float rotateZ = 5; +} + +enum TransformFnType { + NONE = 0; + MATRIX_3D = 1; + PERSPECTIVE = 2; + TRANSLATE_Z = 3; + ROTATE_X = 4; + ROTATE_Y = 5; + ROTATE_Z = 6; +} + +message TransformFnValue { + Matrix4f matrix3d = 1; + float perspective = 2; + float rotateAngle = 3; + float translateValue = 4; +} + +message TransformFn { + TransformFnType type = 1; + TransformFnValue value = 2; +} + +message TransformSchemaValue { + FixedOrderTransform3d fixedOrderTransform3d = 1; + TransformFn[] transformFunctions = 2; +} + +message Transform3d { + TransformSchemaType type = 1; + TransformSchemaValue value = 2; + bool backfaceHidden = 3; +} + +struct NumberVector2D { + Number x; + Number y; +} + +message Scene3d { + float perspective = 1; + NumberVector2D perspectiveOrigin = 2; + bool preserve3d = 3; +} + +message TransformOrigin { + Number x = 1; + Number y = 2; +} + +message TransitionInfo { + TransitionType type = 1; + float duration = 2; +} + +enum PrototypeDeviceType { + NONE = 0; + PRESET = 1; + CUSTOM = 2; + PRESENTATION = 3; +} + +enum DeviceRotation { + NONE = 0; + CCW_90 = 1; +} + +message PrototypeDevice { + PrototypeDeviceType type = 1; + Vector size = 2; + string presetIdentifier = 3; + DeviceRotation rotation = 4; +} + +enum OverlayPositionType { + CENTER = 0; + TOP_LEFT = 1; + TOP_CENTER = 2; + TOP_RIGHT = 3; + BOTTOM_LEFT = 4; + BOTTOM_CENTER = 5; + BOTTOM_RIGHT = 6; + MANUAL = 7; +} + +enum OverlayBackgroundInteraction { + NONE = 0; + CLOSE_ON_CLICK_OUTSIDE = 1; +} + +enum OverlayBackgroundType { + NONE = 0; + SOLID_COLOR = 1; +} + +message OverlayBackgroundAppearance { + OverlayBackgroundType backgroundType = 1; + Color backgroundColor = 2; +} + +enum NavigationType { + NAVIGATE = 0; + OVERLAY = 1; + SWAP = 2; + SWAP_STATE = 3; + SCROLL_TO = 4; +} + +enum ExportColorProfile { + DOCUMENT = 0; + SRGB = 1; + DISPLAY_P3_V4 = 2; + CMYK = 3; +} + +enum ExportBackgroundType { + SOLID = 0; + TRANSPARENT = 1; + GRID = 2; +} + +message ExportSettings { + string suffix = 1; + ImageType imageType = 2; + ExportConstraint constraint = 3; + bool svgDataName = 4; + ExportSVGIDMode svgIDMode = 5; + bool svgOutlineText = 6; + bool contentsOnly = 7; + bool svgForceStrokeMasks = 8; + bool useAbsoluteBounds = 9; + ExportColorProfile colorProfile = 10; + float quality = 11; + bool useBicubicSampler = 12; + int frameRate = 13; + int loopCount = 14; + ExportBackgroundType backgroundType = 15; +} + +enum ExportSVGIDMode { + IF_NEEDED = 0; + ALWAYS = 1; +} + +message LayoutGrid { + LayoutGridType type = 1; + Axis axis = 2; + bool visible = 3; + int numSections = 4; + float offset = 5; + float sectionSize = 6; + float gutterSize = 7; + Color color = 8; + LayoutGridPattern pattern = 9; + VariableData numSectionsVar = 10; + VariableData offsetVar = 11; + VariableData sectionSizeVar = 12; + VariableData gutterSizeVar = 13; +} + +message Guide { + Axis axis = 1; + float offset = 2; + GUID guid = 3; +} + +message Path { + WindingRule windingRule = 1; + uint commandsBlob = 2; + uint styleID = 3; +} + +enum StyleType { + NONE = 0; + FILL = 1; + STROKE = 2; + TEXT = 3; + EFFECT = 4; + EXPORT = 5; + GRID = 6; + ANIMATION = 7; +} + +enum BrushOrientation { + FORWARD = 0; + REVERSE = 1; +} + +enum BrushType { + STRETCH = 0; + SCATTER = 1; +} + +message DynamicStrokeSettings { + float frequency = 1; + float wiggle = 2; + float smoothen = 3; +} + +message ScatterStrokeSettings { + float gap = 1; + float wiggle = 2; + float angularJitter = 3; + float rotation = 4; + float sizeJitter = 5; +} + +message StretchStrokeSettings { + BrushOrientation orientation = 1; +} + +message StrokeData { + Stroke[] strokes = 1; + uint version = 2; +} + +message Stroke { + int strokeId = 1; + float strokeWeight = 2; + VariableData strokeWeightVar = 3; + Paint[] strokePaint = 4; + StyleId styleIdForStrokeFill = 5; + StrokeAlign strokeAlign = 6; + StrokeCap strokeCap = 7; + Number strokeCapSize = 8; + StrokeJoin strokeJoin = 9; + float miterLimit = 10; + float[] dashPattern = 11; + OptionalVector pathTrim = 12; + float strokeOffset = 13; + bool isDeleted = 14; +} + +message VariableWidthPoint { + float position = 1; + float ascent = 2; + float descent = 3; + int segmentId = 4; +} + +message SharedStyleReference { + string styleKey = 1; + string versionHash = 2; +} + +message SharedStyleMasterData { + string styleKey = 1; + string sortPosition = 2; + string fileKey = 3; +} + +enum ScrollBehavior { + SCROLLS = 0; + FIXED_WHEN_CHILD_OF_SCROLLING_FRAME = 1; + STICKY_SCROLLS = 2; +} + +message ArcData { + float startingAngle = 1; + float endingAngle = 2; + float innerRadius = 3; +} + +message SymbolLink { + string uri = 1; + string displayName = 2; + string displayText = 3; +} + +message PluginData { + string pluginID = 1; + string value = 2; + string key = 3; +} + +message PluginRelaunchData { + string pluginID = 1; + string message = 2; + string command = 3; + bool isDeleted = 4; + CodeComponentId customToolId = 5; +} + +message MultiplayerFieldVersion { + uint counter = 1; + uint sessionID = 2; +} + +enum ConnectorMagnet { + NONE = 0; + AUTO = 1; + TOP = 2; + LEFT = 3; + BOTTOM = 4; + RIGHT = 5; + CENTER = 6; + AUTO_HORIZONTAL = 7; + EDGE = 8; + ABSOLUTE = 9; +} + +message ConnectorEndpoint { + GUID endpointNodeID = 1; + Vector position = 2; + ConnectorMagnet magnet = 3; + Vector relativePosition = 4; +} + +message ConnectorControlPoint { + Vector position = 1; + Vector axis = 2; +} + +enum ConnectorTextSection { + MIDDLE_TO_START = 0; + MIDDLE_TO_END = 1; +} + +enum ConnectorOffAxisOffset { + NONE = 0; + ABOVE = 1; + BELOW = 2; +} + +message ConnectorTextMidpoint { + ConnectorTextSection section = 1; + float offset = 2; + ConnectorOffAxisOffset offAxisOffset = 3; +} + +enum ConnectorLineStyle { + ELBOWED = 0; + STRAIGHT = 1; + CURVED = 2; +} + +enum ConnectorType { + MANUAL = 0; + DIAGRAM = 1; +} + +enum AnnotationPropertyType { + FILL = 0; + STROKE = 1; + WIDTH = 2; + HEIGHT = 3; + MIN_WIDTH = 4; + MIN_HEIGHT = 5; + MAX_WIDTH = 6; + MAX_HEIGHT = 7; + STROKE_WIDTH = 8; + CORNER_RADIUS = 9; + EFFECT = 10; + TEXT_STYLE = 11; + TEXT_ALIGN_HORIZONTAL = 12; + FONT_FAMILY = 13; + FONT_SIZE = 14; + FONT_WEIGHT = 15; + LINE_HEIGHT = 16; + LETTER_SPACING = 17; + STACK_SPACING = 18; + STACK_PADDING = 19; + STACK_MODE = 20; + STACK_ALIGNMENT = 21; + OPACITY = 22; + COMPONENT = 23; + FONT_STYLE = 24; + GRID_ROW_GAP = 25; + GRID_COLUMN_GAP = 26; + GRID_ROW_COUNT = 27; + GRID_COLUMN_COUNT = 28; + GRID_ROW_ANCHOR_INDEX = 29; + GRID_COLUMN_ANCHOR_INDEX = 30; + GRID_ROW_SPAN = 31; + GRID_COLUMN_SPAN = 32; +} + +message AnnotationProperty { + AnnotationPropertyType type = 1; +} + +enum AnnotationCategoryPreset { + NONE = 0; + ACCESSIBILITY = 1; + BEHAVIOR = 2; + CONTENT = 3; + DEVELOPMENT = 4; + INTERACTION = 5; +} + +enum AnnotationCategoryColor { + YELLOW = 0; + ORANGE = 1; + RED = 2; + PINK = 3; + VIOLET = 4; + BLUE = 5; + TEAL = 6; + GREEN = 7; +} + +message AnnotationCategoryCustom { + AnnotationCategoryColor color = 1; + Color customColor = 2; + string label = 3; +} + +message AnnotationCategory { + GUID id = 1; + AnnotationCategoryPreset preset = 2; + AnnotationCategoryCustom custom = 3; +} + +message AnnotationCategories { + uint version = 1; + AnnotationCategory[] items = 2; +} + +message Annotation { + string label = 1; + AnnotationProperty[] properties = 2; + string labelV2 = 3; + GUID categoryId = 4; +} + +enum AnnotationMeasurementNodeSide { + TOP = 0; + BOTTOM = 1; + LEFT = 2; + RIGHT = 3; +} + +message AnnotationMeasurement { + GUID id = 1; + GUID fromNode = 2; + GUID toNode = 3; + AnnotationMeasurementNodeSide fromNodeSide = 4; + bool toSameSide = 5; + float innerOffsetRelative = 6; + float outerOffsetFixed = 7; + GUIDPath toNodeStablePath = 8; + string freeText = 9; +} + +message LibraryMoveInfo { + string oldKey = 1; + string pasteFileKey = 2; +} + +message LibraryMoveHistoryItem { + GUID sourceNodeId = 1; + string sourceComponentKey = 2; +} + +message DeveloperRelatedLink { + string nodeId = 1; + string fileKey = 2; + string linkName = 3; + string linkUrl = 4; +} + +message WidgetPointer { + GUID nodeId = 1; +} + +message EditInfo { + string timestampIso8601 = 1; + string userId = 2; + uint lastEditedAt = 3; + uint createdAt = 4; +} + +enum EditorType { + DESIGN = 0; + WHITEBOARD = 1; + SLIDES = 2; + DEV_HANDOFF = 3; + SITES = 4; + COOPER = 5; + ILLUSTRATION = 6; + FIGMAKE = 7; + FIGSPEC = 8; +} + +enum MaskType { + ALPHA = 0; + OUTLINE = 1; + LUMINANCE = 2; +} + +enum ModuleType { + NONE = 0; + SINGLE_NODE = 1; + MULTI_NODE = 2; +} + +enum SectionStatus { + NONE = 0; + BUILD = 1; + COMPLETED = 2; +} + +message SectionStatusInfo { + SectionStatus status = 1; + uint lastUpdateUnixTimestamp = 2; + string description = 3; + string userId = 4; + SectionStatus prevStatus = 5; +} + +message BuzzApprovalRequestInfo { + string requestId = 1; + string requesterUserId = 2; + uint requestedAt = 3; + string[] reviewerUserIds = 4; + string title = 5; + string note = 6; + GUID[] assetsInRequest = 7; +} + +message BuzzApprovalRequests { + BuzzApprovalRequestInfo[] requests = 1; +} + +enum BuzzApprovalNodeStatus { + NONE = 0; + IN_REVIEW = 1; + APPROVED = 2; + CHANGES_REQUESTED = 3; +} + +message BuzzApprovalNodeStatusInfo { + BuzzApprovalNodeStatus currentStatus = 1; + bool wasPreviouslyApproved = 2; + uint[] approvalRevokedAtHistory = 3; +} + +message CodeEmbedInfo { + string url = 1; + string srcUrl = 2; + string title = 3; + string thumbnailImageHash = 4; + bool isPublishedSite = 5; +} + +enum VariableTimingDisplayUnit { + MILLISECONDS = 0; + SECONDS = 1; +} + +message NodeChange { + GUID guid = 1; + uint guidTag = 53; + NodePhase phase = 2; + uint phaseTag = 54; + ParentIndex parentIndex = 3; + uint parentIndexTag = 55; + NodeType type = 4; + uint typeTag = 56; + string name = 5; + uint nameTag = 57; + bool isPublishable = 174; + string description = 318; + LibraryMoveInfo libraryMoveInfo = 256; + LibraryMoveHistoryItem[] libraryMoveHistory = 281; + string key = 319; + AssetIdMap fileAssetIds = 383; + uint styleID = 49; + uint styleIDTag = 101; + bool isFillStyle = 157; + bool isStrokeStyle = 161; + bool isOverrideOverTextStyle = 376; + StyleType styleType = 163; + string styleDescription = 191; + string version = 171; + string userFacingVersion = 399; + string sortPosition = 320; + SharedStyleMasterData ojansSuperSecretNodeField = 345; + SharedStyleMasterData sevMoonlitLilyData = 348; + bool isSoftDeletedStyle = 176; + bool isNonUpdateable = 177; + SharedStyleMasterData sharedStyleMasterData = 172; + SharedStyleReference sharedStyleReference = 173; + GUID inheritFillStyleID = 158; + GUID inheritStrokeStyleID = 162; + GUID inheritTextStyleID = 167; + GUID inheritExportStyleID = 168; + GUID inheritEffectStyleID = 169; + GUID inheritGridStyleID = 170; + GUID inheritFillStyleIDForStroke = 185; + StyleId styleIdForFill = 332; + StyleId styleIdForStrokeFill = 333; + StyleId styleIdForText = 334; + StyleId styleIdForEffect = 335; + StyleId styleIdForGrid = 336; + StyleAnimation[] styleAnimations = 580; + Paint[] backgroundPaints = 193; + GUID inheritFillStyleIDForBackground = 194; + bool isStateGroup = 225; + StateGroupPropertyValueOrder[] stateGroupPropertyValueOrders = 238; + PartialPasteAnnotation partialPasteAnnotation = 581; + SharedSymbolReference sharedSymbolReference = 122; + bool isSymbolPublishable = 123; + GUIDPathMapping[] sharedSymbolMappings = 124; + string sharedSymbolVersion = 126; + SharedComponentMasterData sharedComponentMasterData = 152; + string symbolDescription = 144; + GUIDPathMapping[] unflatteningMappings = 164; + GUIDPathMapping[] forceUnflatteningMappings = 228; + string publishFile = 214; + string sourceLibraryKey = 395; + GUID publishID = 215; + string componentKey = 216; + bool isC2 = 217; + string publishedVersion = 218; + string originComponentKey = 252; + ComponentPropDef[] componentPropDefs = 266; + ComponentPropRef[] componentPropRefs = 267; + VariantPropSpec[] variantPropSpecs = 483; + SymbolData symbolData = 113; + uint symbolDataTag = 114; + NodeChange[] derivedSymbolData = 125; + bool nestedInstanceResizeEnabled = 394; + GUID overriddenSymbolID = 143; + ComponentPropAssignment[] componentPropAssignments = 268; + bool propsAreBubbled = 305; + InstanceOverrideStash[] overrideStash = 248; + InstanceOverrideStashV2[] overrideStashV2 = 250; + GUIDPath guidPath = 111; + uint guidPathTag = 112; + int overrideLevel = 321; + ModuleType moduleType = 382; + bool isSlot = 463; + bool isSlotContent = 495; + float fontSize = 21; + uint fontSizeTag = 73; + float paragraphIndent = 22; + uint paragraphIndentTag = 74; + float paragraphSpacing = 23; + uint paragraphSpacingTag = 75; + TextAlignHorizontal textAlignHorizontal = 32; + uint textAlignHorizontalTag = 84; + TextAlignVertical textAlignVertical = 33; + uint textAlignVerticalTag = 85; + TextCase textCase = 34; + uint textCaseTag = 86; + TextDecoration textDecoration = 35; + uint textDecorationTag = 87; + Number lineHeight = 40; + uint lineHeightTag = 92; + FontName fontName = 41; + uint fontNameTag = 93; + TextData textData = 42; + uint textDataTag = 94; + DerivedTextData derivedTextData = 359; + bool fontVariantCommonLigatures = 127; + bool fontVariantContextualLigatures = 128; + bool fontVariantDiscretionaryLigatures = 129; + bool fontVariantHistoricalLigatures = 130; + bool fontVariantOrdinal = 131; + bool fontVariantSlashedZero = 132; + FontVariantNumericFigure fontVariantNumericFigure = 133; + FontVariantNumericSpacing fontVariantNumericSpacing = 134; + FontVariantNumericFraction fontVariantNumericFraction = 135; + FontVariantCaps fontVariantCaps = 136; + FontVariantPosition fontVariantPosition = 137; + Number letterSpacing = 165; + string fontVersion = 202; + LeadingTrim leadingTrim = 322; + bool hangingPunctuation = 337; + bool hangingList = 339; + bool fallbackGlyphs = 550; + int maxLines = 351; + ResponsiveTextStyleVariant[] responsiveTextStyleVariants = 417; + SectionStatus sectionStatus = 352; + SectionStatusInfo sectionStatusInfo = 355; + uint textUserLayoutVersion = 203; + uint textExplicitLayoutVersion = 396; + OpenTypeFeature[] toggledOnOTFeatures = 205; + OpenTypeFeature[] toggledOffOTFeatures = 206; + Hyperlink hyperlink = 223; + Mention mention = 340; + FontVariation[] fontVariations = 260; + uint textBidiVersion = 279; + TextTruncation textTruncation = 280; + bool hasHadRTLText = 292; + EmojiImageSet emojiImageSet = 391; + string slideThumbnailHash = 392; + bool visible = 6; + uint visibleTag = 58; + bool locked = 7; + uint lockedTag = 59; + LockMode lockMode = 434; + float opacity = 8; + uint opacityTag = 60; + BlendMode blendMode = 9; + uint blendModeTag = 61; + Vector size = 11; + uint sizeTag = 63; + Matrix transform = 12; + uint transformTag = 64; + float[] dashPattern = 13; + uint dashPatternTag = 65; + bool mask = 16; + uint maskTag = 68; + Vector rotationOrigin = 424; + bool maskIsOutline = 18; + uint maskIsOutlineTag = 70; + MaskType maskType = 317; + float backgroundOpacity = 19; + uint backgroundOpacityTag = 71; + float cornerRadius = 20; + uint cornerRadiusTag = 72; + float strokeWeight = 26; + uint strokeWeightTag = 78; + StrokeAlign strokeAlign = 29; + uint strokeAlignTag = 81; + StrokeCap strokeCap = 30; + uint strokeCapTag = 82; + Number strokeCapSize = 497; + StrokeJoin strokeJoin = 31; + uint strokeJoinTag = 83; + Paint[] fillPaints = 38; + uint fillPaintsTag = 90; + Paint[] strokePaints = 39; + uint strokePaintsTag = 91; + Effect[] effects = 43; + uint effectsTag = 95; + Color backgroundColor = 50; + uint backgroundColorTag = 102; + Path[] fillGeometry = 51; + uint fillGeometryTag = 103; + Path[] strokeGeometry = 52; + uint strokeGeometryTag = 104; + Path[] offsetFillMaskGeometry = 564; + Paint[] textDecorationFillPaints = 411; + bool textDecorationSkipInk = 412; + Number textUnderlineOffset = 413; + Number textDecorationThickness = 415; + TextDecorationStyle textDecorationStyle = 416; + TransformModifier[] transformModifiers = 455; + Transform3d transform3d = 570; + StrokeData strokeData = 571; + Scene3d scene3d = 572; + TransformOrigin transformOrigin = 587; + float rectangleTopLeftCornerRadius = 145; + float rectangleTopRightCornerRadius = 146; + float rectangleBottomLeftCornerRadius = 147; + float rectangleBottomRightCornerRadius = 148; + bool rectangleCornerRadiiIndependent = 149; + bool rectangleCornerToolIndependent = 150; + bool proportionsConstrained = 151; + OptionalVector targetAspectRatio = 423; + bool useAbsoluteBounds = 258; + bool borderTopHidden = 287; + bool borderBottomHidden = 288; + bool borderLeftHidden = 289; + bool borderRightHidden = 290; + bool bordersTakeSpace = 294; + float borderTopWeight = 295; + float borderBottomWeight = 296; + float borderLeftWeight = 297; + float borderRightWeight = 298; + bool borderStrokeWeightsIndependent = 299; + ConstraintType horizontalConstraint = 28; + uint horizontalConstraintTag = 80; + StackMode stackMode = 105; + uint stackModeTag = 106; + float stackSpacing = 107; + uint stackSpacingTag = 108; + float stackPadding = 109; + uint stackPaddingTag = 110; + StackCounterAlign stackCounterAlign = 120; + StackJustify stackJustify = 121; + StackAlign stackAlign = 208; + float stackHorizontalPadding = 209; + float stackVerticalPadding = 210; + StackSize stackWidth = 211; + StackSize stackHeight = 212; + StackSize stackPrimarySizing = 229; + StackJustify stackPrimaryAlignItems = 230; + StackAlign stackCounterAlignItems = 231; + float stackChildPrimaryGrow = 232; + float stackPaddingRight = 233; + float stackPaddingBottom = 234; + StackCounterAlign stackChildAlignSelf = 236; + StackPositioning stackPositioning = 269; + bool stackReverseZIndex = 271; + StackWrap stackWrap = 323; + float stackCounterSpacing = 324; + OptionalVector minSize = 325; + OptionalVector maxSize = 326; + StackCounterAlignContent stackCounterAlignContent = 343; + int[] sortedMovingChildIndices = 406; + uint stackLayoutVersion = 574; + GUIDPositionMap gridRows = 435; + GUIDPositionMap gridColumns = 436; + float gridRowGap = 437; + float gridColumnGap = 438; + GUID gridRowAnchor = 439; + GUID gridColumnAnchor = 440; + uint gridRowSpan = 441; + uint gridColumnSpan = 442; + GUIDGridTrackSizeMap gridColumnsSizing = 474; + GUIDGridTrackSizeMap gridRowsSizing = 475; + GridChildAlign gridChildVerticalAlign = 476; + GridChildAlign gridChildHorizontalAlign = 477; + GridAutoTracks gridAutoTracks = 555; + bool gridReflowEnabled = 556; + bool isSnakeGameBoard = 344; + GUID transitionNodeID = 139; + GUID prototypeStartNodeID = 140; + Color prototypeBackgroundColor = 141; + TransitionInfo transitionInfo = 153; + TransitionType transitionType = 154; + float transitionDuration = 155; + EasingType easingType = 156; + bool transitionPreserveScroll = 181; + ConnectionType connectionType = 182; + string connectionURL = 183; + PrototypeDevice prototypeDevice = 184; + InteractionType interactionType = 187; + float transitionTimeout = 188; + bool interactionMaintained = 189; + float interactionDuration = 190; + bool destinationIsOverlay = 192; + bool transitionShouldSmartAnimate = 207; + PrototypeInteraction[] prototypeInteractions = 226; + PrototypeInteraction[] objectAnimations = 426; + PrototypeStartingPoint prototypeStartingPoint = 249; + PluginData[] pluginData = 204; + PluginRelaunchData[] pluginRelaunchData = 219; + ConnectorEndpoint connectorStart = 242; + ConnectorEndpoint connectorEnd = 243; + ConnectorLineStyle connectorLineStyle = 244; + StrokeCap connectorStartCap = 245; + StrokeCap connectorEndCap = 246; + ConnectorControlPoint[] connectorControlPoints = 253; + ConnectorControlPoint[] connectorBezierControlPoints = 479; + ConnectorTextMidpoint connectorTextMidpoint = 255; + ConnectorType connectorType = 373; + int connectorVersion = 533; + Annotation[] annotations = 369; + AnnotationMeasurement[] measurements = 384; + AnnotationCategories annotationCategories = 453; + ShapeWithTextType shapeWithTextType = 241; + float shapeUserHeight = 247; + bool isStrokePaintDerived = 530; + DerivedImmutableFrameData derivedImmutableFrameData = 254; + MultiplayerFieldVersion derivedImmutableFrameDataVersion = 338; + NodeGenerationData nodeGenerationData = 240; + JsxData jsxData = 491; + DerivedJsxData derivedJsxData = 492; + string stableKey = 493; + CodeBlockLanguage codeBlockLanguage = 259; + CodeBlockTheme codeBlockTheme = 433; + LinkPreviewData linkPreviewData = 278; + bool shapeTruncates = 282; + bool sectionContentsHidden = 283; + VideoPlayback videoPlayback = 300; + StampData stampData = 301; + SectionPresetInfo sectionPresetInfo = 370; + PlatformShapeDefinition platformShapeDefinition = 409; + MultiplayerMap widgetSyncedState = 273; + uint widgetSyncCursor = 274; + WidgetDerivedSubtreeCursor widgetDerivedSubtreeCursor = 275; + WidgetPointer widgetCachedAncestor = 276; + WidgetInputBehavior widgetInputBehavior = 285; + string widgetTooltip = 286; + WidgetHoverStyle widgetHoverStyle = 291; + bool isWidgetStickable = 293; + bool shouldHideCursorsOnWidgetHover = 360; + WidgetMetadata widgetMetadata = 262; + WidgetEvent[] widgetEvents = 263; + WidgetPropertyMenuItem[] widgetPropertyMenuItems = 265; + WidgetInputTextNodeType widgetInputTextNodeType = 401; + MultiplayerMap jsxProps = 489; + TableRowColumnPositionMap tableRowPositions = 308; + TableRowColumnPositionMap tableColumnPositions = 309; + TableRowColumnSizeMap tableRowHeights = 310; + TableRowColumnSizeMap tableColumnWidths = 311; + TableMergedCellMap tableMergedCells = 538; + MultiplayerMap interactiveSlideConfigData = 371; + MultiplayerMap interactiveSlideParticipantData = 372; + FlappType flappType = 402; + bool isEmbeddedPrototype = 486; + string slideSpeakerNotes = 389; + bool isSkippedSlide = 410; + MultiplayerMap presentationOutlines = 573; + ThemeID themeID = 379; + SlideThemeData slideThemeData = 381; + SlideThemeMap slideThemeMap = 390; + string slideTemplateFileKey = 393; + SlideNumber slideNumber = 443; + string slideNumberSeparator = 456; + GUID diagramParentId = 363; + GUID layoutRoot = 362; + string layoutPosition = 364; + DiagramLayoutRuleType diagramLayoutRuleType = 366; + DiagramParentIndex diagramParentIndex = 367; + DiagramLayoutPaused diagramLayoutPaused = 368; + bool isPageDivider = 380; + InternalEnumForTest internalEnumForTest = 251; + InternalDataForTest internalDataForTest = 257; + bool autoRename = 14; + uint autoRenameTag = 66; + bool backgroundEnabled = 15; + uint backgroundEnabledTag = 67; + bool exportContentsOnly = 17; + uint exportContentsOnlyTag = 69; + float miterLimit = 25; + uint miterLimitTag = 77; + float textTracking = 27; + uint textTrackingTag = 79; + ConstraintType verticalConstraint = 37; + uint verticalConstraintTag = 89; + ExportSettings[] exportSettings = 45; + uint exportSettingsTag = 97; + TextAutoResize textAutoResize = 46; + uint textAutoResizeTag = 98; + LayoutGrid[] layoutGrids = 47; + uint layoutGridsTag = 99; + bool frameMaskDisabled = 115; + uint frameMaskDisabledTag = 116; + bool resizeToFit = 117; + uint resizeToFitTag = 118; + BooleanOperation booleanOperation = 36; + uint booleanOperationTag = 88; + VectorMirror handleMirroring = 44; + uint handleMirroringTag = 96; + uint count = 10; + uint countTag = 62; + float starInnerScale = 24; + uint starInnerScaleTag = 76; + ArcData arcData = 195; + VectorData vectorData = 48; + uint vectorDataTag = 100; + uint vectorOperationVersion = 425; + TextPathStart textPathStart = 432; + bool exportBackgroundDisabled = 119; + Guide[] guides = 138; + bool internalOnly = 142; + ScrollDirection scrollDirection = 159; + float cornerSmoothing = 160; + Vector scrollOffset = 166; + bool exportTextAsSVGText = 175; + ScrollContractedState scrollContractedState = 178; + Vector contractedSize = 179; + string fixedChildrenDivider = 180; + ScrollBehavior scrollBehavior = 186; + int derivedSymbolDataLayoutVersion = 196; + NavigationType navigationType = 197; + OverlayPositionType overlayPositionType = 198; + Vector overlayRelativePosition = 199; + OverlayBackgroundInteraction overlayBackgroundInteraction = 200; + OverlayBackgroundAppearance overlayBackgroundAppearance = 201; + GUID overrideKey = 213; + bool containerSupportsFillStrokeAndCorners = 220; + StackSize stackCounterSizing = 221; + bool containersSupportFillStrokeAndCorners = 222; + KeyTrigger keyTrigger = 224; + string voiceEventPhrase = 227; + GUID[] ancestorPathBeforeDeletion = 235; + SymbolLink[] symbolLinks = 237; + TextListData textListData = 239; + bool detachOpticalSizeFromFontSize = 261; + float listSpacing = 264; + EmbedData embedData = 270; + RichMediaData richMediaData = 272; + MultiplayerMap renderedSyncedState = 277; + bool simplifyInstancePanels = 284; + HTMLTag accessibleHTMLTag = 302; + ARIARole ariaRole = 303; + ARIAAttributesMap ariaAttributes = 357; + string accessibleLabel = 304; + bool isDecorativeImage = 490; + VariableData variableData = 306; + VariableDataMap variableConsumptionMap = 307; + VariableModeBySetMap variableModeBySetMap = 316; + VariableSetMode[] variableSetModes = 312; + VariableSetID variableSetID = 313; + VariableResolvedDataType variableResolvedType = 314; + VariableDataValues variableDataValues = 315; + string variableTokenName = 350; + VariableTimingDisplayUnit timingDisplayUnit = 566; + VariableScope[] variableScopes = 353; + VariableDataMap parameterConsumptionMap = 445; + CodeSyntaxMap codeSyntax = 358; + PasteSource pasteSource = 388; + EditorType pageType = 397; + GUID strokeBrushGuid = 446; + uint64 strokeSeed = 482; + VariableWidthPoint[] variableWidthPoints = 447; + DynamicStrokeSettings dynamicStrokeSettings = 448; + ScatterStrokeSettings scatterStrokeSettings = 449; + StretchStrokeSettings stretchStrokeSettings = 450; + Matrix[] scatterBrushTransforms = 488; + BrushType brushType = 452; + OptionalVector pathTrim = 542; + float strokeOffset = 554; + VariableSetID backingVariableSetId = 377; + VariableID overriddenVariableId = 464; + VariableIdOrVariableOverrideId backingVariableId = 378; + bool isCollectionExtendable = 385; + string rootVariableKey = 386; + InheritedVariablesData inheritedVariableIds = 517; + HandoffStatusMap handoffStatusMap = 361; + AgendaPositionMap agendaPositionMap = 327; + AgendaMetadataMap agendaMetadataMap = 328; + MigrationStatus migrationStatus = 329; + bool isSoftDeleted = 330; + EditInfo editInfo = 331; + ColorProfile colorProfile = 341; + SymbolId detachedSymbolId = 342; + ChildReadingDirection childReadingDirection = 346; + string readingIndex = 347; + DocumentColorProfile documentColorProfile = 349; + DeveloperRelatedLink[] developerRelatedLinks = 354; + string slideActiveThemeLibKey = 356; + EditScopeInfo editScopeInfo = 365; + SemanticWeight semanticWeight = 374; + SemanticItalic semanticItalic = 375; + bool areSlidesManuallyIndented = 403; + bool isResponsiveSet = 387; + DerivedBreakpointData derivedBreakpointData = 500; + GUID defaultResponsiveSetId = 398; + bool isPrimaryBreakpoint = 458; + GUID primaryResponsiveNodeId = 457; + GUID multiEditGlueId = 462; + float breakpointMinWidth = 501; + bool isBreakpointInFocus = 522; + ResponsiveSetSettings responsiveSetSettings = 400; + NodeBehaviors behaviors = 404; + string sourceCode = 414; + CollaborativeTextOpID[] sourceCodeCollaborativeTextVersion = 534; + CollaborativePlainText collaborativeSourceCode = 444; + CodeLibraryId belongsToCodeLibraryId = 427; + ImportedCodeFiles importedCodeFiles = 467; + CanvasNodeId codeFileCanvasNodeId = 468; + bool isEntrypointCodeFile = 498; + string componentOrStateGroupKey = 502; + uint componentOrStateGroupVersion = 503; + string sourceCodeLibraryKey = 504; + string[] sourceCodeLibraryKeys = 515; + UsedMakeLibrary[] usedMakeLibraries = 524; + string makeLibraryComponentId = 518; + bool shouldHidePreviewForMakeKitCreation = 520; + bool isMakeKit = 551; + PrototypeDevice codePreviewSettings = 531; + CodeExample[] codeExamples = 525; + CodeFileId exportedFromCodeFileId = 428; + string codeExportName = 430; + string codeComponentDescription = 547; + CodeComponentId backingCodeComponentId = 429; + bool isMainCodeComponent = 487; + CodeSnapshotState codeSnapshotState = 431; + NodeChatMessage[] chatMessages = 451; + NodeChatCompressionState chatCompressionState = 485; + AIChatThread aiChatThread = 496; + string codeChatMessagesKey = 484; + CodeSnapshot codeSnapshot = 459; + CodeSnapshotLayers codeSnapshotLayers = 589; + uint codeSnapshotInvalidatedAt = 480; + bool isCodeBehavior = 465; + bool autoForkCode = 469; + bool hasBeenManuallyRenamed = 470; + bool codeCreatedFromDesign = 471; + CanvasNodeId codeCreatedFromDesignNodeId = 481; + ImageImportMap imageImports = 473; + CodeObjectType codeObjectType = 516; + string codeFilePath = 472; + CodeBehaviorData codeBehaviorData = 478; + uint codeLibraryFormat = 519; + bool isCodePreviewPlayingOnCanvas = 527; + CodeEmbedInfo codeEmbedInfo = 532; + bool isEmbedCodeLayer = 546; + CodeSourceInfo codeSourceInfo = 558; + string mimeType = 536; + byte[] blobRef = 537; + CMSSelector cmsSelector = 419; + CMSConsumptionMap cmsConsumptionMap = 420; + CMSRichTextStyleMap cmsRichTextStyleMap = 460; + SymbolId repeaterSymbolId = 539; + RepeaterCmsOverrideData repeaterCmsOverrideData = 540; + RepeaterSymbolOverrideData repeaterSymbolOverrideData = 549; + RepeaterOverrideData repeaterOverrideData = 541; + uint[] aiEditedNodeChangeFieldNumbers = 405; + string aiEditScopeLabel = 408; + FirstDraftData firstDraftData = 407; + FirstDraftKitElementData firstDraftKitElementData = 418; + CooperRevertData cooperRevertData = 421; + CooperTemplateData cooperTemplateData = 461; + BuzzApprovalRequests buzzApprovalRequests = 528; + BuzzApprovalNodeStatusInfo buzzApprovalNodeStatusInfo = 529; + HubFileAttribution hubFileAttribution = 422; + ManagedStringData managedStringData = 454; + ThumbnailInfo thumbnailInfo = 466; + AiCanvasPrompt aiCanvasPrompt = 494; + CanvasNodeId backingNodeId = 499; + string pageStatus = 548; + TRSSTransform2D motionTransform = 523; + int64 timelinePosition = 506; + KeyframeValueData keyframeValue = 507; + VariableData keyframeValueRef = 586; + InterpolationType interpolationType = 505; + BezierHandles bezierHandles = 508; + EasingData easingData = 535; + KeyframeOperation keyframeOperation = 509; + TimelinePositionType timelinePositionType = 510; + bool isClip = 545; + GUID clipId = 511; + uint64 timelineDuration = 512; + int64 timelineOffset = 513; + bool timelineDisabled = 543; + PlaybackStyle playbackStyle = 514; + TimelineDefinitionsMap timelineDefinitions = 552; + TimelineAssignmentsMap timelineAssignments = 553; + AnimationPresets animationPresets = 521; + StyleIdForAnimation[] styleIdsForAnimation = 582; + AnimationPresetId backingAnimationPresetId = 583; + Tools tools = 568; + CustomEffects customEffects = 569; + TransitionOverrideData transitionOverrides = 526; + bool useLegacySmartAnimate = 544; + SourceControlConfig sourceControlConfig = 557; + SpecBlockType specBlockType = 559; + CollaborativePlainText specBlockContent = 560; + string specCodeBlockLanguage = 561; + string specBlockTableAlignment = 562; + int specBlockIndentLevel = 563; + string specImageHash = 575; + int specWidth = 576; + int specHeight = 577; + TableRowColumnSizeMap specBlockTableRowHeights = 578; + TableRowColumnSizeMap specBlockTableColumnWidths = 579; + string specEmbedUrl = 590; + bool placeholder = 565; + string placeholderClientLifecycleId = 584; + int placeholderInvalidateAt = 585; + bool disableJitDst = 567; + CustomToolArtifactRef customToolArtifactRef = 588; +} + +enum GitRepoRefProvider { + UGIT = 0; + GITHUB = 1; + OTHER = 2; +} + +message GitRepoRef { + GitRepoRefProvider provider = 1; + string gitRepo = 2; + string gitRef = 3; +} + +message SourceControlConfig { + GitRepoRef origin = 1; + GitRepoRef upstream = 2; +} + +message CodeSnapshot { + CodeSnapshotState state = 6; + uint invalidatedAt = 7; + Paint[] paints = 1; + Vector offset = 2; + Vector layoutSize = 3; + Vector canvasSize = 5; + uint devicePixelRatio = 4; +} + +message CodeOutputLayer { + NodeChange node = 1; +} + +message CodeSnapshotLayers { + CodeSnapshotState state = 1; + uint invalidatedAt = 2; + CodeOutputLayer[] layers = 3; +} + +message CodeBehaviorData { + string name = 1; + string icon = 2; + string[] nodeTypes = 3; + string category = 4; + uint apiVersion = 5; +} + +message CodeExample { + string exampleName = 1; + string codeExportName = 2; +} + +message UsedMakeLibrary { + string makeLibraryId = 1; +} + +message CookieBannerText { + string bannerHeader = 1; + string bannerDisclaimerExplicit = 2; + string bannerDisclaimerImplicit = 3; + string policyLabel = 4; + string acceptText = 5; + string acknowledgeText = 6; + string manageText = 7; + string rejectText = 8; + string necessaryText = 9; + string necessaryDescription = 10; + string analyticsText = 11; + string analyticsDescription = 12; + string preferencesText = 13; + string preferencesDescription = 14; + string marketingText = 15; + string marketingDescription = 16; + string saveLabel = 17; + string triggerLabel = 18; +} + +message CookieBannerSettings { + bool enabled = 1; + CookieBannerComponentType componentType = 2; + CookieXAlignment xAlignment = 3; + CookieYAlignment yAlignment = 4; + CookieXAlignment triggerXAlignment = 5; + CookieYAlignment triggerYAlignment = 6; + TriggerComponentType triggerComponentType = 7; + string policyUrl = 8; + CookieBannerText text = 9; + GUID policyLink = 10; + string locale = 11; +} + +enum CookieBannerComponentType { + BANNER = 0; + MODAL = 1; +} + +enum TriggerComponentType { + BANNER = 0; + TAG = 1; +} + +enum CookieXAlignment { + LEFT = 0; + CENTER = 1; + RIGHT = 2; +} + +enum CookieYAlignment { + TOP = 0; + CENTER = 1; + BOTTOM = 2; +} + +message ResponsiveSetSettings { + string title = 1; + string description = 2; + ResponsiveScalingMode scalingMode = 3; + float scalingMinFontSize = 4; + float scalingMaxFontSize = 5; + float scalingMinLayoutWidth = 6; + float scalingMaxLayoutWidth = 7; + string lang = 8; + string faviconHash = 9; + string socialImageHash = 10; + string googleAnalyticsID = 11; + bool blockSearchIndexing = 12; + string customCodeHeadStart = 13; + string customCodeHeadEnd = 14; + string customCodeBodyStart = 15; + string customCodeBodyEnd = 16; + GUID faviconID = 17; + GUID socialImageID = 18; + bool addBypassLinks = 19; + bool ignoreReducedMotion = 20; + CookieBannerSettings cookieBanner = 21; +} + +enum ResponsiveScalingMode { + REFLOW = 0; + SCALE = 1; +} + +message CMSSelector { + string cmsCollectionId = 1; + CMSFilterCritera filterCriteria = 2; + CMSSelectorSort[] sorts = 3; + uint limit = 4; +} + +message CMSFilterCritera { + CMSFilterCriteriaMatchType matchType = 1; + CMSSelectorFilter[] filters = 2; +} + +enum CMSFilterCriteriaMatchType { + MATCH_ALL = 0; + MATCH_ANY = 1; +} + +message CMSSelectorFilter { + string cmsFieldId = 1; + CMSSelectorFilterOperator op = 2; + string comparisonValue = 3; +} + +enum CMSSelectorFilterOperator { + EQUALS = 0; +} + +message CMSSelectorSort { + string cmsFieldId = 1; + CMSFieldOrderBy orderBy = 2; +} + +enum CMSFieldOrderBy { + ASCENDING = 0; + DESCENDING = 1; +} + +message CMSConsumptionMap { + CMSConsumptionMapEntry[] entries = 1; +} + +message CMSConsumptionMapEntry { + CMSConsumptionField consumptionField = 1; + string cmsFieldId = 2; +} + +enum CMSConsumptionField { + MISSING = 0; + TEXT_DATA = 1; +} + +message CMSRichTextStyleMap { + CMSRichTextStyleEntry[] entries = 1; +} + +message CMSRichTextStyleEntry { + CMSRichTextStyleClass styleClass = 1; + CMSRichTextDescriptor textDescriptor = 2; +} + +enum CMSRichTextStyleClass { + HEADING1 = 0; + HEADING2 = 1; + HEADING3 = 2; + HEADING4 = 3; + HEADING5 = 4; + HEADING6 = 5; + PARAGRAPH = 6; + LINK = 7; + BLOCKQUOTE = 8; +} + +message CMSRichTextDescriptor { + StyleId textStyleId = 1; + FontName[] fontNameVariants = 2; +} + +message RepeaterCmsOverrideData { + NodeChange[] overrides = 1; +} + +message RepeaterOverrideData { + NodeChange[] parentIndexOverrides = 1; +} + +message RepeaterSymbolOverrideData { + RepeaterPositionOverrides[] overridesByPosition = 1; +} + +message RepeaterPositionOverrides { + ParentIndex position = 1; + NodeChange[] overrides = 2; +} + +message InheritedVariablesData { + InheritedVariableEntry[] variableIds = 1; +} + +message InheritedVariableEntry { + VariableID variableId = 1; +} + +message HubFileAttribution { + string hubFileId = 1; + string hubFileName = 2; +} + +message ManagedStringData { + string key = 1; + string context = 2; + string locale = 3; + ManagedStringNode content = 4; + ManagedStringContentSchema contentSchema = 5; +} + +enum ManagedStringContentSchema { + V0 = 0; +} + +enum ManagedStringNodeType { + TEXT = 0; + CONCATENATE = 1; + PLURAL = 2; + PLACEHOLDER = 3; +} + +message ManagedStringNode { + ManagedStringNodeType type = 1; + ManagedStringTextNodeData textNodeData = 2; + ManagedStringConcatenateAstNodeData concatenateNodeData = 3; + ManagedStringPluralAstNodeData pluralNodeData = 4; + ManagedStringPlaceholderAstNodeData placeholderNodeData = 5; +} + +message ManagedStringTextNodeData { + string value = 1; +} + +message ManagedStringConcatenateAstNodeData { + ManagedStringNode[] values = 1; +} + +enum ManagedStringPluralType { + ZERO = 0; + ONE = 1; + TWO = 2; + FEW = 3; + MANY = 4; + OTHER = 5; +} + +message ManagedStringPluralAstNodeData { + string identifier = 1; + ManagedStringPluralTypeMapEntry[] conditions = 2; +} + +message ManagedStringPluralTypeMapEntry { + ManagedStringPluralType key = 1; + ManagedStringNode value = 2; +} + +enum ManagedStringFormatType { + TEXT = 0; + DATE = 1; + TIME = 2; + NUMBER = 3; +} + +message ManagedStringPlaceholderAstNodeData { + string identifier = 1; + ManagedStringFormatType formatType = 2; + string formatPattern = 3; +} + +message CooperRevertData { + NodeChange originalValues = 1; +} + +message VideoPlayback { + bool autoplay = 1; + bool mediaLoop = 2; + bool muted = 3; + bool showControls = 4; + uint startTimeMs = 5; + uint endTimeMs = 6; +} + +enum MediaAction { + PLAY = 0; + PAUSE = 1; + TOGGLE_PLAY_PAUSE = 2; + MUTE = 3; + UNMUTE = 4; + TOGGLE_MUTE_UNMUTE = 5; + SKIP_FORWARD = 6; + SKIP_BACKWARD = 7; + SKIP_TO = 8; + SET_PLAYBACK_RATE = 9; +} + +enum AnimationTimelineAction { + PLAY = 0; + PAUSE = 1; + TOGGLE_PLAY_PAUSE = 2; + SET_PLAYHEAD = 3; +} + +message WidgetHoverStyle { + Paint[] fillPaints = 1; + Paint[] strokePaints = 2; + float opacity = 3; + bool areFillPaintsSet = 4; + bool areStrokePaintsSet = 5; + bool isOpacitySet = 6; +} + +message WidgetDerivedSubtreeCursor { + uint sessionID = 1; + uint counter = 2; +} + +message MultiplayerMap { + MultiplayerMapEntry[] entries = 1; +} + +message MultiplayerMapEntry { + string key = 1; + string value = 2; +} + +message VariableDataMap { + VariableDataMapEntry[] entries = 1; +} + +message VariableDataMapEntry { + uint nodeField = 1; + VariableData variableData = 2; + VariableField variableField = 3; +} + +enum VariableField { + MISSING = 0; + CORNER_RADIUS = 1; + PARAGRAPH_SPACING = 2; + PARAGRAPH_INDENT = 3; + STROKE_WEIGHT = 4; + STACK_SPACING = 5; + STACK_PADDING_LEFT = 6; + STACK_PADDING_TOP = 7; + STACK_PADDING_RIGHT = 8; + STACK_PADDING_BOTTOM = 9; + VISIBLE = 10; + TEXT_DATA = 11; + WIDTH = 12; + HEIGHT = 13; + RECTANGLE_TOP_LEFT_CORNER_RADIUS = 14; + RECTANGLE_TOP_RIGHT_CORNER_RADIUS = 15; + RECTANGLE_BOTTOM_LEFT_CORNER_RADIUS = 16; + RECTANGLE_BOTTOM_RIGHT_CORNER_RADIUS = 17; + BORDER_TOP_WEIGHT = 18; + BORDER_BOTTOM_WEIGHT = 19; + BORDER_LEFT_WEIGHT = 20; + BORDER_RIGHT_WEIGHT = 21; + VARIANT_PROPERTIES = 22; + STACK_COUNTER_SPACING = 23; + MIN_WIDTH = 24; + MAX_WIDTH = 25; + MIN_HEIGHT = 26; + MAX_HEIGHT = 27; + FONT_FAMILY = 28; + FONT_STYLE = 29; + FONT_VARIATIONS = 30; + OPACITY = 31; + FONT_SIZE = 32; + LETTER_SPACING = 34; + LINE_HEIGHT = 36; + OVERRIDDEN_SYMBOL_ID = 37; + HYPERLINK = 38; + CMS_SERIALIZED_RICH_TEXT_DATA = 39; + SLOT_CONTENT_ID = 40; + GRID_ROW_GAP = 41; + GRID_COLUMN_GAP = 42; + X_POSITION = 43; + Y_POSITION = 44; + ROTATION = 45; + MOTION_TRANSLATION_X = 46; + MOTION_TRANSLATION_Y = 47; + MOTION_ROTATION = 48; + MOTION_SCALE_X = 49; + MOTION_SCALE_Y = 50; + MOTION_SHEAR = 51; + SCROLL_OFFSET_X = 52; + SCROLL_OFFSET_Y = 53; + PATH_TRIM_START = 54; + PATH_TRIM_END = 55; + DISSOLVE_PROGRESS = 56; + EASING_DATA = 57; + MEDIA_CURRENT_TIME = 58; + TRANSFORM_3D_PERSPECTIVE = 59; + TRANSFORM_3D_TRANSLATION_Z = 60; + TRANSFORM_3D_ROTATION_X = 61; + TRANSFORM_3D_ROTATION_Y = 62; + TRANSFORM_3D_ROTATION_Z = 63; + POLYGON_COUNT = 64; + ARC_DATA_STARTING_ANGLE = 65; + ARC_DATA_ENDING_ANGLE = 66; + ARC_DATA_INNER_RADIUS = 67; +} + +message VariableModeBySetMap { + VariableModeBySetMapEntry[] entries = 1; +} + +message VariableModeBySetMapEntry { + VariableSetID variableSetID = 1; + GUID variableModeID = 2; + VariableSetID variableSetExtensionID = 3; +} + +message CodeSyntaxMap { + CodeSyntaxMapEntry[] entries = 1; +} + +message CodeSyntaxMapEntry { + CodeSyntaxPlatform platform = 1; + string value = 2; +} + +message TableMergedCellMap { + TableMergedCellMapEntry[] entries = 1; +} + +message TableMergedCellMapEntry { + GUID rowId = 1; + GUID colId = 2; + int rowSpan = 3; + int colSpan = 4; +} + +message TableRowColumnPositionMap { + TableRowColumnPositionMapEntry[] entries = 1; +} + +message TableRowColumnPositionMapEntry { + GUID id = 1; + string position = 2; +} + +message GUIDPositionMap { + GUIDPositionMapEntry[] entries = 1; +} + +message GUIDPositionMapEntry { + GUID id = 1; + string position = 2; +} + +message GUIDGridTrackSizeMap { + GUIDGridTrackSizeMapEntry[] entries = 1; +} + +message GUIDGridTrackSizeMapEntry { + GUID id = 1; + GridTrackSize trackSize = 2; +} + +message ObjectAnimationList { + ObjectAnimationListItem[] entries = 1; +} + +message ObjectAnimationListItem { + GUID targetNodeId = 1; + PrototypeAction animation = 2; +} + +message GridTrackSize { + GridTrackSizingFunction minSizing = 1; + GridTrackSizingFunction maxSizing = 2; +} + +message GridTrackSizingFunction { + GridTrackSizingType type = 1; + float value = 2; +} + +enum GridTrackSizingType { + FLEX = 0; + FIXED = 1; + HUG = 2; +} + +message TableRowColumnSizeMap { + TableRowColumnSizeMapEntry[] entries = 1; +} + +message TableRowColumnSizeMapEntry { + GUID id = 1; + float size = 2; +} + +message AgendaPositionMap { + AgendaPositionMapEntry[] entries = 1; +} + +message AgendaPositionMapEntry { + GUID id = 1; + string position = 2; +} + +enum AgendaItemType { + NODE = 0; + BLOCK = 1; +} + +message AgendaMetadataMap { + AgendaMetadataMapEntry[] entries = 1; +} + +message AgendaMetadataMapEntry { + GUID id = 1; + AgendaMetadata data = 2; +} + +message AgendaMetadata { + string name = 1; + AgendaItemType type = 2; + GUID targetNodeID = 3; + AgendaTimerInfo timerInfo = 4; + AgendaVoteInfo voteInfo = 5; + AgendaMusicInfo musicInfo = 6; +} + +message AgendaTimerInfo { + uint timerLength = 1; +} + +message AgendaVoteInfo { + uint voteCount = 1; +} + +message AgendaMusicInfo { + string songID = 1; + uint startTimeMs = 2; +} + +enum DiagramLayoutRuleType { + NONE = 0; + TREE = 1; +} + +struct DiagramParentIndex { + GUID guid; + string position; +} + +enum DiagramLayoutPaused { + NO = 0; + YES = 1; +} + +message ComponentPropRef { + uint nodeField = 1; + GUID defID = 2; + string zombieFallbackName = 3; + ComponentPropNodeField componentPropNodeField = 4; + bool isDeleted = 5; +} + +enum ComponentPropNodeField { + VISIBLE = 0; + TEXT_DATA = 1; + OVERRIDDEN_SYMBOL_ID = 2; + INHERIT_FILL_STYLE_ID = 3; + SLOT_CONTENT_ID = 4; +} + +message ComponentPropAssignment { + GUID defID = 1; + ComponentPropValue value = 2; + VariableData varValue = 3; + DerivedTextData legacyDerivedTextData = 4; +} + +message ComponentPropDef { + GUID id = 1; + string name = 2; + ComponentPropValue initialValue = 3; + string sortPosition = 4; + GUID parentPropDefId = 5; + ComponentPropType type = 6; + bool isDeleted = 7; + ComponentPropPreferredValues preferredValues = 8; + VariableData varValue = 9; + ParameterConfig parameterConfig = 10; + string description = 11; + SlotPropConfig slotPropConfig = 12; + ColorArrayConfig colorArrayConfig = 13; +} + +message ComponentPropValue { + bool boolValue = 1; + TextData textValue = 2; + GUID guidValue = 3; + float floatValue = 4; + EasingData easingData = 5; + Vector vectorValue = 6; + Line lineValue = 7; + Circle circleValue = 8; + Rotation3D rotation3DValue = 9; + CirclePoint circlePointValue = 10; + Gradient gradientValue = 11; + ColorPoint colorPointValue = 12; +} + +message TimelineData { + uint64 durationUs = 1; + bool defaultTimeline = 2; + GUID parentTimelineDefId = 3; + PlaybackStyle playbackStyle = 4; +} + +message TimelineDefinitionsMap { + TimelineDefinitionsMapEntry[] entries = 1; +} + +message TimelineDefinitionsMapEntry { + GUID id = 1; + TimelineData data = 2; +} + +message TimelineAssignmentKey { + GUID assignedTimelineId = 1; + GUID containingTimelineId = 2; +} + +message TimelineBindingData { + int64 offsetUs = 1; + bool disabled = 2; +} + +message TimelineAssignmentsMap { + TimelineAssignmentsMapEntry[] entries = 1; +} + +message TimelineAssignmentsMapEntry { + TimelineAssignmentKey key = 1; + TimelineBindingData value = 2; +} + +enum ComponentPropType { + BOOL = 0; + TEXT = 1; + COLOR = 2; + INSTANCE_SWAP = 3; + VARIANT = 4; + NUMBER = 5; + IMAGE = 6; + SLOT = 7; + EASING = 8; + COLOR_ARRAY = 9; + VECTOR = 10; + LINE = 11; + CIRCLE = 12; + ROTATION_3D = 13; + CIRCLE_POINT = 14; + GRADIENT = 15; + COLOR_POINT = 16; +} + +message ComponentPropPreferredValues { + string[] stringValues = 1; + InstanceSwapPreferredValue[] instanceSwapValues = 2; +} + +message ParameterConfig { + NumberPropConfig numberPropConfig = 1; + ParameterConfigControl control = 2; + SliderConfig sliderConfig = 3; + VariableData label = 4; + InputConfig inputConfig = 5; + SelectConfig selectConfig = 6; + PointConfig pointConfig = 7; + LineConfig lineConfig = 8; + PointRadiusConfig pointRadiusConfig = 9; + Rotation3DConfig rotation3DConfig = 10; + CirclePointConfig circlePointConfig = 11; + ColorPointConfig colorPointConfig = 12; + bool showDividerAbove = 13; +} + +enum ParameterConfigControl { + DEFAULT = 0; + SLIDER = 1; + INPUT = 2; + SELECT = 3; +} + +message InputConfig { + VariableData unit = 1; + VariableData min = 2; + VariableData max = 3; +} + +message SliderConfig { + VariableData min = 1; + VariableData max = 2; + VariableData step = 3; + VariableData unit = 4; +} + +enum PointMode { + CANVAS_AND_UI = 0; + CANVAS = 1; + UI = 2; +} + +message PointConfig { + PointMode mode = 1; + NumberUnits unit = 2; +} + +message Line { + Vector a = 1; + Vector b = 2; +} + +message LineConfig { + PointMode mode = 1; + NumberUnits unit = 2; +} + +message Circle { + Vector center = 1; + float radius = 2; +} + +message PointRadiusConfig { + PointMode mode = 1; + NumberUnits positionUnit = 2; + NumberUnits radiusUnit = 3; + float minRadius = 4; + float maxRadius = 5; +} + +message Rotation3D { + float x = 1; + float y = 2; + float z = 3; + float translateZ = 4; +} + +message Rotation3DConfig { + PointMode mode = 1; +} + +message CirclePoint { + Vector center = 1; + float radius = 2; + float angle = 3; +} + +message CirclePointConfig { + PointMode mode = 1; + NumberUnits positionUnit = 2; + NumberUnits radiusUnit = 3; + float minRadius = 4; + float maxRadius = 5; +} + +message ColorPoint { + Vector point = 1; + VariableData color = 2; +} + +message ColorPointConfig { + PointMode mode = 1; + NumberUnits unit = 2; +} + +message Gradient { + GradientStop[] stops = 1; +} + +message GradientStop { + float position = 1; + VariableData color = 2; +} + +message SelectOption { + VariableData value = 1; + string label = 2; +} + +message SelectConfig { + SelectOption[] options = 1; +} + +message ColorArrayConfig { + uint minLength = 1; + uint maxLength = 2; +} + +message SlotPropConfig { + bool stretchChildOnInsert = 1; + bool displayByDefault = 2; + uint minChildren = 3; + uint maxChildren = 4; + bool allowPreferredValuesOnly = 5; +} + +message NumberPropConfig { + ParameterConfigControl control = 1; + VariableData min = 2; + VariableData max = 3; + VariableData step = 4; +} + +message InstanceSwapPreferredValue { + InstanceSwapPreferredValueType type = 1; + string key = 2; +} + +enum InstanceSwapPreferredValueType { + COMPONENT = 0; + STATE_GROUP = 1; +} + +enum WidgetEvent { + MOUSE_DOWN = 0; + CLICK = 1; + TEXT_EDIT_END = 2; + ATTACHED_STICKABLES_CHANGED = 3; + STUCK_STATUS_CHANGED = 4; +} + +enum WidgetInputBehavior { + WRAP = 0; + TRUNCATE = 1; + MULTILINE = 2; +} + +message WidgetMetadata { + string pluginID = 1; + string pluginVersionID = 2; + string widgetName = 3; + bool isResizable = 4; + bool isRotatable = 5; +} + +enum WidgetPropertyMenuItemType { + ACTION = 0; + SEPARATOR = 1; + COLOR = 2; + DROPDOWN = 3; + COLOR_SELECTOR = 4; + TOGGLE = 5; + LINK = 6; +} + +message WidgetPropertyMenuSelectorOption { + string option = 1; + string tooltip = 2; +} + +enum WidgetInputTextNodeType { + WIDGET_CONTROLLED = 0; + RICH_TEXT = 1; +} + +message WidgetPropertyMenuItem { + string propertyName = 1; + string tooltip = 2; + WidgetPropertyMenuItemType itemType = 3; + string icon = 4; + WidgetPropertyMenuSelectorOption[] options = 5; + string selectedOption = 6; + bool isToggled = 7; + string href = 8; + bool allowCustomColor = 9; +} + +enum CodeBlockLanguage { + TYPESCRIPT = 0; + CPP = 1; + RUBY = 2; + CSS = 3; + JAVASCRIPT = 4; + HTML = 5; + JSON = 6; + GRAPHQL = 7; + PYTHON = 8; + GO = 9; + SQL = 10; + SWIFT = 11; + KOTLIN = 12; + RUST = 13; + BASH = 14; + PLAINTEXT = 15; + MARKDOWN = 16; +} + +enum CodeBlockTheme { + FIGJAM_DARK = 0; + DRACULA = 1; + DUOTONE_SEA = 2; + DUOTONE_SPACE = 3; + DUOTONE_EARTH = 4; + DUOTONE_FOREST = 5; + DUOTONE_LIGHT = 6; +} + +enum SpecBlockType { + DEFAULT = 0; + PARAGRAPH = 1; + HEADING_1 = 2; + HEADING_2 = 3; + HEADING_3 = 4; + HEADING_4 = 5; + HEADING_5 = 6; + HEADING_6 = 7; + CODE_BLOCK = 8; + BLOCK_QUOTE = 9; + HORIZONTAL_RULE = 10; + ORDERED_LIST_ITEM = 11; + UNORDERED_LIST_ITEM = 12; + DOCUMENT = 13; + TABLE = 14; + TABLE_ROW = 15; + TABLE_CELL = 16; + TODO_LIST_ITEM_UNCHECKED = 17; + TODO_LIST_ITEM_CHECKED = 18; + IMAGE = 19; + EMBED = 20; +} + +enum InternalEnumForTest { + OLD = 1; +} + +message InternalDataForTest { + int testFieldA = 1; +} + +message StateGroupPropertyValueOrder { + string property = 1; + string[] values = 2; +} + +enum BackfillError { + NONE = 0; + TRANSIENT_RETRYING = 1; + PERMANENTLY_FAILED = 2; + PASTE_FAILED = 3; +} + +message PartialPasteAnnotation { + bool isPartial = 1; + uint64 annotatedAt = 2; + BackfillError errorState = 3; +} + +message VariantPropSpec { + GUID propDefId = 1; + string value = 2; +} + +message TextListData { + int listID = 1; + BulletType bulletType = 2; + int indentationLevel = 3; + int lineNumber = 4; +} + +enum BulletType { + ORDERED = 0; + UNORDERED = 1; + INDENT = 2; + NO_LIST = 3; +} + +message TextLineData { + LineType lineType = 1; + int styleId = 10; + int indentationLevel = 2; + SourceDirectionality sourceDirectionality = 9; + Directionality directionality = 3; + DirectionalityIntent directionalityIntent = 4; + int downgradeStyleId = 5; + int consistencyStyleId = 6; + int listStartOffset = 7; + bool isFirstLineOfList = 8; +} + +message DerivedTextLineData { + Directionality directionality = 1; +} + +enum LineType { + PLAIN = 0; + ORDERED_LIST = 1; + UNORDERED_LIST = 2; + BLOCKQUOTE = 3; + HEADER = 4; +} + +enum SourceDirectionality { + AUTO = 0; + LTR = 1; + RTL = 2; +} + +enum Directionality { + LTR = 0; + RTL = 1; +} + +enum DirectionalityIntent { + IMPLICIT = 0; + EXPLICIT = 1; +} + +message PrototypeInteraction { + GUID id = 1; + PrototypeEvent event = 2; + PrototypeAction[] actions = 3; + bool isDeleted = 4; + int stateManagementVersion = 5; +} + +message PrototypeEvent { + InteractionType interactionType = 1; + bool interactionMaintained = 2; + float interactionDuration = 3; + KeyTrigger keyTrigger = 4; + string voiceEventPhrase = 5; + float transitionTimeout = 6; + float mediaHitTime = 7; +} + +message PrototypeVariableTarget { + VariableID id = 1; + NodeFieldAlias nodeFieldAlias = 2; +} + +message ConditionalActions { + PrototypeAction[] actions = 1; + VariableData condition = 2; +} + +message PrototypeAction { + GUID transitionNodeID = 1; + TransitionType transitionType = 2; + float transitionDuration = 3; + EasingType easingType = 4; + float transitionTimeout = 5; + bool transitionShouldSmartAnimate = 6; + ConnectionType connectionType = 7; + Vector overlayRelativePosition = 9; + NavigationType navigationType = 10; + bool transitionPreserveScroll = 11; + float[] easingFunction = 12; + Vector extraScrollOffset = 13; + bool transitionResetScrollPosition = 25; + bool transitionResetInteractiveComponents = 26; + bool transitionOverridesEnabled = 42; + string connectionURL = 8; + bool openUrlInNewTab = 18; + VariableData linkParam = 34; + CMSItemPageTarget cmsTarget = 35; + GUID targetVariableID = 14; + VariableAnyValue targetVariableValue = 15; + PrototypeVariableTarget targetVariable = 19; + VariableData targetVariableData = 20; + MediaAction mediaAction = 16; + bool transitionResetVideoPosition = 17; + float mediaSkipToTime = 21; + float mediaSkipByAmount = 22; + float mediaPlaybackRate = 36; + VariableData[] conditions = 23; + ConditionalActions[] conditionalActions = 24; + VariableSetID targetVariableSetID = 27; + GUID targetVariableModeID = 28; + string targetVariableSetKey = 29; + VariableSetID variableSetTargetExtensionId = 38; + AnimationType animationType = 30; + GUID animationTargetId = 31; + AnimationPhase animationPhase = 32; + AnimationState animationState = 33; + bool simpleLink = 37; + AnimationTimelineAction animationTimelineAction = 39; + GUID animationTimelineDefId = 41; + float animationSkipToTime = 40; +} + +enum AnimationPhase { + IN = 0; + OUT = 1; +} + +enum AnimationType { + NONE = 0; + FADE = 1; + SLIDE_FROM_LEFT = 2; + SLIDE_FROM_RIGHT = 3; + SLIDE_FROM_TOP = 4; + SLIDE_FROM_BOTTOM = 5; +} + +message AnimationState { + float opacity = 1; + Matrix transform = 2; +} + +message PrototypeStartingPoint { + string name = 1; + string description = 2; + string position = 3; +} + +enum TriggerDevice { + KEYBOARD = 0; + UNKNOWN_CONTROLLER = 1; + XBOX_ONE = 2; + PS4 = 3; + SWITCH_PRO = 4; +} + +message KeyTrigger { + int[] keyCodes = 1; + TriggerDevice triggerDevice = 2; +} + +message Hyperlink { + string url = 1; + GUID guid = 2; + CMSItemPageTarget cmsTarget = 4; + bool openInNewTab = 3; +} + +message CMSItemPageTarget { + GUID nodeId = 1; + string cmsItemId = 2; + string fieldSchemaId = 3; +} + +enum MentionSource { + DEFAULT = 0; + COPY_DUPLICATE = 1; + SILENT_INSERT = 2; +} + +message Mention { + GUID id = 1; + string mentionedUserId = 2; + string mentionedByUserId = 3; + string fileKey = 4; + MentionSource source = 5; + uint64 mentionedUserIdInt = 6; + uint64 mentionedByUserIdInt = 7; + string mentionedUserGroupId = 8; +} + +message EmbedData { + string url = 1; + string srcUrl = 2; + string title = 3; + string thumbnailUrl = 4; + float width = 5; + float height = 6; + string embedType = 7; + string thumbnailImageHash = 8; + string faviconImageHash = 9; + string provider = 10; + string originalText = 11; + string description = 12; + string embedVersionId = 13; + bool isPublishedSite = 14; +} + +message StampData { + string userId = 1; + string votingSessionId = 2; + string stampedByUserId = 3; +} + +message LinkPreviewData { + string url = 1; + string title = 2; + string provider = 3; + string description = 4; + string thumbnailImageHash = 5; + string faviconImageHash = 6; + float thumbnailImageWidth = 7; + float thumbnailImageHeight = 8; +} + +message Viewport { + Rect canvasSpaceBounds = 1; + bool pixelPreview = 2; + float pixelDensity = 3; + GUID canvasGuid = 4; +} + +message Mouse { + MouseCursor cursor = 1; + Vector canvasSpaceLocation = 2; + Rect canvasSpaceSelectionBox = 3; + GUID canvasGuid = 4; + uint cursorHiddenReason = 5; +} + +struct Click { + uint id; + Vector point; +} + +struct ScrollPosition { + GUID node; + Vector scrollOffset; +} + +struct TriggeredOverlay { + GUID overlayGuid; + GUID hotspotGuid; + GUID swapGuid; +} + +message TriggeredOverlayData { + GUID overlayGuid = 1; + GUID hotspotGuid = 2; + GUID swapGuid = 3; + GUID prototypeInteractionGuid = 4; + GUIDPath hotspotBlueprintId = 5; +} + +message TriggeredSetVariableActionData { + GUID nodeForFindingTopmostScreenId = 1; + string targetVariableId = 2; + string targetVariableData = 3; + string resolvedVariableModes = 4; +} + +message TriggeredSetVariableModeActionData { + GUID nodeForFindingTopmostScreenId = 1; + string targetVariableSetKey = 2; + string targetVariableModeId = 3; + VariableSetID targetVariableSetId = 4; +} + +message VideoStateChangeData { + GUID targetNodeId = 1; + bool isPlaying = 2; + bool isPlayingSound = 3; + uint[] currentTimes = 4; + uint actionTakenTimestamp = 5; +} + +message EmbeddedPrototypeData { + GUID nodeId = 1; + uint sessionId = 2; +} + +message PresentedState { + GUID baseScreenID = 1; + TriggeredOverlayData[] overlays = 2; +} + +enum TransitionDirection { + FORWARD = 0; + REVERSE = 1; +} + +message TopLevelPlaybackChange { + PresentedState oldState = 1; + PresentedState newState = 2; + GUIDPath hotspotBlueprintID = 3; + GUID interactionID = 4; + bool isHotspotInNewPresentedState = 5; + TransitionDirection direction = 6; + GUIDPath instanceStablePath = 7; +} + +message InstanceStateChange { + GUID stateID = 1; + GUID interactionID = 2; + GUIDPath hotspotStablePath = 3; + GUIDPath instanceStablePath = 4; + PlaybackChangePhase phase = 5; +} + +message TextCursor { + Rect selectionBox = 1; + GUID canvasGuid = 2; + GUID textNodeGuid = 3; +} + +message TextSelection { + Rect[] selectionBoxes = 1; + GUID canvasGuid = 2; + GUID textNodeGuid = 3; + Vector textSelectionRange = 4; + GUID textNodeOrContainingIfGuid = 5; + GUID tableCellRowId = 6; + GUID tableCellColId = 7; +} + +enum PlaybackChangePhase { + INITIATED = 0; + ABORTED = 1; + COMMITTED = 2; +} + +message PlaybackChangeKeyframe { + PlaybackChangePhase phase = 1; + float progress = 2; + float timestamp = 3; +} + +message StateMapping { + GUIDPath stablePath = 1; + TopLevelPlaybackChange lastTopLevelChange = 2; + PlaybackChangeKeyframe lastTopLevelChangeStatus = 3; + float timestamp = 4; +} + +message ScrollMapping { + GUIDPath blueprintID = 1; + uint overlayIndex = 2; + Vector scrollOffset = 3; +} + +message PlaybackUpdate { + TopLevelPlaybackChange lastTopLevelChange = 1; + PlaybackChangeKeyframe lastTopLevelChangeStatus = 2; + ScrollMapping[] scrollMappings = 3; + float timestamp = 4; + Vector pointerLocation = 5; + bool isTopLevelFrameChange = 6; + StateMapping[] stateMappings = 7; +} + +message ChatMessage { + string text = 1; + string previousText = 2; +} + +message VoiceMetadata { + string connectedCallId = 1; +} + +message AprilFunCursor { + string id = 1; + bool trailEnabled = 2; +} + +message AprilFunFigPal { + string customization = 1; + string name = 2; +} + +enum Heartbeat { + FOREGROUND = 0; + BACKGROUND = 1; +} + +enum SitesViewState { + FILE = 0; + CODE = 1; + DAKOTA = 2; + SETTINGS = 3; + INSERT = 4; + VARIABLES = 5; +} + +enum DesignFullPageViewState { + NONE = 0; + DESIGN_SYSTEM = 1; + VARIABLES = 2; +} + +message AgentInfo { + string name = 1; + string logo = 2; + string oauthClientId = 3; +} + +message UserChange { + uint sessionID = 1; + string stableSessionID = 44; + bool connected = 2; + string name = 3; + Color color = 4; + string imageURL = 5; + Viewport viewport = 6; + Mouse mouse = 7; + GUID[] selection = 8; + uint[] observing = 9; + string deviceName = 10; + Click[] recentClicks = 11; + ScrollPosition[] scrollPositions = 12; + TriggeredOverlay[] triggeredOverlays = 13; + string userID = 14; + GUID lastTriggeredHotspot = 15; + GUID lastTriggeredPrototypeInteractionID = 16; + uint lastTriggeredObjectAnimationIndex = 38; + TriggeredOverlayData[] triggeredOverlaysData = 17; + PlaybackUpdate[] playbackUpdates = 18; + ChatMessage chatMessage = 19; + VoiceMetadata voiceMetadata = 20; + bool canWrite = 21; + bool highFiveStatus = 22; + InstanceStateChange[] instanceStateChanges = 23; + TextCursor textCursor = 24; + TextSelection textSelection = 25; + uint connectedAtTimeS = 26; + bool focusOnTextCursor = 27; + Heartbeat heartbeat = 28; + TriggeredSetVariableActionData[] triggeredSetVariableActionData = 29; + VideoStateChangeData[] videoStateChangeData = 30; + string clientID = 31; + GUID focusedSlideId = 32; + TriggeredSetVariableModeActionData[] triggeredSetVariableModeActionData = 33; + AprilFunCursor aprilFunCursor = 34; + EmbeddedPrototypeData[] embeddedPrototypeData = 35; + GUID activeSlidesEmbeddablePrototype = 36; + GUID[] activeEmbeddedPrototypes = 43; + GUID activeCodeComponentId = 37; + AprilFunFigPal aprilFunFigPal = 39; + CollaborativeTextSelection collaborativeTextSelection = 40; + SitesViewState sitesViewState = 41; + NodeChatExchange[] nodeChatExchanges = 42; + DesignFullPageViewState designFullPageViewState = 45; + AgentInfo agentInfo = 46; +} + +message InteractiveSlideElementChange { + string userID = 1; + string anonymousUserID = 2; + GUID nodeID = 3; + string responseData = 4; +} + +message NodeStatusChange { + GUID[] nodeIds = 1; + SectionStatusInfo statusInfo = 2; +} + +message BuzzApprovalAssetEntry { + GUID assetNodeId = 1; + bool approved = 2; +} + +message BuzzApprovalChange { + BuzzApprovalAssetEntry[] assetEntries = 1; + GUID canvasGridNodeId = 2; + string requestId = 3; +} + +enum SceneGraphQueryBehavior { + DEFAULT = 0; + CONTAINING_PAGE = 1; + PLUGIN = 2; +} + +enum SceneGraphQueryMode { + ADD = 0; + SET = 1; +} + +message SceneGraphQuery { + GUID startingNode = 1; + uint depth = 2; + SceneGraphQueryBehavior behavior = 3; +} + +message NodeChangesMetadata { + uint blobsFieldOffset = 1; +} + +message CursorReaction { + string imageUrl = 1; +} + +message TimerInfo { + bool isPaused = 1; + uint timeRemainingMs = 2; + uint totalTimeMs = 3; + uint timerID = 4; + string setBy = 5; + uint songID = 6; + uint lastReceivedSongTimestampMs = 7; + string songUUID = 8; +} + +message MusicInfo { + bool isPaused = 1; + uint messageID = 2; + string songID = 3; + uint lastReceivedSongTimestampMs = 4; + bool isStopped = 5; +} + +message PresenterNomination { + uint sessionID = 1; + bool isCancelled = 2; +} + +message PresenterInfo { + uint sessionID = 1; + PresenterNomination nomination = 2; + bool isReconnected = 3; +} + +message ClientBroadcast { + uint sessionID = 1; + CursorReaction cursorReaction = 2; + TimerInfo timer = 3; + PresenterInfo presenter = 4; + PresenterInfo prototypePresenter = 5; + MusicInfo music = 6; +} + +enum PasteAssetType { + UNKNOWN = 0; + VARIABLE = 1; +} + +message Message { + MessageType type = 1; + uint sessionID = 2; + string stableSessionID = 42; + uint ackID = 3; + bool isRetransmission = 37; + NodeChange[] nodeChanges = 4; + UserChange[] userChanges = 5; + InteractiveSlideElementChange interactiveSlideElementChange = 32; + NodeStatusChange nodeStatusChange = 36; + BuzzApprovalChange buzzApprovalChange = 44; + Blob[] blobs = 6; + uint blobBaseIndex = 30; + string signalName = 7; + Access access = 8; + string styleSetName = 9; + StyleSetType styleSetType = 10; + StyleSetContentType styleSetContentType = 11; + int pasteID = 12; + Vector pasteOffset = 13; + string pasteFileKey = 14; + string signalPayload = 15; + SceneGraphQuery[] sceneGraphQueries = 16; + NodeChangesMetadata nodeChangesMetadata = 17; + uint fileVersion = 18; + bool pasteIsPartiallyOutsideEnclosingFrame = 19; + GUID pastePageId = 20; + bool isCut = 21; + Message[] localUndoStack = 22; + Message[] localRedoStack = 23; + ClientBroadcast[] broadcasts = 24; + uint reconnectSequenceNumber = 25; + string pasteBranchSourceFileKey = 26; + EditorType pasteEditorType = 27; + string postSyncActions = 28; + GUID[] publishedAssetGuids = 29; + bool dirtyFromInitialLoad = 31; + ClipboardSelectionRegion[] clipboardSelectionRegions = 33; + EncodedOffsetsIndex encodedOffsetsIndex = 34; + bool hasRepeatingContent = 35; + uint64 sentTimestamp = 38; + AnnotationCategory[] annotationCategories = 39; + ClientRenderedMetadata clientRenderedMetadata = 40; + PasteAssetType pasteAssetType = 41; + ObjectAnimationList objectAnimations = 43; + SceneGraphQueryMode sceneGraphQueryMode = 45; +} + +message EncodedOffsetsIndex { + uint nodeChangesFieldOffset = 1; + uint nodeChangesFieldLength = 2; + uint blobsFieldOffset = 3; + GUIDAndEncodedOffset[] nodeChangeOffsets = 4; +} + +struct GUIDAndEncodedOffset { + GUID guid; + uint offset; +} + +message DiffChunk { + uint[] nodeChanges = 1; + NodePhase phase = 2; + NodeChange displayNode = 3; + GUID canvasId = 4; + string canvasName = 5; + bool canvasIsInternal = 6; + uint[] chunksAffectingThisChunk = 7; + NodeChange[] basisParentHierarchy = 8; + NodeChange[] parentHierarchy = 9; + GUID[] basisParentHierarchyGuids = 10; + GUID[] parentHierarchyGuids = 11; +} + +enum DiffType { + BRANCHING = 0; + NODE_CHANGES_ONLY = 1; +} + +message DiffPayload { + NodeChange[] nodeChanges = 1; + Blob[] blobs = 2; + DiffChunk[] diffChunks = 3; + NodeChange[] diffBasis = 4; + NodeChange[] basisParentNodeChanges = 5; + NodeChange[] parentNodeChanges = 6; + DiffType diffType = 7; +} + +enum RichMediaType { + ANIMATED_IMAGE = 0; + VIDEO = 1; +} + +message RichMediaData { + string mediaHash = 1; + RichMediaType richMediaType = 2; +} + +enum VariableDataType { + BOOLEAN = 0; + FLOAT = 1; + STRING = 2; + ALIAS = 3; + COLOR = 4; + EXPRESSION = 5; + MAP = 6; + SYMBOL_ID = 7; + FONT_STYLE = 8; + TEXT_DATA = 9; + INVALID = 10; + NODE_FIELD_ALIAS = 11; + CMS_ALIAS = 12; + PROP_REF = 13; + IMAGE = 14; + MANAGED_STRING_ALIAS = 15; + LINK = 16; + JS_RUNTIME_ALIAS = 17; + SLOT_CONTENT_ID = 18; + DATE = 19; + KEYFRAME_TRACK_ID = 20; + KEYFRAME_TRACK_PARAMETER_DATA = 21; + EASING = 22; + TIMING = 23; + VECTOR = 24; + COLOR_ARRAY = 25; + LINE = 26; + CIRCLE = 27; + ROTATION_3D = 28; + CIRCLE_POINT = 29; + GRADIENT = 30; + COLOR_POINT = 31; +} + +enum VariableResolvedDataType { + BOOLEAN = 0; + FLOAT = 1; + STRING = 2; + COLOR = 4; + MAP = 5; + SYMBOL_ID = 6; + FONT_STYLE = 7; + TEXT_DATA = 8; + IMAGE = 9; + LINK = 10; + JS_RUNTIME_ALIAS = 11; + SLOT_CONTENT_ID = 12; + KEYFRAME_TRACK_ID = 13; + KEYFRAME_TRACK_PARAMETER_DATA = 14; + EASING = 15; + TIMING = 16; + VECTOR = 17; + COLOR_ARRAY = 18; + LINE = 19; + CIRCLE = 20; + ROTATION_3D = 21; + CIRCLE_POINT = 22; + GRADIENT = 23; + COLOR_POINT = 24; +} + +message VariableAnyValue { + bool boolValue = 1; + string textValue = 2; + float floatValue = 3; + VariableID alias = 4; + Color colorValue = 5; + Expression expressionValue = 6; + VariableMap mapValue = 7; + SymbolId symbolIdValue = 8; + VariableFontStyle fontStyleValue = 9; + TextData textDataValue = 10; + NodeFieldAlias nodeFieldAliasValue = 11; + CMSAlias cmsAliasValue = 12; + PropRefValue propRefValue = 13; + ImageParameterValue imageValue = 14; + ManagedStringAlias managedStringAliasValue = 15; + Hyperlink linkValue = 16; + JsRuntimeAlias jsRuntimeAliasValue = 17; + SlotContentId slotContentIdValue = 18; + KeyframeTrackId keyframeTrackIdValue = 19; + KeyframeTrackParameterValue keyframeTrackParameterValue = 20; + EasingData easingValue = 21; + Vector vectorValue = 22; + ColorArray colorArrayValue = 23; + Line lineValue = 24; + Circle circleValue = 25; + Rotation3D rotation3DValue = 26; + CirclePoint circlePointValue = 27; + Gradient gradientValue = 28; + ColorPoint colorPointValue = 29; +} + +enum ExpressionFunction { + ADDITION = 0; + SUBTRACTION = 1; + RESOLVE_VARIANT = 2; + MULTIPLY = 3; + DIVIDE = 4; + EQUALS = 5; + NOT_EQUAL = 6; + LESS_THAN = 7; + LESS_THAN_OR_EQUAL = 8; + GREATER_THAN = 9; + GREATER_THAN_OR_EQUAL = 10; + AND = 11; + OR = 12; + NOT = 13; + STRINGIFY = 14; + TERNARY = 15; + VAR_MODE_LOOKUP = 16; + NEGATE = 17; + IS_TRUTHY = 18; + KEYFRAME = 19; +} + +message Expression { + ExpressionFunction expressionFunction = 1; + VariableData[] expressionArguments = 2; +} + +message VariableMapValue { + string key = 1; + VariableData value = 2; + GUID guidKey = 3; +} + +message VariableMap { + VariableMapValue[] values = 1; +} + +message ColorArray { + VariableData[] colors = 1; +} + +message VariableFontStyle { + VariableData asString = 1; + VariableData asFloat = 2; + VariableData asVariations = 3; +} + +message ImageParameterValue { + Image image = 1; + Image imageThumbnail = 2; + Image animatedImage = 6; + string altText = 3; + uint originalImageHeight = 4; + uint originalImageWidth = 5; + uint animationFrame = 7; +} + +message ThumbnailInfo { + GUID nodeID = 1; + string thumbnailVersion = 2; +} + +message AiCanvasPrompt { + string userPrompt = 1; + string authorId = 2; + GUID[] parentNodeIds = 3; +} + +message NodeFieldAlias { + GUIDPath stablePathToNode = 1; + NodeFieldAliasType nodeField = 2; + string indexOrKey = 3; +} + +enum NodeFieldAliasType { + MISSING = 0; + COMPONENT_PROP_ASSIGNMENTS = 1; +} + +message CMSAlias { + string collectionId = 1; + string itemId = 2; + string fieldId = 3; + VariableDataType type = 4; +} + +message JsRuntimeAlias { + string lookupKey = 1; +} + +message PropRefValue { + GUID defId = 1; +} + +message ManagedStringId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message ManagedStringPlaceholderMapEntry { + string key = 1; + string value = 2; +} + +message SlotContentId { + GUID guid = 1; +} + +message ManagedStringAlias { + ManagedStringId managedStringId = 1; + ManagedStringPlaceholderMapEntry[] placeholderValues = 2; +} + +message KeyframeTrackId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message AnimationPresetId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message ToolId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message CustomEffectId { + GUID guid = 1; + AssetRef assetRef = 2; +} + +message TRSSTransform2D { + Vector translation = 1; + float rotation = 2; + Vector scale = 3; + float shearX = 4; +} + +message VariableData { + VariableAnyValue value = 1; + VariableDataType dataType = 2; + VariableResolvedDataType resolvedDataType = 3; +} + +message VariableSetMode { + GUID id = 1; + string name = 2; + string sortPosition = 3; + VariableSetID parentVariableSetId = 4; + GUID parentModeId = 5; +} + +message VariableDataValues { + VariableDataValuesEntry[] entries = 1; +} + +message VariableDataValuesEntry { + GUID modeID = 1; + VariableData variableData = 2; +} + +enum VariableScope { + ALL_SCOPES = 0; + TEXT_CONTENT = 1; + CORNER_RADIUS = 2; + WIDTH_HEIGHT = 3; + GAP = 4; + ALL_FILLS = 5; + FRAME_FILL = 6; + SHAPE_FILL = 7; + TEXT_FILL = 8; + STROKE = 9; + STROKE_FLOAT = 10; + EFFECT_FLOAT = 11; + EFFECT_COLOR = 12; + OPACITY = 13; + FONT_STYLE = 14; + FONT_FAMILY = 15; + FONT_SIZE = 16; + LINE_HEIGHT = 17; + LETTER_SPACING = 18; + PARAGRAPH_SPACING = 19; + PARAGRAPH_INDENT = 20; + FONT_VARIATIONS = 21; + TRANSFORM = 22; +} + +message KeyframeAnyValue { + float floatValue = 1; + Color colorValue = 2; + TextData textDataValue = 3; + Vector vectorValue = 4; +} + +enum KeyframeValueType { + FLOAT = 0; + INVALID = 1; + COLOR = 2; + TEXT_DATA = 3; + VECTOR = 4; +} + +message KeyframeValueData { + KeyframeAnyValue value = 1; + KeyframeValueType valueType = 2; +} + +enum KeyframeTrackParameterType { + INVALID = 0; + MANUAL = 1; + ANIMATION_PRESET = 2; +} + +message ManualKeyframeTrackParameter { + KeyframeTrackId keyframeTrackId = 1; + GUID timelineDefId = 2; +} + +message AnimationPresetKeyframeTrackParameter { + AnimationPresetId animationPresetId = 1; + KeyframeTrackId keyframeTrackId = 2; + GUID timelineDefId = 3; +} + +message KeyframeTrackAnyParameter { + ManualKeyframeTrackParameter manual = 1; + AnimationPresetKeyframeTrackParameter animationPreset = 2; + GUID animationStyleBindingId = 3; +} + +message KeyframeTrackParameter { + KeyframeTrackAnyParameter value = 1; + KeyframeTrackParameterType type = 2; +} + +message KeyframeTrackParameterValue { + KeyframeTrackParameter[] parameters = 1; +} + +message AnimationPresets { + AnimationPresetData[] presets = 1; +} + +message AnimationPresetData { + AnimationPresetId animationPresetId = 1; + GUID timelineDefId = 2; +} + +message StyleAnimation { + AnimationPresetId animationPresetId = 1; +} + +message StyleIdForAnimation { + GUID id = 1; + GUID timelineDefId = 2; + StyleId animationStyleId = 3; + int64 timelineOffset = 4; +} + +message Tools { + ToolData[] tools = 1; +} + +message ToolData { + ToolId toolId = 1; + CodeComponentId backingCodeComponentId = 2; + ComponentPropAssignment[] componentPropAssignments = 3; +} + +message CustomEffects { + CustomEffectData[] customEffects = 1; +} + +message CustomEffectData { + CustomEffectId customEffectId = 1; +} + +message SpringParams { + float stiffness = 1; + float damping = 2; + float mass = 3; +} + +message TransitionEasingAnyValue { + SpringParams springEasing = 1; + BezierHandles bezierEasing = 2; +} + +message EasingData { + EasingType easingType = 1; + TransitionEasingAnyValue easingValue = 2; +} + +message TransitionOverride { + GUID id = 1; + float duration = 2; + VariableData durationVar = 3; + float delay = 4; + VariableData delayVar = 5; + EasingData easing = 6; + VariableData easingVar = 7; + uint64 createdAtMs = 8; + GUID[] interactionIDs = 9; + bool disabled = 10; +} + +message TransitionOverrideData { + TransitionOverride[] all = 1; + TransitionOverridePropMap propertyOverrides = 2; +} + +enum TransitionOverrideProp { + ALL = 0; + OPACITY = 1; + TRANSLATION = 2; + ROTATION = 3; + SCALE = 4; +} + +enum TransitionOverrideBindingTopLevelField { + MISSING = 0; + PARAMETER_CONSUMPTION_MAP = 1; + EFFECT_DATA = 2; + FILL_PAINT_DATA = 3; + STROKE_PAINT_DATA = 4; +} + +message TransitionOverrideBindingLocation { + TransitionOverrideBindingTopLevelField topLevelField = 1; + int parameterFieldValue = 2; + int index = 3; + int effectParametrizedFieldValue = 4; +} + +enum KeyframeBindingEffectParametrizedField { + MISSING = 0; + OFFSET_X = 1; + OFFSET_Y = 2; + RADIUS = 3; + SPREAD = 4; + COLOR = 5; + REFRACTION_RADIUS = 6; + SPECULAR_ANGLE = 7; + SPECULAR_INTENSITY = 8; + CHROMATIC_ABERRATION = 9; + SPLAY = 10; + REFRACTION_INTENSITY = 11; + START_RADIUS = 12; + START_OFFSET_X = 13; + START_OFFSET_Y = 14; + END_OFFSET_X = 15; + END_OFFSET_Y = 16; + NOISE_SIZE_X = 17; + NOISE_SIZE_Y = 18; + DENSITY = 19; + EFFECT_OPACITY = 20; + SECONDARY_COLOR = 21; +} + +message NodeContentsKeyframeBindingLocation { + TransitionOverrideBindingTopLevelField topLevelField = 1; + VariableField parameterFieldValue = 2; + int index = 3; + KeyframeBindingEffectParametrizedField effectParametrizedFieldValue = 4; +} + +message TransitionOverridePropMap { + TransitionOverridePropMapEntry[] entries = 1; +} + +message TransitionOverridePropMapEntry { + TransitionOverrideProp prop = 1; + TransitionOverride[] overrides = 2; + TransitionOverrideBindingLocation bindingLocation = 3; + NodeContentsKeyframeBindingLocation nodeContentsBindingLocation = 4; +} + +enum CodeSyntaxPlatform { + WEB = 0; + ANDROID = 1; + iOS = 2; +} + +message OptionalVector { + Vector value = 1; +} + +enum HTMLTag { + AUTO = 0; + ARTICLE = 1; + SECTION = 2; + NAV = 3; + ASIDE = 4; + H1 = 5; + H2 = 6; + H3 = 7; + H4 = 8; + H5 = 9; + H6 = 10; + HGROUP = 11; + HEADER = 12; + FOOTER = 13; + ADDRESS = 14; + P = 15; + HR = 16; + PRE = 17; + BLOCKQUOTE = 18; + OL = 19; + UL = 20; + MENU = 21; + LI = 22; + DL = 23; + DT = 24; + DD = 25; + FIGURE = 26; + FIGCAPTION = 27; + MAIN = 28; + DIV = 29; + A = 30; + EM = 31; + STRONG = 32; + SMALL = 33; + S = 34; + CITE = 35; + Q = 36; + DFN = 37; + ABBR = 38; + RUBY = 39; + RT = 40; + RP = 41; + DATA = 42; + TIME = 43; + CODE = 44; + VAR = 45; + SAMP = 46; + KBD = 47; + SUB = 48; + SUP = 49; + I = 50; + B = 51; + U = 52; + MARK = 53; + BDI = 54; + BDO = 55; + SPAN = 56; + BR = 57; + WBR = 58; + PICTURE = 59; + SOURCE = 60; + IMG = 61; + FORM = 62; + LABEL = 63; + INPUT = 64; + BUTTON = 65; + SELECT = 66; + DATALIST = 67; + OPTGROUP = 68; + OPTION = 69; + TEXTAREA = 70; + OUTPUT = 71; + PROGRESS = 72; + METER = 73; + FIELDSET = 74; + LEGEND = 75; + VIDEO = 76; +} + +enum ARIARole { + AUTO = 0; + NONE = 52; + APPLICATION = 30; + BANNER = 67; + COMPLEMENTARY = 68; + CONTENTINFO = 69; + FORM = 70; + MAIN = 71; + NAVIGATION = 72; + REGION = 73; + SEARCH = 74; + SEPARATOR = 13; + ARTICLE = 31; + COLUMNHEADER = 35; + DEFINITION = 36; + DIRECTORY = 38; + DOCUMENT = 39; + GROUP = 44; + HEADING = 45; + IMG = 46; + LIST = 48; + LISTITEM = 49; + MATH = 50; + NOTE = 53; + PRESENTATION = 55; + ROW = 56; + ROWGROUP = 57; + ROWHEADER = 58; + TABLE = 62; + TOOLBAR = 65; + BUTTON = 1; + CHECKBOX = 2; + GRIDCELL = 3; + LINK = 4; + MENUITEM = 5; + MENUITEMCHECKBOX = 6; + MENUITEMRADIO = 7; + OPTION = 8; + PROGRESSBAR = 9; + RADIO = 10; + SCROLLBAR = 11; + SLIDER = 14; + SPINBUTTON = 15; + TAB = 17; + TABPANEL = 18; + TEXTBOX = 19; + TREEITEM = 20; + COMBOBOX = 21; + GRID = 22; + LISTBOX = 23; + MENU = 24; + MENUBAR = 25; + RADIOGROUP = 26; + TABLIST = 27; + TREE = 28; + TREEGRID = 29; + TOOLTIP = 66; + ALERT = 75; + LOG = 76; + MARQUEE = 77; + STATUS = 78; + TIMER = 79; + ALERTDIALOG = 80; + DIALOG = 81; + SEARCHBOX = 12; + SWITCH = 16; + BLOCKQUOTE = 32; + CAPTION = 33; + CELL = 34; + DELETION = 37; + EMPHASIS = 40; + FEED = 41; + FIGURE = 42; + GENERIC = 43; + INSERTION = 47; + METER = 51; + PARAGRAPH = 54; + STRONG = 59; + SUBSCRIPT = 60; + SUPERSCRIPT = 61; + TERM = 63; + TIME = 64; + IMAGE = 82; + HEADING_1 = 83; + HEADING_2 = 84; + HEADING_3 = 85; + HEADING_4 = 86; + HEADING_5 = 87; + HEADING_6 = 88; + HEADER = 89; + FOOTER = 90; + SIDEBAR = 91; + SECTION = 92; + MAINCONTENT = 93; + TABLE_CELL = 94; + WIDGET = 95; +} + +message MigrationStatus { + bool dsdCleanup = 1; +} + +message NodeFieldMap { + NodeFieldMapEntry[] entries = 1; +} + +message NodeFieldMapEntry { + GUID guid = 1; + uint field = 2; + uint lastModifiedSequenceNumber = 3; +} + +enum ColorProfile { + SRGB = 0; + DISPLAY_P3 = 1; +} + +enum DocumentColorProfile { + LEGACY = 0; + SRGB = 1; + DISPLAY_P3 = 2; +} + +enum ChildReadingDirection { + NONE = 0; + LEFT_TO_RIGHT = 1; + RIGHT_TO_LEFT = 2; +} + +message ARIAAttributeAnyValue { + bool boolValue = 1; + string stringValue = 2; + float floatValue = 3; + int intValue = 4; + string[] stringArrayValue = 5; +} + +enum ARIAAttributeDataType { + BOOLEAN = 0; + STRING = 1; + FLOAT = 2; + INT = 3; + STRING_LIST = 4; +} + +message ARIAAttributeData { + ARIAAttributeDataType type = 1; + ARIAAttributeAnyValue value = 2; +} + +message ARIAAttributesMap { + ARIAAttributesMapEntry[] entries = 1; +} + +message ARIAAttributesMapEntry { + string attribute = 1; + ARIAAttributeData value = 2; +} + +message HandoffStatusMapEntry { + GUID guid = 1; + SectionStatusInfo handoffStatus = 2; +} + +message HandoffStatusMap { + HandoffStatusMapEntry[] entries = 1; +} + +message EditScopeInfo { + EditScopeStack[] editScopeStacks = 1; + EditScopeSnapshot[] snapshots = 2; +} + +message EditScopeSnapshot { + EditScopeStack[] frames = 1; + uint[] nodeChangeFieldNumbers = 2; +} + +message EditScopeStack { + EditScope[] stack = 1; +} + +message EditScope { + EditScopeType type = 1; + string label = 2; + EditorType editorType = 3; +} + +enum EditScopeType { + INVALID = 0; + TEST_SETUP = 1; + USER = 2; + PLUGIN = 3; + SYSTEM = 4; + REST_API = 5; + ONBOARDING = 6; + AUTOSAVE = 7; + AI = 8; +} + +enum SectionPresetState { + INSERTED = 0; + USER_EDITED = 1; +} + +enum EmojiImageSet { + APPLE = 0; + NOTO = 1; +} + +enum SelectionRegionFocusType { + NONE = 0; + PRIMARY = 1; + SECONDARY = 2; +} + +message SectionPresetInfo { + uint64 shelfId = 1; + uint64 templateId = 2; + string templateName = 3; + SectionPresetState state = 4; +} + +message ClipboardSelectionRegion { + GUID parent = 1; + GUID[] nodes = 2; + Vector enclosingFrameOffset = 3; + bool pasteIsPartiallyOutsideEnclosingFrame = 4; + SelectionRegionFocusType focusType = 5; +} + +enum FirstDraftKitType { + LOCAL = 0; + LIBRARY = 1; + NONE = 2; +} + +message FirstDraftKit { + string key = 1; + FirstDraftKitType type = 2; +} + +message FirstDraftData { + string generationId = 1; + FirstDraftKit kit = 2; +} + +enum FirstDraftKitElementType { + NONE = 0; + BUILDING_BLOCK = 1; + GROUPED_COMPONENT = 2; +} + +message FirstDraftKitElementData { + FirstDraftKitElementType type = 1; +} + +enum PlatformShapeProperty { + FILL = 0; + STROKE = 1; + TEXT = 2; + STROKE_COLOR = 3; +} + +enum PlatformShapeBehaviorType { + SHAPE = 0; + CONTAINER = 1; + ADVANCED_CONTAINER = 2; +} + +message PlatformShapePropertyMapEntry { + PlatformShapeProperty property = 1; + GUIDPath[] nodePaths = 2; +} + +message PlatformShapeDefinition { + PlatformShapePropertyMapEntry[] propertyMapEntries = 1; + PlatformShapeBehaviorType behaviorType = 2; + GUIDPath thumbnailNode = 3; +} + +message NodeBehaviors { + LinkBehavior link = 1; + AppearBehavior appear = 2; + HoverBehavior hover = 3; + PressBehavior press = 4; + FocusBehavior focus = 5; + ScrollParallaxBehavior scrollParallax = 6; + ScrollTransformBehavior scrollTransform = 7; + CursorBehavior cursor = 8; + MarqueeBehavior marquee = 9; + CodeBehavior[] code = 10; +} + +message BehaviorTransition { + EasingType easingType = 1; + float[] easingFunction = 2; + float transitionDuration = 3; + float delay = 4; + VariableData transitionDurationVar = 5; + VariableData delayVar = 6; +} + +enum AppearBehaviorTrigger { + PAGE_LOAD = 1; + THIS_LAYER_IN_VIEW = 2; + OTHER_LAYER_IN_VIEW = 3; + SCROLL_DIRECTION = 4; +} + +enum RelativeDirection { + UP = 1; + DOWN = 2; + LEFT = 3; + RIGHT = 4; +} + +message AppearBehavior { + AppearBehaviorTrigger trigger = 1; + RelativeDirection direction = 2; + GUID otherLayer = 3; + BehaviorTransition enterTransition = 4; + NodeChange enterState = 5; + BehaviorTransition exitTransition = 6; + NodeChange exitState = 7; + bool playsOnce = 8; + VariableData playsOnceVar = 9; + bool isDeleted = 10; +} + +message HoverBehavior { + BehaviorTransition transition = 1; + NodeChange state = 2; + bool isDeleted = 3; +} + +message PressBehavior { + BehaviorTransition transition = 1; + NodeChange state = 2; + bool isDeleted = 3; +} + +message FocusBehavior { + BehaviorTransition transition = 1; + NodeChange state = 2; + bool isDeleted = 3; +} + +message ScrollParallaxBehavior { + ScrollDirection axis = 1; + float speed = 2; + bool relativeToPage = 3; + VariableData speedVar = 4; + bool isDeleted = 5; +} + +enum ScrollTransformBehaviorTrigger { + PAGE_HEIGHT = 1; + THIS_LAYER_IN_VIEW = 2; + OTHER_LAYER_IN_VIEW = 3; +} + +message ScrollTransformBehavior { + ScrollTransformBehaviorTrigger trigger = 1; + GUID otherLayer = 2; + BehaviorTransition transition = 3; + NodeChange fromState = 4; + NodeChange toState = 5; + bool playsOnce = 6; + bool playsOnceVar = 7; + VariableData playsOnceVar2 = 8; + bool isDeleted = 9; +} + +message CursorBehavior { + float hotspotX = 1; + float hotspotY = 2; + GUID cursorGuid = 3; + bool isDeleted = 4; +} + +message MarqueeBehavior { + RelativeDirection direction = 1; + float speed = 2; + bool shouldLoopInfinitely = 3; + VariableData speedVar = 4; + VariableData shouldLoopInfinitelyVar = 5; + VariableData pauseOnHover = 6; + bool isDeleted = 7; +} + +message CodeBehavior { + CodeComponentId codeComponentId = 1; + ComponentPropAssignment[] componentPropAssignments = 2; + bool isDeleted = 3; +} + +message ClientRenderedMetadata { + string loadID = 1; + string trackingSessionId = 2; + uint trackingSessionSequenceId = 3; + string reconnectID = 4; +} + +enum LinkBehaviorType { + URL = 1; + PAGE = 2; +} + +message LinkBehavior { + LinkBehaviorType type = 1; + string url = 2; + GUID page = 3; + bool openInNewWindow = 4; +} + +message VariableIdOrVariableOverrideId { + VariableID variableId = 1; + VariableOverrideId variableOverrideId = 2; +} + +struct IndexFontVariationAxis { + string tag; + string name; + float min; + float max; + float defaultValue; +} + +struct IndexFontVariationAxisValue { + string tag; + float value; +} + +message IndexFontStyle { + string name = 1; + string postscript = 2; + float weight = 3; + bool italic = 4; + float stretch = 5; + IndexFontVariationAxisValue[] variationAxisValues = 6; +} + +message IndexFontFile { + string filename = 1; + uint version = 2; + string family = 3; + IndexFontStyle[] styles = 4; + IndexFontVariationAxis[] variationAxes = 5; + bool useFontOpticalSize = 6; +} + +struct IndexFamilyRename { + string oldFamily; + string newFamily; +} + +struct IndexStyleRename { + string oldStyle; + string newStyle; +} + +struct IndexFamilyStylesRename { + string familyName; + IndexStyleRename[] styleRenames; +} + +struct IndexRenames { + IndexFamilyRename[] family; + IndexFamilyStylesRename[] style; +} + +struct IndexEmojiSequence { + uint[] codepoints; +} + +struct IndexEmojis { + uint revision; + uint[] sizes; + IndexEmojiSequence[] sequences; +} + +message FontIndex { + uint schemaVersion = 1; + IndexFontFile[] files = 2; + IndexRenames renames = 3; + IndexEmojis emojis = 4; +} + +message SlideThemeData { + ThemeID themeID = 1; + string version = 2; +} + +enum SlideNumber { + NONE = 0; + SLIDE = 1; + SECTION = 2; + SUBSECTION = 3; + TOTAL_WITHIN_DECK = 4; + TOTAL_WITHIN_SECTION = 5; +} + +enum NodeChatMessageType { + USER_MESSAGE = 0; + ASSISTANT_MESSAGE = 1; + TOOL_MESSAGE = 2; + SYSTEM_MESSAGE = 3; +} + +message NodeChatMessage { + GUID id = 1; + NodeChatMessageType type = 2; + string userId = 3; + string textContent = 4; + uint sentAt = 5; + NodeChatToolCall[] toolCalls = 6; + NodeChatToolResult[] toolResults = 7; + uint64 sentAt64 = 8; +} + +message NodeChatToolCall { + string toolCallId = 1; + string toolName = 2; + string argsJson = 3; +} + +message NodeChatToolResult { + string toolCallId = 1; + string toolName = 2; + string resultJson = 3; +} + +message NodeChatExchange { + GUID node = 1; + NodeChatMessage[] messages = 2; + bool isTyping = 3; + FileUpdate[] fileUpdates = 4; +} + +message NodeChatCompressionState { + uint startIndex = 1; + string summary = 2; +} + +message FileUpdate { + string name = 1; + string contents = 2; + bool isDeleted = 3; +} + +message AIChatContentPart { + AIChatContentPartType type = 1; + AIChatContentPartAnyValue value = 2; +} + +enum AIChatContentPartType { + INVALID = 0; + TEXT = 1; + SELECTED_NODE_IDS = 2; +} + +message AIChatContentPartAnyValue { + string textValue = 1; + string[] selectedNodeIds = 2; +} + +enum AIChatMessageRole { + USER = 0; + ASSISTANT = 1; + TOOL = 2; + SYSTEM = 3; +} + +message AIChatMessage { + uint createdAtMs = 1; + AIChatMessageRole role = 2; + AIChatContentPart[] content = 3; + string clientId = 4; + uint64 createdAtMs64 = 5; +} + +message AIChatThread { + AIChatMessage[] messages = 1; +} + +enum CooperTemplateType { + CUSTOM = 0; + TWITTER_POST = 1; + LINKEDIN_POST = 2; + INSTA_POST_SQUARE = 3; + INSTA_POST_PORTRAIT = 4; + INSTA_STORY = 5; + INSTA_AD = 6; + FACEBOOK_POST = 7; + FACEBOOK_COVER_PHOTO = 8; + FACEBOOK_EVENT_COVER = 9; + FACEBOOK_AD_PORTRAIT = 10; + FACEBOOK_AD_SQUARE = 11; + PINTEREST_AD_PIN = 12; + TWITTER_BANNER = 13; + LINKEDIN_POST_SQUARE = 15; + LINKEDIN_POST_PORTRAIT = 16; + LINKEDIN_POST_LANDSCAPE = 17; + LINKEDIN_PROFILE_BANNER = 18; + LINKEDIN_ARTICLE_BANNER = 19; + LINKEDIN_AD_LANDSCAPE = 20; + LINKEDIN_AD_SQUARE = 21; + LINKEDIN_AD_VERTICAL = 22; + YOUTUBE_THUMBNAIL = 23; + YOUTUBE_BANNER = 24; + YOUTUBE_AD = 25; + TWITCH_BANNER = 26; + GOOGLE_LEADERBOARD_AD = 27; + GOOGLE_LARGE_AD = 28; + GOOGLE_MED_AD = 29; + GOOGLE_MOBILE_BANNER_AD = 30; + GOOGLE_SKYSCRAPER_AD = 31; + CARD_HORIZONTAL = 32; + CARD_VERTICAL = 33; + PRINT_US_LETTER = 34; + POSTER = 35; + BANNER_STANDARD = 36; + BANNER_WIDE = 37; + BANNER_ULTRAWIDE = 38; + NAME_TAG_PORTRAIT = 39; + NAME_TAG_LANDSCAPE = 40; + INSTA_REEL_COVER = 41; + ZOOM_BACKGROUND = 42; + TIKTOK_POST = 43; + INSTA_AD_PORTRAIT = 44; + INSTA_POST_TALL_PORTRAIT = 45; + TWITTER_POST_SQUARE = 46; + FACEBOOK_POST_SQUARE = 47; + FACEBOOK_POST_PORTRAIT = 48; + FACEBOOK_STORY = 49; + GOOGLE_SQUARE_AD = 50; + GOOGLE_SMALL_SQUARE_AD = 51; + GOOGLE_NARROW_SKYSCRAPER_AD = 52; + GOOGLE_HALF_PAGE_AD = 53; + GOOGLE_LARGE_LEADERBOARD_AD = 54; + GOOGLE_BILLBOARD_AD = 55; + GOOGLE_BANNER_LEADERBOARD_AD = 56; + GOOGLE_TOP_BANNER_AD = 57; + GOOGLE_MOBILE_LEADERBOARD_BANNER_AD = 58; + GOOGLE_LARGE_MOBILE_BANNER_AD = 59; + GOOGLE_MOBILE_INTERSTITIAL_AD = 60; + GOOGLE_MOBILE_MED_RECTANGLE_AD = 61; + PINTEREST_PIN_STANDARD = 62; + PINTEREST_PIN_SQUARE = 63; + PINTEREST_AD_SQUARE = 64; + PRINT_A4 = 65; +} + +message CooperTemplateData { + CooperTemplateType type = 1; +} + +message ImageImportMap { + ImageImport[] imports = 1; +} + +message ImageImport { + string name = 1; + Image image = 2; +} + +enum InterpolationType { + HOLD = 0; + BEZIER = 1; + SPRING = 2; +} + +message BezierHandles { + float p1x = 1; + float p1y = 2; + float p2x = 3; + float p2y = 4; +} + +enum KeyframeOperation { + SET = 0; + SCALE = 1; + OFFSET = 2; +} + +enum TimelinePositionType { + ABSOLUTE = 0; + RELATIVE = 1; +} + +enum PlaybackStyle { + ONCE = 0; + LOOP = 1; + BOOMERANG = 2; +} diff --git a/packages/kiwi/src/global.d.ts b/packages/kiwi/src/global.d.ts new file mode 100644 index 000000000..7461f762f --- /dev/null +++ b/packages/kiwi/src/global.d.ts @@ -0,0 +1,4 @@ +declare module '*.kiwi?raw' { + const content: string + export default content +} diff --git a/packages/kiwi/src/index.ts b/packages/kiwi/src/index.ts new file mode 100644 index 000000000..91082a251 --- /dev/null +++ b/packages/kiwi/src/index.ts @@ -0,0 +1,2 @@ +export * from './schema-runtime' +export * from './fig' diff --git a/packages/kiwi/src/schema-runtime/bb.ts b/packages/kiwi/src/schema-runtime/bb.ts new file mode 100644 index 000000000..e99b89e69 --- /dev/null +++ b/packages/kiwi/src/schema-runtime/bb.ts @@ -0,0 +1,252 @@ +let int32 = new Int32Array(1) +let float32 = new Float32Array(int32.buffer) +const textDecoder = new TextDecoder() + +export class ByteBuffer { + private _data: Uint8Array + private _index: number + length: number + + constructor(data?: Uint8Array) { + if (data && !(data instanceof Uint8Array)) { + throw new Error('Must initialize a ByteBuffer with a Uint8Array') + } + this._data = data || new Uint8Array(256) + this._index = 0 + this.length = data ? data.length : 0 + } + + /** + * Returns a view into the internal buffer, not a copy. + * + * Consumers transferring this Uint8Array across thread boundaries (e.g. via + * postMessage with transferables) MUST copy first: `new Uint8Array(buffer)`. + * Otherwise, if multiple Uint8Arrays share the same underlying ArrayBuffer and + * one is transferred, all views into that buffer become detached. + */ + toUint8Array(): Uint8Array { + return this._data.subarray(0, this.length) + } + + readByte(): number { + return this._data[this._index++] + } + + readByteArray(): Uint8Array { + const length = this.readVarUint() + const start = this._index + this._index = start + length + return this._data.slice(start, start + length) + } + + readVarFloat(): number { + const index = this._index + const data = this._data + const first = data[index] + if (first === 0) { + this._index = index + 1 + return 0 + } + + let bits = first | (data[index + 1] << 8) | (data[index + 2] << 16) | (data[index + 3] << 24) + this._index = index + 4 + bits = (bits << 23) | (bits >>> 9) + int32[0] = bits + return float32[0] + } + + readVarUint(): number { + const data = this._data + let i = this._index + let b = data[i++] + let value = b & 127 + if (b < 128) { + this._index = i + return value + } + b = data[i++] + value |= (b & 127) << 7 + if (b < 128) { + this._index = i + return value + } + b = data[i++] + value |= (b & 127) << 14 + if (b < 128) { + this._index = i + return value + } + b = data[i++] + value |= (b & 127) << 21 + if (b < 128) { + this._index = i + return value + } + b = data[i++] + value |= (b & 127) << 28 + this._index = i + return value >>> 0 + } + + readVarInt(): number { + let value = this.readVarUint() | 0 + return value & 1 ? ~(value >>> 1) : value >>> 1 + } + + readVarUint64(): bigint { + let value = BigInt(0) + let shift = BigInt(0) + let seven = BigInt(7) + let byte: number + while ((byte = this.readByte()) & 128 && shift < 56) { + value |= BigInt(byte & 127) << shift + shift += seven + } + value |= BigInt(byte) << shift + return value + } + + readVarInt64(): bigint { + let value = this.readVarUint64() + let one = BigInt(1) + let sign = value & one + value >>= one + return sign ? ~value : value + } + + readString(): string { + const data = this._data + const start = this._index + let i = start + while (data[i] !== 0) i++ + this._index = i + 1 + return textDecoder.decode(data.subarray(start, i)) + } + + private _growBy(amount: number): void { + if (this.length + amount > this._data.length) { + let data = new Uint8Array((this.length + amount) << 1) + data.set(this._data) + this._data = data + } + this.length += amount + } + + writeByte(value: number): void { + let index = this.length + this._growBy(1) + this._data[index] = value + } + + writeByteArray(value: Uint8Array): void { + this.writeVarUint(value.length) + let index = this.length + this._growBy(value.length) + this._data.set(value, index) + } + + writeVarFloat(value: number): void { + let index = this.length + + // Reinterpret as an integer + float32[0] = value + let bits = int32[0] + + // Move the exponent to the first 8 bits + bits = (bits >>> 23) | (bits << 9) + + // Optimization: use a single byte to store zero and denormals (check for an exponent of 0) + if ((bits & 255) === 0) { + this.writeByte(0) + return + } + + // Endian-independent 32-bit write + this._growBy(4) + let data = this._data + data[index] = bits + data[index + 1] = bits >> 8 + data[index + 2] = bits >> 16 + data[index + 3] = bits >> 24 + } + + writeVarUint(value: number): void { + if (value < 0 || value > 0xffff_ffff) throw new Error('Outside uint range: ' + value) + do { + let byte = value & 127 + value >>>= 7 + this.writeByte(value ? byte | 128 : byte) + } while (value) + } + + writeVarInt(value: number): void { + if (value < -0x8000_0000 || value > 0x7fff_ffff) throw new Error('Outside int range: ' + value) + this.writeVarUint(((value << 1) ^ (value >> 31)) >>> 0) + } + + writeVarUint64(value: bigint | string): void { + if (typeof value === 'string') value = BigInt(value) + else if (typeof value !== 'bigint') + throw new Error(`Expected bigint but got ${typeof value}: ${String(value)}`) + if (value < 0 || value > BigInt('0xFFFFFFFFFFFFFFFF')) + throw new Error('Outside uint64 range: ' + value) + let mask = BigInt(127) + let seven = BigInt(7) + for (let i = 0; value > mask && i < 8; i++) { + this.writeByte(Number(value & mask) | 128) + value >>= seven + } + this.writeByte(Number(value)) + } + + writeVarInt64(value: bigint | string): void { + if (typeof value === 'string') value = BigInt(value) + else if (typeof value !== 'bigint') + throw new Error(`Expected bigint but got ${typeof value}: ${String(value)}`) + if (value < -BigInt('0x8000000000000000') || value > BigInt('0x7FFFFFFFFFFFFFFF')) + throw new Error('Outside int64 range: ' + value) + let one = BigInt(1) + this.writeVarUint64(value < 0 ? ~(value << one) : value << one) + } + + writeString(value: string): void { + let codePoint + + for (let i = 0; i < value.length; i++) { + // Decode UTF-16 + let a = value.charCodeAt(i) + if (i + 1 === value.length || a < 0xd800 || a >= 0xdc00) { + codePoint = a + } else { + let b = value.charCodeAt(++i) + codePoint = (a << 10) + b + (0x10000 - (0xd800 << 10) - 0xdc00) + } + + // Strings are null-terminated + if (codePoint === 0) { + throw new Error('Cannot encode a string containing the null character') + } + + // Encode UTF-8 + if (codePoint < 0x80) { + this.writeByte(codePoint) + } else { + if (codePoint < 0x800) { + this.writeByte(((codePoint >> 6) & 0x1f) | 0xc0) + } else { + if (codePoint < 0x10000) { + this.writeByte(((codePoint >> 12) & 0x0f) | 0xe0) + } else { + this.writeByte(((codePoint >> 18) & 0x07) | 0xf0) + this.writeByte(((codePoint >> 12) & 0x3f) | 0x80) + } + this.writeByte(((codePoint >> 6) & 0x3f) | 0x80) + } + this.writeByte((codePoint & 0x3f) | 0x80) + } + } + + // Strings are null-terminated + this.writeByte(0) + } +} diff --git a/packages/kiwi/src/schema-runtime/binary.ts b/packages/kiwi/src/schema-runtime/binary.ts new file mode 100644 index 000000000..9e6af4691 --- /dev/null +++ b/packages/kiwi/src/schema-runtime/binary.ts @@ -0,0 +1,102 @@ +import { ByteBuffer } from './bb' +import { Schema, Field, Definition, DefinitionKind } from './schema' + +let types: (string | null)[] = ['bool', 'byte', 'int', 'uint', 'float', 'string', 'int64', 'uint64'] +let kinds: DefinitionKind[] = ['ENUM', 'STRUCT', 'MESSAGE'] + +export function decodeBinarySchema(buffer: Uint8Array | ByteBuffer): Schema { + let bb = buffer instanceof ByteBuffer ? buffer : new ByteBuffer(buffer) + let definitionCount = bb.readVarUint() + let definitions: Definition[] = [] + + // Read in the schema + for (let i = 0; i < definitionCount; i++) { + let definitionName = bb.readString() + let kind = bb.readByte() + let fieldCount = bb.readVarUint() + let fields: Field[] = [] + + for (let j = 0; j < fieldCount; j++) { + let fieldName = bb.readString() + let type = bb.readVarInt() + let isArray = !!(bb.readByte() & 1) + let value = bb.readVarUint() + + fields.push({ + name: fieldName, + line: 0, + column: 0, + type: kinds[kind] === 'ENUM' ? null : (type as any), + isArray: isArray, + isDeprecated: false, + value: value + }) + } + + definitions.push({ + name: definitionName, + line: 0, + column: 0, + kind: kinds[kind], + fields: fields + }) + } + + // Bind type names afterwards + for (let i = 0; i < definitionCount; i++) { + let fields = definitions[i].fields + for (let j = 0; j < fields.length; j++) { + let field = fields[j] + let type = field.type as any as number | null + + if (type !== null && type < 0) { + if (~type >= types.length) { + throw new Error('Invalid type ' + type) + } + field.type = types[~type] + } else { + if (type !== null && type >= definitions.length) { + throw new Error('Invalid type ' + type) + } + field.type = type === null ? null : definitions[type].name + } + } + } + + return { + package: null, + definitions: definitions + } +} + +export function encodeBinarySchema(schema: Schema): Uint8Array { + let bb = new ByteBuffer() + let definitions = schema.definitions + let definitionIndex: { [name: string]: number } = {} + + bb.writeVarUint(definitions.length) + + for (let i = 0; i < definitions.length; i++) { + definitionIndex[definitions[i].name] = i + } + + for (let i = 0; i < definitions.length; i++) { + let definition = definitions[i] + + bb.writeString(definition.name) + bb.writeByte(kinds.indexOf(definition.kind)) + bb.writeVarUint(definition.fields.length) + + for (let j = 0; j < definition.fields.length; j++) { + let field = definition.fields[j] + let type = types.indexOf(field.type) + + bb.writeString(field.name) + bb.writeVarInt(type === -1 ? definitionIndex[field.type!] : ~type) + bb.writeByte(field.isArray ? 1 : 0) + bb.writeVarUint(field.value) + } + } + + return bb.toUint8Array() +} diff --git a/packages/kiwi/src/schema-runtime/index.ts b/packages/kiwi/src/schema-runtime/index.ts new file mode 100644 index 000000000..9b8cbc5d4 --- /dev/null +++ b/packages/kiwi/src/schema-runtime/index.ts @@ -0,0 +1,12 @@ +export type { Schema, Definition, Field } from './schema' +export { ByteBuffer } from './bb' +export { compileSchema } from './js' +export { decodeBinarySchema, encodeBinarySchema } from './binary' +export { parseSchema } from './parser' +export { + validateSchema, + expectFieldNumber, + expectEnumValue, + findDefinition, + findField +} from './validate' diff --git a/packages/kiwi/src/schema-runtime/js.ts b/packages/kiwi/src/schema-runtime/js.ts new file mode 100644 index 000000000..63649e7e8 --- /dev/null +++ b/packages/kiwi/src/schema-runtime/js.ts @@ -0,0 +1,339 @@ +import { ByteBuffer } from './bb' +import { Schema, Definition } from './schema' +import { error, quote } from './util' + +function compileDecode( + definition: Definition, + definitions: { [name: string]: Definition } +): string { + let lines: string[] = [] + let indent = ' ' + + lines.push('function (bb) {') + lines.push(' var result = {};') + lines.push(' if (!(bb instanceof this.ByteBuffer)) {') + lines.push(' bb = new this.ByteBuffer(bb);') + lines.push(' }') + lines.push('') + + if (definition.kind === 'MESSAGE') { + lines.push(' while (true) {') + lines.push(' switch (bb.readVarUint()) {') + lines.push(' case 0:') + lines.push(' return result;') + lines.push('') + indent = ' ' + } + + for (let i = 0; i < definition.fields.length; i++) { + let field = definition.fields[i] + let code: string + + switch (field.type) { + case 'bool': { + code = '!!bb.readByte()' + break + } + + case 'byte': { + code = 'bb.readByte()' // only used if not array + break + } + + case 'int': { + code = 'bb.readVarInt()' + break + } + + case 'uint': { + code = 'bb.readVarUint()' + break + } + + case 'float': { + code = 'bb.readVarFloat()' + break + } + + case 'string': { + code = 'bb.readString()' + break + } + + case 'int64': { + code = 'bb.readVarInt64()' + break + } + + case 'uint64': { + code = 'bb.readVarUint64()' + break + } + + default: { + let type = definitions[field.type!] + if (!type) { + error( + 'Invalid type ' + quote(field.type!) + ' for field ' + quote(field.name), + field.line, + field.column + ) + } else if (type.kind === 'ENUM') { + code = 'this[' + quote(type.name) + '][bb.readVarUint()]' + } else { + code = 'this[' + quote('decode' + type.name) + '](bb)' + } + } + } + + if (definition.kind === 'MESSAGE') { + lines.push(' case ' + field.value + ':') + } + + if (field.isArray) { + if (field.isDeprecated) { + if (field.type === 'byte') { + lines.push(indent + 'bb.readByteArray();') + } else { + lines.push(indent + 'var length = bb.readVarUint();') + lines.push(indent + 'while (length-- > 0) ' + code + ';') + } + } else { + if (field.type === 'byte') { + lines.push(indent + 'result[' + quote(field.name) + '] = bb.readByteArray();') + } else { + lines.push(indent + 'var length = bb.readVarUint();') + lines.push(indent + 'var values = result[' + quote(field.name) + '] = Array(length);') + lines.push(indent + 'for (var i = 0; i < length; i++) values[i] = ' + code + ';') + } + } + } else { + if (field.isDeprecated) { + lines.push(indent + code + ';') + } else { + lines.push(indent + 'result[' + quote(field.name) + '] = ' + code + ';') + } + } + + if (definition.kind === 'MESSAGE') { + lines.push(' break;') + lines.push('') + } + } + + if (definition.kind === 'MESSAGE') { + lines.push(' default:') + lines.push(' throw new Error("Attempted to parse invalid message");') + lines.push(' }') + lines.push(' }') + } else { + lines.push(' return result;') + } + + lines.push('}') + + return lines.join('\n') +} + +function compileEncode( + definition: Definition, + definitions: { [name: string]: Definition } +): string { + let lines: string[] = [] + + lines.push('function (message, bb) {') + lines.push(' var isTopLevel = !bb;') + lines.push(' if (isTopLevel) bb = new this.ByteBuffer();') + + for (let j = 0; j < definition.fields.length; j++) { + let field = definition.fields[j] + let code: string + + if (field.isDeprecated) { + continue + } + + switch (field.type) { + case 'bool': { + code = 'bb.writeByte(value);' + break + } + + case 'byte': { + code = 'bb.writeByte(value);' // only used if not array + break + } + + case 'int': { + code = 'bb.writeVarInt(value);' + break + } + + case 'uint': { + code = 'bb.writeVarUint(value);' + break + } + + case 'float': { + code = 'bb.writeVarFloat(value);' + break + } + + case 'string': { + code = 'bb.writeString(value);' + break + } + + case 'int64': { + code = 'bb.writeVarInt64(value);' + break + } + + case 'uint64': { + code = 'bb.writeVarUint64(value);' + break + } + + default: { + let type = definitions[field.type!] + if (!type) { + throw new Error('Invalid type ' + quote(field.type!) + ' for field ' + quote(field.name)) + } else if (type.kind === 'ENUM') { + code = + 'var encoded = this[' + + quote(type.name) + + '][value]; ' + + 'if (encoded === void 0) throw new Error("Invalid value " + JSON.stringify(value) + ' + + quote(' for enum ' + quote(type.name)) + + '); ' + + 'bb.writeVarUint(encoded);' + } else { + code = 'this[' + quote('encode' + type.name) + '](value, bb);' + } + } + } + + lines.push('') + lines.push(' var value = message[' + quote(field.name) + '];') + lines.push(' if (value != null) {') // Comparing with null using "!=" also checks for undefined + + if (definition.kind === 'MESSAGE') { + lines.push(' bb.writeVarUint(' + field.value + ');') + } + + if (field.isArray) { + if (field.type === 'byte') { + lines.push(' bb.writeByteArray(value);') + } else { + lines.push(' var values = value, n = values.length;') + lines.push(' bb.writeVarUint(n);') + lines.push(' for (var i = 0; i < n; i++) {') + lines.push(' value = values[i];') + lines.push(' ' + code) + lines.push(' }') + } + } else { + lines.push(' ' + code) + } + + if (definition.kind === 'STRUCT') { + lines.push(' } else {') + lines.push( + ' throw new Error(' + quote('Missing required field ' + quote(field.name)) + ');' + ) + } + + lines.push(' }') + } + + // A field id of zero is reserved to indicate the end of the message + if (definition.kind === 'MESSAGE') { + lines.push(' bb.writeVarUint(0);') + } + + lines.push('') + lines.push(' if (isTopLevel) return bb.toUint8Array();') + lines.push('}') + + return lines.join('\n') +} + +export function compileSchemaJS(schema: Schema): string { + let definitions: { [name: string]: Definition } = {} + let name = schema.package + let js: string[] = [] + + if (name !== null) { + js.push('var ' + name + ' = exports || ' + name + ' || {}, exports;') + } else { + js.push('var exports = exports || {};') + name = 'exports' + } + + js.push(name + '.ByteBuffer = ' + name + '.ByteBuffer || require("kiwi-schema").ByteBuffer;') + + for (let i = 0; i < schema.definitions.length; i++) { + let definition = schema.definitions[i] + definitions[definition.name] = definition + } + + for (let i = 0; i < schema.definitions.length; i++) { + let definition = schema.definitions[i] + + switch (definition.kind) { + case 'ENUM': { + let value: any = {} + for (let j = 0; j < definition.fields.length; j++) { + let field = definition.fields[j] + value[field.name] = field.value + value[field.value] = field.name + } + js.push(name + '[' + quote(definition.name) + '] = ' + JSON.stringify(value, null, 2) + ';') + break + } + + case 'STRUCT': + case 'MESSAGE': { + js.push('') + js.push( + name + + '[' + + quote('decode' + definition.name) + + '] = ' + + compileDecode(definition, definitions) + + ';' + ) + js.push('') + js.push( + name + + '[' + + quote('encode' + definition.name) + + '] = ' + + compileEncode(definition, definitions) + + ';' + ) + break + } + + default: { + error( + 'Invalid definition kind ' + quote(definition.kind), + definition.line, + definition.column + ) + break + } + } + } + + js.push('') + return js.join('\n') +} + +export function compileSchema(schema: Schema): any { + let result = { + ByteBuffer: ByteBuffer + } + new Function('exports', compileSchemaJS(schema))(result) + return result +} diff --git a/packages/kiwi/src/schema-runtime/parser.ts b/packages/kiwi/src/schema-runtime/parser.ts new file mode 100644 index 000000000..7f599623d --- /dev/null +++ b/packages/kiwi/src/schema-runtime/parser.ts @@ -0,0 +1,287 @@ +import { Schema, Definition, Field, DefinitionKind } from './schema' +import { error, quote } from './util' + +export let nativeTypes = ['bool', 'byte', 'float', 'int', 'int64', 'string', 'uint', 'uint64'] + +// These are special names on the object returned by compileSchema() +export let reservedNames = ['ByteBuffer', 'package'] + +let regex = /((?:-|\b)\d+\b|[=;{}]|\[\]|\[deprecated\]|\b[A-Za-z_][A-Za-z0-9_]*\b|\/\/.*|\s+)/g +let identifier = /^[A-Za-z_][A-Za-z0-9_]*$/ +let whitespace = /^\/\/.*|\s+$/ +let equals = /^=$/ +let endOfFile = /^$/ +let semicolon = /^;$/ +let integer = /^-?\d+$/ +let leftBrace = /^\{$/ +let rightBrace = /^\}$/ +let arrayToken = /^\[\]$/ +let enumKeyword = /^enum$/ +let structKeyword = /^struct$/ +let messageKeyword = /^message$/ +let packageKeyword = /^package$/ +let deprecatedToken = /^\[deprecated\]$/ + +interface Token { + text: string + line: number + column: number +} + +function tokenize(text: string): Token[] { + let parts = text.split(regex) + let tokens = [] + let column = 0 + let line = 0 + + for (let i = 0; i < parts.length; i++) { + let part = parts[i] + + // Keep non-whitespace tokens + if (i & 1) { + if (!whitespace.test(part)) { + tokens.push({ + text: part, + line: line + 1, + column: column + 1 + }) + } + } + + // Detect syntax errors + else if (part !== '') { + error('Syntax error ' + quote(part), line + 1, column + 1) + } + + // Keep track of the line and column counts + let lines = part.split('\n') + if (lines.length > 1) column = 0 + line += lines.length - 1 + column += lines[lines.length - 1].length + } + + // End-of-file token + tokens.push({ + text: '', + line: line, + column: column + }) + + return tokens +} + +function parse(tokens: Token[]): Schema { + function current(): Token { + return tokens[index] + } + + function eat(test: RegExp): boolean { + if (test.test(current().text)) { + index++ + return true + } + return false + } + + function expect(test: RegExp, expected: string): void { + if (!eat(test)) { + let token = current() + error('Expected ' + expected + ' but found ' + quote(token.text), token.line, token.column) + } + } + + function unexpectedToken(): never { + let token = current() + error('Unexpected token ' + quote(token.text), token.line, token.column) + } + + let definitions: Definition[] = [] + let packageText = null + let index = 0 + + if (eat(packageKeyword)) { + packageText = current().text + expect(identifier, 'identifier') + expect(semicolon, '";"') + } + + while (index < tokens.length && !eat(endOfFile)) { + let fields: Field[] = [] + let kind: DefinitionKind + + if (eat(enumKeyword)) kind = 'ENUM' + else if (eat(structKeyword)) kind = 'STRUCT' + else if (eat(messageKeyword)) kind = 'MESSAGE' + else unexpectedToken() + + // All definitions start off the same + let name = current() + expect(identifier, 'identifier') + expect(leftBrace, '"{"') + + // Parse fields + while (!eat(rightBrace)) { + let type: string | null = null + let isArray = false + let isDeprecated = false + + // Enums don't have types + if (kind !== 'ENUM') { + type = current().text + expect(identifier, 'identifier') + isArray = eat(arrayToken) + } + + let field = current() + expect(identifier, 'identifier') + + // Structs don't have explicit values + let value: Token | null = null + if (kind !== 'STRUCT') { + expect(equals, '"="') + value = current() + expect(integer, 'integer') + + if ((+value.text | 0) + '' !== value.text) { + error('Invalid integer ' + quote(value.text), value.line, value.column) + } + } + + let deprecated = current() + if (eat(deprecatedToken)) { + if (kind !== 'MESSAGE') { + error('Cannot deprecate this field', deprecated.line, deprecated.column) + } + + isDeprecated = true + } + + expect(semicolon, '";"') + + fields.push({ + name: field.text, + line: field.line, + column: field.column, + type: type, + isArray: isArray, + isDeprecated: isDeprecated, + value: value !== null ? +value.text | 0 : fields.length + 1 + }) + } + + definitions.push({ + name: name.text, + line: name.line, + column: name.column, + kind: kind, + fields: fields + }) + } + + return { + package: packageText, + definitions: definitions + } +} + +function verify(root: Schema): void { + let definedTypes = nativeTypes.slice() + let definitions: { [name: string]: Definition } = {} + + // Define definitions + for (let i = 0; i < root.definitions.length; i++) { + let definition = root.definitions[i] + if (definedTypes.includes(definition.name)) { + error( + 'The type ' + quote(definition.name) + ' is defined twice', + definition.line, + definition.column + ) + } + if (reservedNames.includes(definition.name)) { + error( + 'The type name ' + quote(definition.name) + ' is reserved', + definition.line, + definition.column + ) + } + definedTypes.push(definition.name) + definitions[definition.name] = definition + } + + // Check fields + for (let i = 0; i < root.definitions.length; i++) { + let definition = root.definitions[i] + let fields = definition.fields + + if (definition.kind === 'ENUM' || fields.length === 0) { + continue + } + + // Check types + for (let j = 0; j < fields.length; j++) { + let field = fields[j] + if (!definedTypes.includes(field.type!)) { + error( + 'The type ' + quote(field.type!) + ' is not defined for field ' + quote(field.name), + field.line, + field.column + ) + } + } + + // Check values + let values: number[] = [] + for (let j = 0; j < fields.length; j++) { + let field = fields[j] + if (values.includes(field.value)) { + error('The id for field ' + quote(field.name) + ' is used twice', field.line, field.column) + } + if (field.value <= 0) { + error( + 'The id for field ' + quote(field.name) + ' must be positive', + field.line, + field.column + ) + } + // Figma schema uses sparse field IDs (up to 435), so skip sequential check + values.push(field.value) + } + } + + // Check that structs don't contain themselves + let state: { [name: string]: number } = {} + let check = (name: string): boolean => { + let definition = definitions[name] + if (definition && definition.kind === 'STRUCT') { + if (state[name] === 1) { + error( + 'Recursive nesting of ' + quote(name) + ' is not allowed', + definition.line, + definition.column + ) + } + if (state[name] !== 2 && definition) { + state[name] = 1 + let fields = definition.fields + for (let i = 0; i < fields.length; i++) { + let field = fields[i] + if (!field.isArray) { + check(field.type!) + } + } + state[name] = 2 + } + } + return true + } + for (let i = 0; i < root.definitions.length; i++) { + check(root.definitions[i].name) + } +} + +export function parseSchema(text: string): Schema { + let schema = parse(tokenize(text)) + verify(schema) + return schema +} diff --git a/packages/kiwi/src/schema-runtime/schema.ts b/packages/kiwi/src/schema-runtime/schema.ts new file mode 100644 index 000000000..82786ef59 --- /dev/null +++ b/packages/kiwi/src/schema-runtime/schema.ts @@ -0,0 +1,24 @@ +export interface Schema { + package: string | null + definitions: Definition[] +} + +export type DefinitionKind = 'ENUM' | 'STRUCT' | 'MESSAGE' + +export interface Definition { + name: string + line: number + column: number + kind: DefinitionKind + fields: Field[] +} + +export interface Field { + name: string + line: number + column: number + type: string | null + isArray: boolean + isDeprecated: boolean + value: number +} diff --git a/packages/kiwi/src/schema-runtime/util.ts b/packages/kiwi/src/schema-runtime/util.ts new file mode 100644 index 000000000..2925ed6dd --- /dev/null +++ b/packages/kiwi/src/schema-runtime/util.ts @@ -0,0 +1,10 @@ +export function quote(text: string): string { + return JSON.stringify(text) +} + +export function error(text: string, line: number, column: number): never { + var error = new Error(text) + ;(error as any).line = line + ;(error as any).column = column + throw error +} diff --git a/packages/kiwi/src/schema-runtime/validate.ts b/packages/kiwi/src/schema-runtime/validate.ts new file mode 100644 index 000000000..367a0f4a7 --- /dev/null +++ b/packages/kiwi/src/schema-runtime/validate.ts @@ -0,0 +1,88 @@ +import type { Definition, Field, Schema } from './schema' +import { error, quote } from './util' + +export function findDefinition(schema: Schema, name: string): Definition | null { + return schema.definitions.find((definition) => definition.name === name) ?? null +} + +export function findField(schema: Schema, definitionName: string, fieldName: string): Field | null { + return ( + findDefinition(schema, definitionName)?.fields.find((field) => field.name === fieldName) ?? null + ) +} + +export function expectFieldNumber( + schema: Schema, + definitionName: string, + fieldName: string, + expectedValue: number +): void { + const field = findField(schema, definitionName, fieldName) + if (!field) { + throw new Error(`Missing field ${definitionName}.${fieldName}`) + } + if (field.value !== expectedValue) { + throw new Error( + `Expected ${definitionName}.${fieldName} to use field ${expectedValue}, got ${field.value}` + ) + } +} + +export function expectEnumValue( + schema: Schema, + enumName: string, + memberName: string, + expectedValue: number +): void { + const definition = findDefinition(schema, enumName) + if (!definition) { + throw new Error(`Missing enum ${enumName}`) + } + if (definition.kind !== 'ENUM') { + throw new Error(`${enumName} is a ${definition.kind}, not an enum`) + } + const field = definition.fields.find((candidate) => candidate.name === memberName) + if (!field) { + throw new Error(`Missing enum member ${enumName}.${memberName}`) + } + if (field.value !== expectedValue) { + throw new Error( + `Expected ${enumName}.${memberName} to use value ${expectedValue}, got ${field.value}` + ) + } +} + +export function validateSchema(schema: Schema): void { + for (const definition of schema.definitions) { + validateUniqueFieldNames(definition) + if (definition.kind === 'ENUM') validateUniqueEnumValues(definition) + } +} + +function validateUniqueFieldNames(definition: Definition): void { + const fieldsByName = new Set() + for (const field of definition.fields) { + if (fieldsByName.has(field.name)) { + error( + `The field ${quote(field.name)} is defined twice in ${quote(definition.name)}`, + field.line, + field.column + ) + } + fieldsByName.add(field.name) + } +} + +function validateUniqueEnumValues(definition: Definition): void { + const fieldsByValue = new Set() + for (const field of definition.fields) { + if (fieldsByValue.has(field.value)) { + error( + `The enum value ${field.value} is used twice in ${quote(definition.name)}`, + field.line, + field.column + ) + } + fieldsByValue.add(field.value) + } +} diff --git a/packages/kiwi/tests/fig-schema.test.ts b/packages/kiwi/tests/fig-schema.test.ts new file mode 100644 index 000000000..7d5c11d72 --- /dev/null +++ b/packages/kiwi/tests/fig-schema.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from 'bun:test' + +import { figmaSchema } from '../src/fig' +import { expectEnumValue, expectFieldNumber, validateSchema } from '../src/schema-runtime' + +describe('bundled Figma Kiwi schema', () => { + test('validates the static Figma schema', () => { + validateSchema(figmaSchema) + }) + + test('keeps high-value Figma schema guards stable', () => { + expect(figmaSchema.definitions).toHaveLength(605) + expect(figmaSchema.definitions.some((def) => def.name === 'NodeChange')).toBe(true) + expect( + figmaSchema.definitions.some((def) => def.name === 'InteractiveSlideElementChange') + ).toBe(true) + + expectFieldNumber(figmaSchema, 'Paint', 'colorVar', 21) + expectFieldNumber(figmaSchema, 'NodeChange', 'pageType', 397) + expectEnumValue(figmaSchema, 'VariableField', 'OVERRIDDEN_SYMBOL_ID', 37) + }) +}) diff --git a/packages/kiwi/tests/protocol.test.ts b/packages/kiwi/tests/protocol.test.ts new file mode 100644 index 000000000..454bbd63a --- /dev/null +++ b/packages/kiwi/tests/protocol.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, test } from 'bun:test' + +import { + buildMultiplayerUrl, + getKiwiMessageType, + hasFigWireHeader, + isKiwiMessage, + isZstdCompressed, + parseVarint, + skipFigWireHeader, + ZSTD_MAGIC +} from '../src/fig' + +describe('Figma Kiwi protocol helpers', () => { + test('detects Zstd magic bytes', () => { + expect(isZstdCompressed(ZSTD_MAGIC)).toBe(true) + expect(isZstdCompressed(new Uint8Array([1, 2, 3, 4]))).toBe(false) + }) + + test('reads Kiwi message type from minimal payloads', () => { + const payload = new Uint8Array([1, 4, 0]) + expect(isKiwiMessage(payload)).toBe(true) + expect(getKiwiMessageType(payload)).toBe(4) + expect(getKiwiMessageType(new Uint8Array([2, 4, 0]))).toBeNull() + }) + + test('parses representative varints', () => { + expect(parseVarint(new Uint8Array([0]), 0)).toEqual([0, 1]) + expect(parseVarint(new Uint8Array([127]), 0)).toEqual([127, 1]) + expect(parseVarint(new Uint8Array([128, 1]), 0)).toEqual([128, 2]) + }) + + test('handles fig-wire headers', () => { + const payload = new Uint8Array([ + ...new TextEncoder().encode('fig-wire'), + 1, + 0, + 0, + 0, + 40, + 181, + 47, + 253 + ]) + expect(hasFigWireHeader(payload)).toBe(true) + expect(skipFigWireHeader(payload)).toEqual(ZSTD_MAGIC) + }) + + test('builds multiplayer URLs', () => { + const url = new URL(buildMultiplayerUrl('file-key', 'tracking-id')) + expect(url.protocol).toBe('wss:') + expect(url.pathname).toBe('/api/multiplayer/file-key') + expect(url.searchParams.get('role')).toBe('editor') + expect(url.searchParams.get('tracking_session_id')).toBe('tracking-id') + }) +}) diff --git a/packages/kiwi/tests/schema-runtime.test.ts b/packages/kiwi/tests/schema-runtime.test.ts new file mode 100644 index 000000000..401a595f9 --- /dev/null +++ b/packages/kiwi/tests/schema-runtime.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, test } from 'bun:test' + +import { + compileSchema, + expectEnumValue, + expectFieldNumber, + parseSchema, + validateSchema +} from '../src/schema-runtime' + +const schemaText = ` +package Example; + +enum Kind { + CARD = 1; + BADGE = 2; +} + +message Item { + uint id = 1; + string name = 2; + Kind kind = 3; + string[] tags = 4; +} +` + +describe('Kiwi schema runtime', () => { + test('parses and validates inline schemas', () => { + const schema = parseSchema(schemaText) + validateSchema(schema) + + expectFieldNumber(schema, 'Item', 'name', 2) + expectFieldNumber(schema, 'Item', 'tags', 4) + expectEnumValue(schema, 'Kind', 'BADGE', 2) + }) + + test('compiles schemas and round-trips messages', () => { + const schema = parseSchema(schemaText) + interface ItemCodec { + encodeItem(value: unknown): Uint8Array + decodeItem(value: Uint8Array): unknown + } + + const codec = compileSchema(schema) as ItemCodec + + const encoded = codec.encodeItem({ id: 42, name: 'OpenPencil', kind: 'CARD', tags: ['kiwi'] }) + expect(encoded.length).toBeGreaterThan(0) + expect(codec.decodeItem(encoded)).toEqual({ + id: 42, + name: 'OpenPencil', + kind: 'CARD', + tags: ['kiwi'] + }) + }) +}) diff --git a/packages/kiwi/tsconfig.json b/packages/kiwi/tsconfig.json new file mode 100644 index 000000000..26a5306ac --- /dev/null +++ b/packages/kiwi/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": false, + "emitDeclarationOnly": true, + "outDir": "dist", + "rootDir": ".", + "types": ["bun"] + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "tests", "scripts"] +} diff --git a/packages/kiwi/tsconfig.test.json b/packages/kiwi/tsconfig.test.json new file mode 100644 index 000000000..b89f8132d --- /dev/null +++ b/packages/kiwi/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.ts"] +} diff --git a/packages/kiwi/tsdown.config.ts b/packages/kiwi/tsdown.config.ts new file mode 100644 index 000000000..e50420124 --- /dev/null +++ b/packages/kiwi/tsdown.config.ts @@ -0,0 +1,56 @@ +import { readFileSync } from 'node:fs' + +import { defineConfig } from 'tsdown' +import type { Plugin } from 'rolldown' + +function rawText(): Plugin { + return { + name: 'raw-text', + load(id) { + if (id.endsWith('?raw')) { + const path = id.slice(0, -'?raw'.length) + return `export default ${JSON.stringify(readFileSync(path, 'utf8'))}` + } + } + } +} + +export default defineConfig({ + entry: { + index: './src/index.ts', + 'schema-runtime': './src/schema-runtime/index.ts', + fig: './src/fig/index.ts' + }, + plugins: [rawText()], + platform: 'neutral', + format: ['esm'], + dts: false, + sourcemap: true, + hash: false, + clean: true, + outDir: './dist', + treeshake: { + moduleSideEffects: false + }, + deps: { + neverBundle: [/^node:/], + onlyBundle: false + }, + outputOptions: { + minifyInternalExports: false, + codeSplitting: { + groups: [ + { + test: /(? { + const cleanId = id.split('?')[0] + const parts = cleanId.split(/[\\/]/g) + const srcIndex = parts.lastIndexOf('src') + const file = srcIndex >= 0 ? parts.slice(srcIndex + 1).join('/') : parts.at(-1) ?? 'chunk' + return `chunks/${file.replace(/\.(ts|tsx)$/, '')}` + } + } + ] + } + } +})