@supernova-studio/client 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supernova-studio/client",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Supernova Data Models",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -25,8 +25,7 @@
25
25
  "scripts": {
26
26
  "build": "tsc",
27
27
  "typecheck": "tsc --noEmit",
28
- "test:unit": "vitest run",
29
- "test:unit:watch": "vitest watch"
28
+ "test:unit": "vitest run"
30
29
  },
31
30
  "author": "",
32
31
  "license": "ISC",
@@ -2,7 +2,6 @@ import * as Y from "yjs";
2
2
  import {
3
3
  PageBlockDefinition,
4
4
  PageBlockDefinitionProperty,
5
- PageBlockItemV2,
6
5
  PageBlockText,
7
6
  PageBlockTextSpan,
8
7
  PageBlockTextSpanAttribute,
@@ -11,12 +10,7 @@ import {
11
10
  PageBlockItemMultiRichTextPropertyValue,
12
11
  } from "@supernova-studio/model";
13
12
  import { PageBlockEditorModel } from "./model/block";
14
- import {
15
- ProsemirrorBlockItem,
16
- ProsemirrorBlockItemPropertyValue,
17
- ProsemirrorNode,
18
- ProsemirrorMark,
19
- } from "./prosemirror/types";
13
+ import { ProsemirrorNode, ProsemirrorMark } from "./prosemirror/types";
20
14
  import { BlockDefinitionUtils, BlockParsingUtils } from "./utils";
21
15
  import { DocumentationPageEditorModel } from "./model";
22
16
  import { prosemirrorJSONToYXmlFragment } from "y-prosemirror";
@@ -26,17 +20,20 @@ import { pmSchema } from "./prosemirror";
26
20
  // Types
27
21
  //
28
22
 
29
- type Input = {
23
+ type BaseInput = {
30
24
  block: PageBlockEditorModel;
31
25
  definition: PageBlockDefinition;
26
+ };
27
+
28
+ type InputWithProperty = BaseInput & {
32
29
  property: PageBlockDefinitionProperty;
33
30
  };
34
31
 
35
- type RichTextInputWithValue = Input & {
32
+ type RichTextInputWithValue = InputWithProperty & {
36
33
  richTextPropertyValue: PageBlockItemRichTextPropertyValue;
37
34
  };
38
35
 
39
- type MultiRichTextInputWithValue = Input & {
36
+ type MultiRichTextInputWithValue = InputWithProperty & {
40
37
  multiRichTextPropertyValue: PageBlockItemMultiRichTextPropertyValue;
41
38
  };
42
39
 
@@ -79,6 +76,7 @@ export function blockToProsemirrorNode(
79
76
  block: PageBlockEditorModel,
80
77
  definition: PageBlockDefinition
81
78
  ): ProsemirrorNode | null {
79
+ // Single rich text
82
80
  const richTextProperty = BlockDefinitionUtils.firstRichTextProperty(definition);
83
81
  if (richTextProperty) {
84
82
  return serializeAsRichTextBlock({
@@ -88,6 +86,7 @@ export function blockToProsemirrorNode(
88
86
  });
89
87
  }
90
88
 
89
+ // Multi rich text
91
90
  const multiRichTextProperty = BlockDefinitionUtils.firstMultiRichTextProperty(definition);
92
91
  if (multiRichTextProperty) {
93
92
  return serializeAsMultiRichTextBlock({
@@ -97,6 +96,28 @@ export function blockToProsemirrorNode(
97
96
  });
98
97
  }
99
98
 
99
+ // Embed
100
+ const embedProperty = BlockDefinitionUtils.firstEmbedProperty(definition);
101
+ const embedType = serializeEmbedType(block.data.packageId);
102
+ if (embedProperty && embedType) {
103
+ return serializeAsEmbed(
104
+ {
105
+ block: block,
106
+ definition: definition,
107
+ property: embedProperty,
108
+ },
109
+ embedType
110
+ );
111
+ }
112
+
113
+ // Divider
114
+ if (block.data.packageId === "io.supernova.block.divider") {
115
+ return serializeAsDivider({
116
+ block: block,
117
+ definition: definition,
118
+ });
119
+ }
120
+
100
121
  return serializeAsCustomBlock(block, definition);
101
122
  }
102
123
 
@@ -104,7 +125,7 @@ export function blockToProsemirrorNode(
104
125
  // Single rich text blocks
105
126
  //
106
127
 
107
- export function serializeAsRichTextBlock(input: Input): ProsemirrorNode {
128
+ function serializeAsRichTextBlock(input: InputWithProperty): ProsemirrorNode {
108
129
  const { block, definition, property: richTextProperty } = input;
109
130
 
110
131
  const blockItem = BlockParsingUtils.singleBlockItem(block);
@@ -176,7 +197,7 @@ function serializeAsCallout(input: RichTextInputWithValue): ProsemirrorNode {
176
197
  // Multi rich text blocks
177
198
  //
178
199
 
179
- export function serializeAsMultiRichTextBlock(input: Input): ProsemirrorNode {
200
+ function serializeAsMultiRichTextBlock(input: InputWithProperty): ProsemirrorNode {
180
201
  const { block, definition, property: richTextProperty } = input;
181
202
 
182
203
  const blockItem = BlockParsingUtils.singleBlockItem(block);
@@ -241,11 +262,57 @@ function serializeAsListItem(text: PageBlockText): ProsemirrorNode {
241
262
  };
242
263
  }
243
264
 
265
+ //
266
+ // Embeds
267
+ //
268
+
269
+ function serializeAsEmbed(input: InputWithProperty, embedType: string): ProsemirrorNode {
270
+ const { block, definition, property: embedProperty } = input;
271
+
272
+ const blockItem = BlockParsingUtils.singleBlockItem(block);
273
+ const embedValue = BlockParsingUtils.embedPropertyValue(blockItem, embedProperty.id);
274
+
275
+ return {
276
+ type: "embed",
277
+ attrs: {
278
+ ...serializeBlockNodeAttributes(input),
279
+ type: embedType,
280
+ url: embedValue.value,
281
+ height: embedValue.height,
282
+ caption: embedValue.caption,
283
+ },
284
+ };
285
+ }
286
+
287
+ function serializeEmbedType(blockDefinitionId: string): string | undefined {
288
+ switch (blockDefinitionId) {
289
+ case "io.supernova.block.embed":
290
+ return "generic";
291
+ case "io.supernova.block.embed-youtube":
292
+ return "youtube";
293
+ case "io.supernova.block.embed-figma":
294
+ return "figma";
295
+ }
296
+
297
+ return undefined;
298
+ }
299
+
300
+ //
301
+ // Divider
302
+ //
303
+
304
+ function serializeAsDivider(input: BaseInput): ProsemirrorNode {
305
+ return {
306
+ type: "horizontalrule",
307
+ attrs: serializeBlockNodeAttributes(input),
308
+ };
309
+ }
310
+
244
311
  //
245
312
  // Attributes
246
313
  //
247
314
 
248
- function serializeBlockNodeAttributes(input: Input) {
315
+ function serializeBlockNodeAttributes(input: BaseInput) {
249
316
  const { block } = input;
250
317
 
251
318
  return {
@@ -351,118 +418,25 @@ function serializeTextSpanAttribute(spanAttribute: PageBlockTextSpanAttribute):
351
418
  }
352
419
 
353
420
  export function serializeAsCustomBlock(block: PageBlockEditorModel, definition: PageBlockDefinition): ProsemirrorNode {
354
- const items = block.data.items.map(i => serializeItem(i, block, definition));
421
+ const items = block.data.items.map(i => {
422
+ return {
423
+ id: i.id,
424
+ props: i.props,
425
+ linksTo: i.linksTo,
426
+ };
427
+ });
355
428
 
356
429
  return {
357
430
  type: "blockNode",
358
431
  attrs: {
359
432
  id: block.id,
360
433
  definitionId: block.data.packageId,
361
- variantId: "default",
362
- columns: 1,
434
+ variantId: block.data.variantId,
363
435
  items: JSON.stringify(items),
364
-
365
- // TODO Docs: variant, columns
366
436
  },
367
437
  };
368
438
  }
369
439
 
370
- function serializeItem(
371
- item: PageBlockItemV2,
372
- block: PageBlockEditorModel,
373
- definition: PageBlockDefinition
374
- ): ProsemirrorBlockItem {
375
- const result: ProsemirrorBlockItem = {
376
- properties: [],
377
- };
378
-
379
- for (const property of definition.item.properties) {
380
- const serializedValue = serializePropertyValue(item, property);
381
- serializedValue && result.properties.push(serializedValue);
382
- }
383
-
384
- return result;
385
- }
386
-
387
- function serializePropertyValue(
388
- item: PageBlockItemV2,
389
- property: PageBlockDefinitionProperty
390
- ): ProsemirrorBlockItemPropertyValue | undefined {
391
- const value = item.props[property.id] ?? undefined;
392
- if (!value) {
393
- return undefined;
394
- }
395
-
396
- // TODO Docs: actual property serialization
397
-
398
- const result: ProsemirrorBlockItemPropertyValue = {
399
- id: property.id,
400
- data: {},
401
- };
402
-
403
- if (typeof value === "string") {
404
- result.data.value = value;
405
- }
406
-
407
- return result;
408
- }
409
-
410
440
  function nonNullFilter<T>(item: T | null): item is T {
411
441
  return !!item;
412
442
  }
413
-
414
- // function serializePropertyValue(item: SDKBlockItem, property: SDKBlockDefinitionProperty) {
415
- // switch (property.type) {
416
- // case SDK.DocsBlockItemPropertyType.text: return value as string;
417
-
418
- // case SDK.DocsBlockItemPropertyType.richText: return {}
419
-
420
- // case SDK.DocsBlockItemPropertyType.boolean: return {}
421
- // case SDK.DocsBlockItemPropertyType.number: return {}
422
- // case SDK.DocsBlockItemPropertyType.singleSelect: return {}
423
- // case SDK.DocsBlockItemPropertyType.multiSelect: return {}
424
- // case SDK.DocsBlockItemPropertyType.image: return {}
425
- // case SDK.DocsBlockItemPropertyType.token: return {}
426
- // case SDK.DocsBlockItemPropertyType.tokenType: return {}
427
- // case SDK.DocsBlockItemPropertyType.tokenProperty: return {}
428
- // case SDK.DocsBlockItemPropertyType.component: return {}
429
- // case SDK.DocsBlockItemPropertyType.componentProperty: return {}
430
- // case SDK.DocsBlockItemPropertyType.asset: return {}
431
- // case SDK.DocsBlockItemPropertyType.assetProperty: return {}
432
- // case SDK.DocsBlockItemPropertyType.page: return {}
433
- // case SDK.DocsBlockItemPropertyType.pageProperty: return {}
434
- // case SDK.DocsBlockItemPropertyType.embedURL: return {}
435
- // case SDK.DocsBlockItemPropertyType.embedFrame: return {}
436
- // case SDK.DocsBlockItemPropertyType.markdown: return {}
437
- // case SDK.DocsBlockItemPropertyType.code: return {}
438
- // case SDK.DocsBlockItemPropertyType.codeSandbox: return {}
439
- // case SDK.DocsBlockItemPropertyType.table: return {}
440
- // case SDK.DocsBlockItemPropertyType.divider: return {}
441
- // case SDK.DocsBlockItemPropertyType.storybook: return {}
442
- // }
443
- // }
444
-
445
- // richText = "RichText",
446
- // text = "Text",
447
- // boolean = "Boolean",
448
- // number = "Number",
449
- // singleSelect = "SingleSelect",
450
- // multiSelect = "MultiSelect",
451
- // image = "Image",
452
- // token = "Token",
453
- // tokenType = "TokenType",
454
- // tokenProperty = "TokenProperty",
455
- // component = "Component",
456
- // componentProperty = "ComponentProperty",
457
- // asset = "Asset",
458
- // assetProperty = "AssetProperty",
459
- // page = "Page",
460
- // pageProperty = "PageProperty",
461
- // embedURL = "EmbedURL",
462
- // embedFrame = "EmbedFrame",
463
- // markdown = "Markdown",
464
- // code = "Code",
465
- // codeSandbox = "CodeSandbox",
466
- // table = "Table",
467
- // divider = "Divider",
468
- // storybook = "Storybook"
@@ -591,7 +591,7 @@ const blocks: PageBlockDefinition[] = [
591
591
  {
592
592
  id: "block.links.property.title",
593
593
  name: "Title",
594
- type: "RichText",
594
+ type: "Text",
595
595
  description: undefined,
596
596
  options: { textStyle: "Title5" },
597
597
  variantOptions: undefined,
@@ -599,7 +599,7 @@ const blocks: PageBlockDefinition[] = [
599
599
  {
600
600
  id: "block.links.property.description",
601
601
  name: "Short description",
602
- type: "RichText",
602
+ type: "Text",
603
603
  description: undefined,
604
604
  options: { textStyle: "Default", color: "NeutralFaded" },
605
605
  variantOptions: undefined,
@@ -1,3 +1,5 @@
1
+ import { PageBlockItemUntypedPropertyValue } from "@supernova-studio/model";
2
+
1
3
  export type ProsemirrorNode = {
2
4
  type: string;
3
5
  text?: string;
@@ -12,12 +14,6 @@ export type ProsemirrorMark = {
12
14
  };
13
15
 
14
16
  export type ProsemirrorBlockItem = {
15
- properties: ProsemirrorBlockItemPropertyValue[];
16
- };
17
-
18
- export type ProsemirrorBlockItemPropertyValue = {
19
17
  id: string;
20
- data: {
21
- value?: string;
22
- };
18
+ props: Record<string, PageBlockItemUntypedPropertyValue>;
23
19
  };
@@ -6,6 +6,14 @@ import {
6
6
  PageBlockTextSpan,
7
7
  PageBlockTextSpanAttribute,
8
8
  PageBlockDefinitionProperty,
9
+ PageBlockCalloutType,
10
+ PageBlockItemRichTextPropertyValue,
11
+ PageBlockItemMultiRichTextPropertyValue,
12
+ PageBlockItemEmbedPropertyValue,
13
+ PageBlockItemV2,
14
+ PageBlockItemTextPropertyValue,
15
+ PageBlockItemUntypedPropertyValue,
16
+ PageBlockItemImageValue,
9
17
  } from "@supernova-studio/model";
10
18
  import { PageBlockEditorModel } from "./model/block";
11
19
  import { DocumentationPageEditorModel } from "./model/page";
@@ -52,9 +60,27 @@ export function prosemirrorNodeToBlock(
52
60
  return parseAsRichText(prosemirrorNode, definition, richTextProperty);
53
61
  }
54
62
 
63
+ const multiRichTextProperty = BlockDefinitionUtils.firstMultiRichTextProperty(definition);
64
+ if (multiRichTextProperty) {
65
+ return parseAsMultiRichText(prosemirrorNode, definition, multiRichTextProperty);
66
+ }
67
+
68
+ const embedProperty = BlockDefinitionUtils.firstEmbedProperty(definition);
69
+ if (prosemirrorNode.type === "embed" && embedProperty) {
70
+ return parseAsEmbed(prosemirrorNode, definition, embedProperty);
71
+ }
72
+
73
+ if (definition.id === "io.supernova.block.divider") {
74
+ return parseAsDivider(prosemirrorNode, definition);
75
+ }
76
+
55
77
  return parseAsCustomBlock(prosemirrorNode, definition);
56
78
  }
57
79
 
80
+ //
81
+ // Single rich text
82
+ //
83
+
58
84
  function parseAsRichText(
59
85
  prosemirrorNode: ProsemirrorNode,
60
86
  definition: PageBlockDefinition,
@@ -62,21 +88,89 @@ function parseAsRichText(
62
88
  ): PageBlockEditorModel {
63
89
  const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
64
90
  const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
91
+ const calloutType = parseCalloutType(parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "type"));
65
92
 
66
93
  return {
67
94
  // TODO Artem: indent
68
95
  id: id,
96
+
97
+ ...(variantId && { variantId: variantId }),
98
+
69
99
  data: {
70
100
  packageId: definition.id,
71
101
  indentLevel: 0,
72
- variantId: variantId,
73
102
  items: [
74
103
  {
75
104
  id: id,
76
105
  props: {
77
106
  [property.id]: {
107
+ // Required
78
108
  value: parseRichText(prosemirrorNode.content ?? []),
79
- },
109
+
110
+ // Optional
111
+ ...(calloutType && { calloutType: calloutType }),
112
+ } satisfies PageBlockItemRichTextPropertyValue,
113
+ },
114
+ },
115
+ ],
116
+ },
117
+ };
118
+ }
119
+
120
+ function parseCalloutType(prosemirrorCalloutType: string | undefined): PageBlockCalloutType | undefined {
121
+ if (!prosemirrorCalloutType) return undefined;
122
+
123
+ switch (prosemirrorCalloutType) {
124
+ case "error":
125
+ return "Error";
126
+ case "neutral":
127
+ return "Info";
128
+ case "success":
129
+ return "Success";
130
+ case "warning":
131
+ return "Warning";
132
+ }
133
+ }
134
+
135
+ //
136
+ // Multi rich text
137
+ //
138
+
139
+ function parseAsMultiRichText(
140
+ prosemirrorNode: ProsemirrorNode,
141
+ definition: PageBlockDefinition,
142
+ property: PageBlockDefinitionProperty
143
+ ) {
144
+ const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
145
+ const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
146
+
147
+ return {
148
+ // TODO Artem: indent
149
+ id: id,
150
+ data: {
151
+ packageId: definition.id,
152
+ indentLevel: 0,
153
+
154
+ ...(variantId && { variantId: variantId }),
155
+
156
+ items: [
157
+ {
158
+ id: id,
159
+ props: {
160
+ [property.id]: {
161
+ // Required
162
+ value: (prosemirrorNode.content ?? [])
163
+ .map(listItem => {
164
+ if (listItem.type !== "listitem") return null;
165
+ if (!listItem.content?.length) return parseRichText([]);
166
+
167
+ const paragraph = listItem.content[0];
168
+ if (paragraph.type !== "paragraph") return parseRichText([]);
169
+
170
+ return parseRichText(paragraph.content ?? []);
171
+ })
172
+ .filter(nonNullFilter),
173
+ } satisfies PageBlockItemMultiRichTextPropertyValue,
80
174
  },
81
175
  },
82
176
  ],
@@ -84,6 +178,10 @@ function parseAsRichText(
84
178
  };
85
179
  }
86
180
 
181
+ //
182
+ // Rich text
183
+ //
184
+
87
185
  function parseRichText(spans: ProsemirrorNode[]): PageBlockText {
88
186
  return {
89
187
  spans: spans.map(parseRichTextSpan).filter(nonNullFilter),
@@ -126,6 +224,67 @@ function parseRichTextAttribute(mark: ProsemirrorMark): PageBlockTextSpanAttribu
126
224
  return null;
127
225
  }
128
226
 
227
+ //
228
+ // Embed
229
+ //
230
+
231
+ function parseAsEmbed(
232
+ prosemirrorNode: ProsemirrorNode,
233
+ definition: PageBlockDefinition,
234
+ property: PageBlockDefinitionProperty
235
+ ): PageBlockEditorModel {
236
+ const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
237
+ const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
238
+
239
+ const url = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "url");
240
+ const caption = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "caption");
241
+ const height = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "height");
242
+
243
+ return {
244
+ id: id,
245
+ data: {
246
+ packageId: definition.id,
247
+ indentLevel: 0,
248
+
249
+ ...(variantId && { variantId: variantId }),
250
+
251
+ items: [
252
+ {
253
+ id: id,
254
+ props: {
255
+ [property.id]: {
256
+ value: url,
257
+ caption: caption,
258
+ height: height,
259
+ } as PageBlockItemEmbedPropertyValue,
260
+ },
261
+ },
262
+ ],
263
+ },
264
+ };
265
+ }
266
+
267
+ //
268
+ // Divider
269
+ //
270
+
271
+ function parseAsDivider(prosemirrorNode: ProsemirrorNode, definition: PageBlockDefinition): PageBlockEditorModel {
272
+ const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
273
+ const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
274
+
275
+ return {
276
+ id: id,
277
+ data: {
278
+ packageId: definition.id,
279
+ indentLevel: 0,
280
+
281
+ ...(variantId && { variantId: variantId }),
282
+
283
+ items: [{ id: id, props: {} }],
284
+ },
285
+ };
286
+ }
287
+
129
288
  //
130
289
  // Custom block
131
290
  //
@@ -134,7 +293,71 @@ function parseAsCustomBlock(
134
293
  prosemirrorNode: ProsemirrorNode,
135
294
  definition: PageBlockDefinition
136
295
  ): PageBlockEditorModel | null {
137
- return null;
296
+ const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
297
+ const variantId = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
298
+
299
+ const itemsString = parseProsemirrorBlockAttribute(prosemirrorNode, "items");
300
+ const itemsJson = JSON.parse(itemsString);
301
+ if (!Array.isArray(itemsJson)) {
302
+ console.error("Block `items` property must be a json array");
303
+ return null;
304
+ }
305
+
306
+ const parsedItems = itemsJson.map(i => parseItem(i, definition));
307
+
308
+ return {
309
+ id: id,
310
+ data: {
311
+ packageId: definition.id,
312
+ indentLevel: 0,
313
+
314
+ ...(variantId && { variantId: variantId }),
315
+
316
+ items: parsedItems,
317
+ },
318
+ };
319
+ }
320
+
321
+ function parseItem(rawItem: unknown, definition: PageBlockDefinition): PageBlockItemV2 | null {
322
+ const parsedItem = PageBlockItemV2.safeParse(rawItem);
323
+ if (!parsedItem.success) {
324
+ return null;
325
+ }
326
+
327
+ const sanitizedProps: Record<string, PageBlockItemUntypedPropertyValue> = {};
328
+
329
+ for (const property of definition.item.properties) {
330
+ const value = parsedItem.data.props[property.id];
331
+ if (!value) {
332
+ return null;
333
+ }
334
+
335
+ // Validate
336
+ switch (property.type) {
337
+ case "Text":
338
+ PageBlockItemTextPropertyValue.parse(value);
339
+ break;
340
+
341
+ case "EmbedURL":
342
+ PageBlockItemEmbedPropertyValue.parse(value);
343
+ break;
344
+
345
+ case "Image":
346
+ PageBlockItemImageValue.parse(value);
347
+ break;
348
+
349
+ default:
350
+ return null;
351
+ }
352
+
353
+ sanitizedProps[property.id] = value;
354
+ }
355
+
356
+ return {
357
+ id: parsedItem.data.id,
358
+ linksTo: parsedItem.data.linksTo,
359
+ props: sanitizedProps,
360
+ };
138
361
  }
139
362
 
140
363
  //
@@ -2,6 +2,7 @@ import { PageBlockEditorModel } from "./model/block";
2
2
  import {
3
3
  PageBlockDefinition,
4
4
  PageBlockDefinitionPropertyType,
5
+ PageBlockItemEmbedPropertyValue,
5
6
  PageBlockItemMultiRichTextPropertyValue,
6
7
  PageBlockItemRichTextPropertyValue,
7
8
  PageBlockItemUntypedPropertyValue,
@@ -31,6 +32,13 @@ export const BlockParsingUtils = {
31
32
  return richText;
32
33
  },
33
34
 
35
+ embedPropertyValue(item: PageBlockItemV2, propertyKey: string): PageBlockItemEmbedPropertyValue {
36
+ const objectValue = this.objectPropertyValue(item, propertyKey);
37
+ const embed = PageBlockItemEmbedPropertyValue.parse(objectValue);
38
+
39
+ return embed;
40
+ },
41
+
34
42
  objectPropertyValue(item: PageBlockItemV2, propertyKey: string) {
35
43
  const value = this.propertyValueOrThrow(item, propertyKey);
36
44
 
@@ -68,6 +76,12 @@ export const BlockDefinitionUtils = {
68
76
  return undefined;
69
77
  },
70
78
 
79
+ firstEmbedProperty(definition: PageBlockDefinition) {
80
+ const property = definition.item.properties.find(p => p.type === "EmbedURL");
81
+ if (property) return property;
82
+ return undefined;
83
+ },
84
+
71
85
  richTextProperty(definition: PageBlockDefinition, propertyKey: string) {
72
86
  return this.property(definition, propertyKey, "RichText");
73
87
  },