@supernova-studio/client 0.32.0 → 0.34.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.
@@ -73,6 +73,12 @@ export function prosemirrorDocToPage(
73
73
  };
74
74
  }
75
75
 
76
+ export function prosemirrorNodeToBlock(prosemirrorNode: ProsemirrorNode, definitions: PageBlockDefinition[]) {
77
+ const definitionsById = mapByUnique(definitions, d => d.id);
78
+ const block = internalProsemirrorNodeToBlock(prosemirrorNode, definitionsById, 0);
79
+ return block[0] ?? null;
80
+ }
81
+
76
82
  //
77
83
  // Sections
78
84
  //
@@ -134,7 +140,7 @@ function prosemirrorNodeToSectionColumns(
134
140
 
135
141
  return {
136
142
  id: id,
137
- blocks: internalProsemirrorNodesToBlocks(prosemirrorNode.content ?? [], definitionsMap),
143
+ blocks: internalProsemirrorNodesToBlocks(prosemirrorNode.content ?? [], definitionsMap, 0),
138
144
  };
139
145
  }
140
146
 
@@ -144,49 +150,49 @@ function prosemirrorNodeToSectionColumns(
144
150
 
145
151
  export function prosemirrorNodesToBlocks(prosemirrorNodes: ProsemirrorNode[], definitions: PageBlockDefinition[]) {
146
152
  const definitionsById = mapByUnique(definitions, d => d.id);
147
- return internalProsemirrorNodesToBlocks(prosemirrorNodes, definitionsById);
153
+ return internalProsemirrorNodesToBlocks(prosemirrorNodes, definitionsById, 0);
148
154
  }
149
155
 
150
156
  function internalProsemirrorNodesToPageItems(
151
157
  prosemirrorNodes: ProsemirrorNode[],
152
158
  definitionsMap: Map<string, PageBlockDefinition>
153
159
  ): (PageBlockEditorModel | PageSectionEditorModel)[] {
154
- return prosemirrorNodes
155
- .map(prosemirrorNode => {
156
- if (prosemirrorNode.type === "tabsSection") {
157
- return prosemirrorNodeToSection(prosemirrorNode, Array.from(definitionsMap.values()));
158
- } else {
159
- return internalProsemirrorNodeToBlock(prosemirrorNode, definitionsMap);
160
- }
161
- })
162
- .filter(nonNullFilter);
160
+ const result: (PageBlockEditorModel | PageSectionEditorModel)[] = [];
161
+
162
+ for (const prosemirrorNode of prosemirrorNodes) {
163
+ if (prosemirrorNode.type === "tabsSection") {
164
+ // Section
165
+ const section = prosemirrorNodeToSection(prosemirrorNode, Array.from(definitionsMap.values()));
166
+ section && result.push(section);
167
+ } else {
168
+ // Block
169
+ result.push(...internalProsemirrorNodeToBlock(prosemirrorNode, definitionsMap, 0));
170
+ }
171
+ }
172
+
173
+ return result;
163
174
  }
164
175
 
165
176
  function internalProsemirrorNodesToBlocks(
166
177
  prosemirrorNodes: ProsemirrorNode[],
167
- definitionsMap: Map<string, PageBlockDefinition>
178
+ definitionsMap: Map<string, PageBlockDefinition>,
179
+ depth: number
168
180
  ): PageBlockEditorModel[] {
169
- return prosemirrorNodes
170
- .map(prosemirrorNode => {
171
- return internalProsemirrorNodeToBlock(prosemirrorNode, definitionsMap);
172
- })
173
- .filter(nonNullFilter);
181
+ return prosemirrorNodes.flatMap(prosemirrorNode => {
182
+ return internalProsemirrorNodeToBlock(prosemirrorNode, definitionsMap, depth);
183
+ });
174
184
  }
175
185
 
176
186
  function internalProsemirrorNodeToBlock(
177
187
  prosemirrorNode: ProsemirrorNode,
178
- definitionsMap: Map<string, PageBlockDefinition>
179
- ) {
180
- let definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
188
+ definitionsMap: Map<string, PageBlockDefinition>,
189
+ depth: number
190
+ ): PageBlockEditorModel[] {
191
+ const definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
181
192
  if (!definitionId) {
182
193
  console.warn(`definitionId on ${prosemirrorNode.type} is required to be interpreted as a block, skipping node`);
183
194
 
184
- return null;
185
- }
186
-
187
- // TODO: remove
188
- if (definitionId === "io.supernova.block.token-detail") {
189
- definitionId = "io.supernova.block.token-list";
195
+ return [];
190
196
  }
191
197
 
192
198
  const definition = definitionsMap.get(definitionId);
@@ -195,36 +201,42 @@ function internalProsemirrorNodeToBlock(
195
201
  `Block definitionId "${definitionId}" (prosemirror node ${prosemirrorNode.type}) is not among available definitions`
196
202
  );
197
203
  console.warn(prosemirrorNode);
198
- return null;
204
+ return [];
199
205
  }
200
206
 
201
- return prosemirrorNodeToBlock(prosemirrorNode, definition);
207
+ return prosemirrorNodeAndDefinitionToBlock(prosemirrorNode, definition, definitionsMap, depth);
202
208
  }
203
209
 
204
- export function prosemirrorNodeToBlock(
210
+ function prosemirrorNodeAndDefinitionToBlock(
205
211
  prosemirrorNode: ProsemirrorNode,
206
- definition: PageBlockDefinition
207
- ): PageBlockEditorModel | null {
212
+ definition: PageBlockDefinition,
213
+ definitionsMap: Map<string, PageBlockDefinition>,
214
+ depth: number
215
+ ): PageBlockEditorModel[] {
208
216
  const richTextProperty = BlockDefinitionUtils.firstRichTextProperty(definition);
209
217
  if (richTextProperty) {
210
- return parseAsRichText(prosemirrorNode, definition, richTextProperty);
218
+ const block = parseAsRichText(prosemirrorNode, definition, richTextProperty);
219
+ return block ? [block] : [];
211
220
  }
212
221
 
213
222
  const multiRichTextProperty = BlockDefinitionUtils.firstMultiRichTextProperty(definition);
214
223
  if (multiRichTextProperty) {
215
- return parseAsMultiRichText(prosemirrorNode, definition, multiRichTextProperty);
224
+ return parseAsMultiRichText(prosemirrorNode, definition, multiRichTextProperty, definitionsMap, depth);
216
225
  }
217
226
 
218
227
  const tableProperty = BlockDefinitionUtils.firstTableProperty(definition);
219
228
  if (tableProperty) {
220
- return parseAsTable(prosemirrorNode, definition, tableProperty);
229
+ const block = parseAsTable(prosemirrorNode, definition, tableProperty);
230
+ return block ? [block] : [];
221
231
  }
222
232
 
223
233
  if (definition.id === "io.supernova.block.divider") {
224
- return parseAsDivider(prosemirrorNode, definition);
234
+ const block = parseAsDivider(prosemirrorNode, definition);
235
+ return block ? [block] : [];
225
236
  }
226
237
 
227
- return parseAsCustomBlock(prosemirrorNode, definition);
238
+ const block = parseAsCustomBlock(prosemirrorNode, definition);
239
+ return block ? [block] : [];
228
240
  }
229
241
 
230
242
  //
@@ -294,46 +306,75 @@ function parseCalloutType(prosemirrorCalloutType: unknown): PageBlockCalloutType
294
306
  function parseAsMultiRichText(
295
307
  prosemirrorNode: ProsemirrorNode,
296
308
  definition: PageBlockDefinition,
297
- property: PageBlockDefinitionProperty
298
- ): PageBlockEditorModel | null {
309
+ property: PageBlockDefinitionProperty,
310
+ definitionsMap: Map<string, PageBlockDefinition>,
311
+ depth: number
312
+ ): PageBlockEditorModel[] {
299
313
  const id = getProsemirrorBlockId(prosemirrorNode);
300
- if (!id) return null;
314
+ if (!id) return [];
301
315
 
302
316
  const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
317
+ const result: PageBlockEditorModel[] = [];
303
318
 
304
- return {
305
- // TODO Artem: indent
306
- id: id,
307
- type: "Block",
308
- data: {
309
- packageId: definition.id,
310
- indentLevel: 0,
311
-
312
- ...(variantId && { variantId: variantId }),
319
+ // Flatten items
320
+ const listItems: ProsemirrorNode[] = [];
321
+ prosemirrorNode.content?.forEach(c => {
322
+ // Illegal child of a list
323
+ if (c.type !== "listItem") return;
313
324
 
314
- items: [
315
- {
316
- id: id,
317
- props: {
318
- [property.id]: {
319
- // Required
320
- value: (prosemirrorNode.content ?? [])
321
- .map(listItem => {
322
- if (listItem.type !== "listItem") return null;
323
- if (!listItem.content?.length) return parseRichText([]);
324
-
325
- const paragraph = listItem.content[0];
326
- if (paragraph.type !== "paragraph") return parseRichText([]);
325
+ c.content?.forEach(cc => {
326
+ listItems.push(cc);
327
+ });
328
+ });
327
329
 
330
+ // Text list items buffering
331
+ let bufferedTextItems: ProsemirrorNode[] = [];
332
+ function flushBufferedTextItems() {
333
+ if (!bufferedTextItems.length) return;
334
+
335
+ const idSuffix = result.length ? `-${result.length}` : "";
336
+
337
+ result.push({
338
+ id: id + idSuffix,
339
+ type: "Block",
340
+ data: {
341
+ packageId: definition.id,
342
+ indentLevel: depth,
343
+ ...(variantId && { variantId: variantId }),
344
+ items: [
345
+ {
346
+ id: id + idSuffix,
347
+ props: {
348
+ [property.id]: {
349
+ value: bufferedTextItems.map(paragraph => {
328
350
  return parseRichText(paragraph.content ?? []);
329
- })
330
- .filter(nonNullFilter),
331
- } satisfies PageBlockItemMultiRichTextValue,
351
+ }),
352
+ } satisfies PageBlockItemMultiRichTextValue,
353
+ },
332
354
  },
333
- },
334
- ],
335
- },
336
- };
355
+ ],
356
+ },
357
+ });
358
+
359
+ bufferedTextItems = [];
360
+ }
361
+
362
+ listItems.forEach(item => {
363
+ if (item.type === "paragraph") {
364
+ bufferedTextItems.push(item);
365
+ return;
366
+ }
367
+
368
+ // This is not a paragraph, flush text items first
369
+ flushBufferedTextItems();
370
+
371
+ result.push(...internalProsemirrorNodeToBlock(item, definitionsMap, depth + 1));
372
+ });
373
+
374
+ // Flush text items at the end
375
+ flushBufferedTextItems();
376
+
377
+ return result;
337
378
  }
338
379
 
339
380
  //