@supernova-studio/client 0.48.6 → 1.0.1

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.
Files changed (121) hide show
  1. package/dist/index.d.mts +1166 -44199
  2. package/dist/index.mjs +4583 -9902
  3. package/dist/index.mjs.map +1 -1
  4. package/package.json +11 -30
  5. package/dist/index.d.ts +0 -44442
  6. package/dist/index.js +0 -11007
  7. package/dist/index.js.map +0 -1
  8. package/src/api/conversion/documentation/documentation-group-v1-to-dto.ts +0 -114
  9. package/src/api/conversion/documentation/documentation-group-v2-to-dto.ts +0 -92
  10. package/src/api/conversion/documentation/documentation-item-configuration-v1-to-dto.ts +0 -52
  11. package/src/api/conversion/documentation/documentation-item-configuration-v2-to-dto.ts +0 -34
  12. package/src/api/conversion/documentation/documentation-page-to-dto-utils.ts +0 -132
  13. package/src/api/conversion/documentation/documentation-page-v1-to-dto.ts +0 -69
  14. package/src/api/conversion/documentation/documentation-page-v2-to-dto.ts +0 -72
  15. package/src/api/conversion/documentation/index.ts +0 -7
  16. package/src/api/conversion/export/index.ts +0 -1
  17. package/src/api/conversion/export/pipeline.ts +0 -24
  18. package/src/api/conversion/index.ts +0 -3
  19. package/src/api/conversion/integrations/git.ts +0 -37
  20. package/src/api/conversion/integrations/index.ts +0 -2
  21. package/src/api/conversion/integrations/integration.ts +0 -37
  22. package/src/api/dto/design-systems/brand.ts +0 -22
  23. package/src/api/dto/design-systems/data-source.ts +0 -122
  24. package/src/api/dto/design-systems/design-system.ts +0 -18
  25. package/src/api/dto/design-systems/elements-diff.ts +0 -14
  26. package/src/api/dto/design-systems/exporter-property.ts +0 -8
  27. package/src/api/dto/design-systems/index.ts +0 -7
  28. package/src/api/dto/design-systems/version.ts +0 -57
  29. package/src/api/dto/design-systems/view.ts +0 -45
  30. package/src/api/dto/documentation/anchor.ts +0 -15
  31. package/src/api/dto/documentation/block-definition.ts +0 -28
  32. package/src/api/dto/documentation/block.ts +0 -6
  33. package/src/api/dto/documentation/documentation-page-snapshot.ts +0 -18
  34. package/src/api/dto/documentation/index.ts +0 -6
  35. package/src/api/dto/documentation/link-preview.ts +0 -23
  36. package/src/api/dto/documentation/publish.ts +0 -25
  37. package/src/api/dto/elements/documentation/draft-state.ts +0 -35
  38. package/src/api/dto/elements/documentation/group-action.ts +0 -108
  39. package/src/api/dto/elements/documentation/group-v1.ts +0 -35
  40. package/src/api/dto/elements/documentation/group-v2.ts +0 -109
  41. package/src/api/dto/elements/documentation/hierarchy.ts +0 -36
  42. package/src/api/dto/elements/documentation/index.ts +0 -11
  43. package/src/api/dto/elements/documentation/item-configuration-v1.ts +0 -24
  44. package/src/api/dto/elements/documentation/item-configuration-v2.ts +0 -14
  45. package/src/api/dto/elements/documentation/page-actions-v2.ts +0 -108
  46. package/src/api/dto/elements/documentation/page-content.ts +0 -15
  47. package/src/api/dto/elements/documentation/page-v1.ts +0 -21
  48. package/src/api/dto/elements/documentation/page-v2.ts +0 -105
  49. package/src/api/dto/elements/elements-action-v2.ts +0 -114
  50. package/src/api/dto/elements/figma-nodes/figma-node.ts +0 -55
  51. package/src/api/dto/elements/figma-nodes/index.ts +0 -2
  52. package/src/api/dto/elements/figma-nodes/node-actions-v2.ts +0 -24
  53. package/src/api/dto/elements/get-elements-v2.ts +0 -21
  54. package/src/api/dto/elements/index.ts +0 -5
  55. package/src/api/dto/elements/properties/index.ts +0 -3
  56. package/src/api/dto/elements/properties/property-definitions-actions-v2.ts +0 -53
  57. package/src/api/dto/elements/properties/property-definitions.ts +0 -62
  58. package/src/api/dto/elements/properties/property-values.ts +0 -17
  59. package/src/api/dto/export/exporter.ts +0 -73
  60. package/src/api/dto/export/index.ts +0 -3
  61. package/src/api/dto/export/job.ts +0 -75
  62. package/src/api/dto/export/pipeline.ts +0 -20
  63. package/src/api/dto/figma-components/assets/download.ts +0 -30
  64. package/src/api/dto/figma-components/assets/index.ts +0 -1
  65. package/src/api/dto/figma-components/index.ts +0 -1
  66. package/src/api/dto/index.ts +0 -8
  67. package/src/api/dto/liveblocks/auth-response.ts +0 -7
  68. package/src/api/dto/liveblocks/index.ts +0 -1
  69. package/src/api/dto/users/index.ts +0 -1
  70. package/src/api/dto/users/profile/index.ts +0 -1
  71. package/src/api/dto/users/profile/update.ts +0 -7
  72. package/src/api/dto/workspaces/git.ts +0 -32
  73. package/src/api/dto/workspaces/index.ts +0 -5
  74. package/src/api/dto/workspaces/integrations.ts +0 -34
  75. package/src/api/dto/workspaces/membership.ts +0 -29
  76. package/src/api/dto/workspaces/npm-registry.ts +0 -35
  77. package/src/api/dto/workspaces/workspace.ts +0 -16
  78. package/src/api/index.ts +0 -3
  79. package/src/api/payloads/design-systems/brand.ts +0 -11
  80. package/src/api/payloads/design-systems/index.ts +0 -2
  81. package/src/api/payloads/design-systems/version.ts +0 -29
  82. package/src/api/payloads/documentation/block-definitions.ts +0 -12
  83. package/src/api/payloads/documentation/design-data-doc-diff.ts +0 -7
  84. package/src/api/payloads/documentation/index.ts +0 -2
  85. package/src/api/payloads/export/index.ts +0 -1
  86. package/src/api/payloads/export/pipeline.ts +0 -46
  87. package/src/api/payloads/index.ts +0 -6
  88. package/src/api/payloads/liveblocks/auth.ts +0 -7
  89. package/src/api/payloads/liveblocks/index.ts +0 -1
  90. package/src/api/payloads/users/index.ts +0 -2
  91. package/src/api/payloads/users/notifications/index.ts +0 -1
  92. package/src/api/payloads/users/notifications/notification-settings.ts +0 -17
  93. package/src/api/payloads/users/profile/index.ts +0 -1
  94. package/src/api/payloads/users/profile/update.ts +0 -4
  95. package/src/api/payloads/workspaces/index.ts +0 -2
  96. package/src/api/payloads/workspaces/workspace-configuration.ts +0 -50
  97. package/src/api/payloads/workspaces/workspace-integrations.ts +0 -26
  98. package/src/index.ts +0 -3
  99. package/src/utils/hash.ts +0 -62
  100. package/src/utils/index.ts +0 -1
  101. package/src/yjs/design-system-content/documentation-hierarchy.ts +0 -33
  102. package/src/yjs/design-system-content/index.ts +0 -2
  103. package/src/yjs/design-system-content/item-configuration.ts +0 -93
  104. package/src/yjs/docs-editor/blocks-to-prosemirror.ts +0 -697
  105. package/src/yjs/docs-editor/index.ts +0 -7
  106. package/src/yjs/docs-editor/list-tree-builder.ts +0 -135
  107. package/src/yjs/docs-editor/mock.ts +0 -2447
  108. package/src/yjs/docs-editor/model/block.ts +0 -8
  109. package/src/yjs/docs-editor/model/index.ts +0 -2
  110. package/src/yjs/docs-editor/model/page.ts +0 -8
  111. package/src/yjs/docs-editor/prosemirror/index.ts +0 -2
  112. package/src/yjs/docs-editor/prosemirror/schema.ts +0 -657
  113. package/src/yjs/docs-editor/prosemirror/types.ts +0 -19
  114. package/src/yjs/docs-editor/prosemirror-to-blocks.ts +0 -900
  115. package/src/yjs/docs-editor/utils.ts +0 -115
  116. package/src/yjs/index.ts +0 -3
  117. package/src/yjs/version-room/backend.ts +0 -60
  118. package/src/yjs/version-room/base.ts +0 -185
  119. package/src/yjs/version-room/frontend.ts +0 -298
  120. package/src/yjs/version-room/index.ts +0 -4
  121. package/src/yjs/version-room/utils.ts +0 -6
@@ -1,900 +0,0 @@
1
- import {
2
- PageBlockAppearanceV2,
3
- PageBlockCalloutType,
4
- PageBlockColorV2,
5
- PageBlockDefinition,
6
- PageBlockDefinitionProperty,
7
- PageBlockDefinitionPropertyType,
8
- PageBlockItemAssetPropertyValue,
9
- PageBlockItemAssetValue,
10
- PageBlockItemBooleanValue,
11
- PageBlockItemCodeValue,
12
- PageBlockItemColorValue,
13
- PageBlockItemComponentPropertyValue,
14
- PageBlockItemComponentValue,
15
- PageBlockItemDividerValue,
16
- PageBlockItemEmbedValue,
17
- PageBlockItemFigmaComponentValue,
18
- PageBlockItemFigmaNodeValue,
19
- PageBlockItemImageValue,
20
- PageBlockItemMarkdownValue,
21
- PageBlockItemMultiRichTextValue,
22
- PageBlockItemMultiSelectValue,
23
- PageBlockItemNumberValue,
24
- PageBlockItemRichTextValue,
25
- PageBlockItemSandboxValue,
26
- PageBlockItemSingleSelectValue,
27
- PageBlockItemStorybookValue,
28
- PageBlockItemTableCell,
29
- PageBlockItemTableNode,
30
- PageBlockItemTableValue,
31
- PageBlockItemTextValue,
32
- PageBlockItemTokenPropertyValue,
33
- PageBlockItemTokenTypeValue,
34
- PageBlockItemTokenValue,
35
- PageBlockItemUntypedValue,
36
- PageBlockItemUrlValue,
37
- PageBlockItemV2,
38
- PageBlockTableCellAlignment,
39
- PageBlockText,
40
- PageBlockTextSpan,
41
- PageBlockTextSpanAttribute,
42
- PageSectionColumnV2,
43
- PageSectionItemV2,
44
- nullishToOptional,
45
- } from "@supernova-studio/model";
46
- import { yXmlFragmentToProsemirrorJSON } from "y-prosemirror";
47
- import * as Y from "yjs";
48
- import { ZodSchema, ZodTypeDef, z } from "zod";
49
- import { PageBlockEditorModel, PageSectionEditorModel } from "./model/block";
50
- import { DocumentationPageEditorModel } from "./model/page";
51
- import { ProsemirrorMark, ProsemirrorNode } from "./prosemirror/types";
52
- import { BlockDefinitionUtils } from "./utils";
53
-
54
- export function yDocToPage(yDoc: Y.Doc, definitions: PageBlockDefinition[]): DocumentationPageEditorModel {
55
- return yXmlFragmentToPage(yDoc.getXmlFragment("default"), definitions);
56
- }
57
-
58
- export function yXmlFragmentToPage(
59
- fragment: Y.XmlFragment,
60
- definitions: PageBlockDefinition[]
61
- ): DocumentationPageEditorModel {
62
- const prosemirrorJson = yXmlFragmentToProsemirrorJSON(fragment) as ProsemirrorNode;
63
- return prosemirrorDocToPage(prosemirrorJson, definitions);
64
- }
65
-
66
- export function prosemirrorDocToPage(
67
- prosemirrorDoc: ProsemirrorNode,
68
- definitions: PageBlockDefinition[]
69
- ): DocumentationPageEditorModel {
70
- const definitionsById = mapByUnique(definitions, d => d.id);
71
-
72
- return {
73
- blocks: internalProsemirrorNodesToPageItems(prosemirrorDoc.content ?? [], definitionsById),
74
- };
75
- }
76
-
77
- export function shallowProsemirrorNodeToBlock(
78
- prosemirrorNode: ProsemirrorNode,
79
- definition: PageBlockDefinition
80
- ): PageBlockEditorModel | null {
81
- return (
82
- prosemirrorNodeAndDefinitionToBlock(prosemirrorNode, definition, new Map([[definition.id, definition]]), 0)[0] ??
83
- null
84
- );
85
- }
86
-
87
- //
88
- // Sections
89
- //
90
-
91
- export function prosemirrorNodeToSection(
92
- prosemirrorNode: ProsemirrorNode,
93
- definitions: PageBlockDefinition[]
94
- ): PageSectionEditorModel | null {
95
- const definitionsById = mapByUnique(definitions, d => d.id);
96
- return internalProsemirrorNodeToSection(prosemirrorNode, definitionsById);
97
- }
98
-
99
- function internalProsemirrorNodeToSection(
100
- prosemirrorNode: ProsemirrorNode,
101
- definitionsMap: Map<string, PageBlockDefinition>
102
- ): PageSectionEditorModel | null {
103
- const id = getProsemirrorBlockId(prosemirrorNode);
104
- if (!id) return null;
105
-
106
- return {
107
- id: id,
108
- type: "Section",
109
- variantId: getProsemirrorBlockVariantId(prosemirrorNode),
110
- sectionType: "Tabs",
111
- appearance: {
112
- contentExpandToEdges: true,
113
- expandToEdges: true,
114
- },
115
- items: (prosemirrorNode.content ?? [])
116
- .filter(c => c.type === "sectionItem")
117
- .map(c => prosemirrorNodeToSectionItem(c, definitionsMap))
118
- .filter(nonNullFilter),
119
- };
120
- }
121
-
122
- function prosemirrorNodeToSectionItem(
123
- prosemirrorNode: ProsemirrorNode,
124
- definitionsMap: Map<string, PageBlockDefinition>
125
- ): PageSectionItemV2 | null {
126
- const id = getProsemirrorBlockId(prosemirrorNode);
127
- if (!id) return null;
128
-
129
- return {
130
- id: id,
131
- title: getProsemirrorAttribute(prosemirrorNode, "title", z.string()) ?? "",
132
- columns: (prosemirrorNode.content ?? [])
133
- .filter(c => c.type === "sectionItemColumn")
134
- .map(c => prosemirrorNodeToSectionColumns(c, definitionsMap))
135
- .filter(nonNullFilter),
136
- };
137
- }
138
-
139
- function prosemirrorNodeToSectionColumns(
140
- prosemirrorNode: ProsemirrorNode,
141
- definitionsMap: Map<string, PageBlockDefinition>
142
- ): PageSectionColumnV2 | null {
143
- const id = getProsemirrorBlockId(prosemirrorNode);
144
- if (!id) return null;
145
-
146
- return {
147
- id: id,
148
- blocks: internalProsemirrorNodesToBlocks(prosemirrorNode.content ?? [], definitionsMap, 0),
149
- };
150
- }
151
-
152
- //
153
- // Blocks
154
- //
155
-
156
- export function prosemirrorNodesToBlocks(prosemirrorNodes: ProsemirrorNode[], definitions: PageBlockDefinition[]) {
157
- const definitionsById = mapByUnique(definitions, d => d.id);
158
- return internalProsemirrorNodesToBlocks(prosemirrorNodes, definitionsById, 0);
159
- }
160
-
161
- function internalProsemirrorNodesToPageItems(
162
- prosemirrorNodes: ProsemirrorNode[],
163
- definitionsMap: Map<string, PageBlockDefinition>
164
- ): (PageBlockEditorModel | PageSectionEditorModel)[] {
165
- const result: (PageBlockEditorModel | PageSectionEditorModel)[] = [];
166
-
167
- for (const prosemirrorNode of prosemirrorNodes) {
168
- if (prosemirrorNode.type === "tabsSection") {
169
- // Section
170
- const section = prosemirrorNodeToSection(prosemirrorNode, Array.from(definitionsMap.values()));
171
- section && result.push(section);
172
- } else {
173
- // Block
174
- result.push(...internalProsemirrorNodeToBlock(prosemirrorNode, definitionsMap, 0));
175
- }
176
- }
177
-
178
- return result;
179
- }
180
-
181
- function internalProsemirrorNodesToBlocks(
182
- prosemirrorNodes: ProsemirrorNode[],
183
- definitionsMap: Map<string, PageBlockDefinition>,
184
- depth: number
185
- ): PageBlockEditorModel[] {
186
- return prosemirrorNodes.flatMap(prosemirrorNode => {
187
- return internalProsemirrorNodeToBlock(prosemirrorNode, definitionsMap, depth);
188
- });
189
- }
190
-
191
- function internalProsemirrorNodeToBlock(
192
- prosemirrorNode: ProsemirrorNode,
193
- definitionsMap: Map<string, PageBlockDefinition>,
194
- depth: number
195
- ): PageBlockEditorModel[] {
196
- const definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
197
- if (!definitionId) {
198
- console.warn(`definitionId on ${prosemirrorNode.type} is required to be interpreted as a block, skipping node`);
199
-
200
- return [];
201
- }
202
-
203
- const definition = definitionsMap.get(definitionId);
204
- if (!definition) {
205
- console.warn(
206
- `Block definitionId "${definitionId}" (prosemirror node ${prosemirrorNode.type}) is not among available definitions`
207
- );
208
- console.warn(prosemirrorNode);
209
- return [];
210
- }
211
-
212
- return prosemirrorNodeAndDefinitionToBlock(prosemirrorNode, definition, definitionsMap, depth);
213
- }
214
-
215
- function prosemirrorNodeAndDefinitionToBlock(
216
- prosemirrorNode: ProsemirrorNode,
217
- definition: PageBlockDefinition,
218
- definitionsMap: Map<string, PageBlockDefinition>,
219
- depth: number
220
- ): PageBlockEditorModel[] {
221
- const multiRichTextProperty = BlockDefinitionUtils.firstMultiRichTextProperty(definition);
222
- if (multiRichTextProperty) {
223
- return parseAsMultiRichText(prosemirrorNode, definition, multiRichTextProperty, definitionsMap, depth);
224
- }
225
-
226
- const block = shallowProsemirrorNodeAndDefinitionToBlock(prosemirrorNode, definition);
227
- return block ? [block] : [];
228
- }
229
-
230
- function shallowProsemirrorNodeAndDefinitionToBlock(
231
- prosemirrorNode: ProsemirrorNode,
232
- definition: PageBlockDefinition
233
- ): PageBlockEditorModel | null {
234
- const richTextProperty = BlockDefinitionUtils.firstRichTextProperty(definition);
235
- if (richTextProperty) {
236
- return parseAsRichText(prosemirrorNode, definition, richTextProperty);
237
- }
238
-
239
- const tableProperty = BlockDefinitionUtils.firstTableProperty(definition);
240
- if (tableProperty) {
241
- return parseAsTable(prosemirrorNode, definition, tableProperty);
242
- }
243
-
244
- if (definition.id === "io.supernova.block.divider") {
245
- return parseAsDivider(prosemirrorNode, definition);
246
- }
247
-
248
- return parseAsCustomBlock(prosemirrorNode, definition);
249
- }
250
-
251
- //
252
- // Single rich text
253
- //
254
-
255
- function parseAsRichText(
256
- prosemirrorNode: ProsemirrorNode,
257
- definition: PageBlockDefinition,
258
- property: PageBlockDefinitionProperty
259
- ): PageBlockEditorModel | null {
260
- const id = getProsemirrorBlockId(prosemirrorNode);
261
- if (!id) return null;
262
-
263
- const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
264
- const calloutType = parseCalloutType(getProsemirrorAttribute(prosemirrorNode, "type", z.string().optional()));
265
-
266
- return {
267
- id: id,
268
- type: "Block",
269
-
270
- data: {
271
- packageId: definition.id,
272
- indentLevel: 0,
273
- ...(variantId && { variantId: variantId }),
274
- items: [
275
- {
276
- id: id,
277
- props: {
278
- [property.id]: {
279
- // Required
280
- value: parseRichText(prosemirrorNode.content ?? []),
281
-
282
- // Optional
283
- ...(calloutType && { calloutType: calloutType }),
284
- } satisfies PageBlockItemRichTextValue,
285
- },
286
- },
287
- ],
288
- },
289
- };
290
- }
291
-
292
- function parseCalloutType(prosemirrorCalloutType: unknown): PageBlockCalloutType | undefined {
293
- if (!prosemirrorCalloutType) return undefined;
294
-
295
- switch (prosemirrorCalloutType) {
296
- case "critical":
297
- return "Error";
298
- case "neutral":
299
- return "Info";
300
- case "positive":
301
- return "Success";
302
- case "warning":
303
- return "Warning";
304
- case "primary":
305
- return "Primary";
306
- }
307
- }
308
-
309
- //
310
- // Multi rich text
311
- //
312
-
313
- function parseAsMultiRichText(
314
- prosemirrorNode: ProsemirrorNode,
315
- definition: PageBlockDefinition,
316
- property: PageBlockDefinitionProperty,
317
- definitionsMap: Map<string, PageBlockDefinition>,
318
- depth: number
319
- ): PageBlockEditorModel[] {
320
- const id = getProsemirrorBlockId(prosemirrorNode);
321
- if (!id) return [];
322
-
323
- const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
324
- const result: PageBlockEditorModel[] = [];
325
-
326
- // Flatten items
327
- const listItems: ProsemirrorNode[] = [];
328
- prosemirrorNode.content?.forEach(c => {
329
- // Illegal child of a list
330
- if (c.type !== "listItem") return;
331
-
332
- c.content?.forEach(cc => {
333
- listItems.push(cc);
334
- });
335
- });
336
-
337
- // Text list items buffering
338
- let bufferedTextItems: ProsemirrorNode[] = [];
339
- function flushBufferedTextItems() {
340
- if (!bufferedTextItems.length) return;
341
-
342
- const idSuffix = result.length ? `-${result.length}` : "";
343
-
344
- result.push({
345
- id: id + idSuffix,
346
- type: "Block",
347
- data: {
348
- packageId: definition.id,
349
- indentLevel: depth,
350
- ...(variantId && { variantId: variantId }),
351
- items: [
352
- {
353
- id: id + idSuffix,
354
- props: {
355
- [property.id]: {
356
- value: bufferedTextItems.map(paragraph => {
357
- return parseRichText(paragraph.content ?? []);
358
- }),
359
- } satisfies PageBlockItemMultiRichTextValue,
360
- },
361
- },
362
- ],
363
- },
364
- });
365
-
366
- bufferedTextItems = [];
367
- }
368
-
369
- listItems.forEach(item => {
370
- if (item.type === "paragraph") {
371
- bufferedTextItems.push(item);
372
- return;
373
- }
374
-
375
- // This is not a paragraph, flush text items first
376
- flushBufferedTextItems();
377
-
378
- result.push(...internalProsemirrorNodeToBlock(item, definitionsMap, depth + 1));
379
- });
380
-
381
- // Flush text items at the end
382
- flushBufferedTextItems();
383
-
384
- return result;
385
- }
386
-
387
- //
388
- // Rich text
389
- //
390
-
391
- function parseRichText(spans: ProsemirrorNode[]): PageBlockText {
392
- return {
393
- spans: spans.map(parseRichTextSpan).filter(nonNullFilter),
394
- };
395
- }
396
-
397
- function parseRichTextSpan(span: ProsemirrorNode): PageBlockTextSpan | null {
398
- if (span.type === "hardBreak") {
399
- return { text: "\n", attributes: [] };
400
- }
401
-
402
- if (span.type !== "text" || !span.text) return null;
403
-
404
- const marks = span.marks ?? [];
405
-
406
- return {
407
- text: span.text,
408
- attributes: marks.map(parseRichTextAttribute).filter(nonNullFilter),
409
- };
410
- }
411
-
412
- function parseRichTextAttribute(mark: ProsemirrorMark): PageBlockTextSpanAttribute | null {
413
- switch (mark.type) {
414
- case "bold":
415
- return { type: "Bold" };
416
- case "italic":
417
- return { type: "Italic" };
418
- case "strike":
419
- return { type: "Strikethrough" };
420
- case "code":
421
- return { type: "Code" };
422
- case "link":
423
- return parseProsemirrorLink(mark);
424
- }
425
-
426
- return null;
427
- }
428
-
429
- function parseProsemirrorLink(mark: ProsemirrorMark): PageBlockTextSpanAttribute | null {
430
- const href = getProsemirrorAttribute(mark, "href", z.string().optional());
431
- if (!href) return null;
432
-
433
- const target = getProsemirrorAttribute(mark, "target", z.string().optional());
434
- const openInNewTab = target === "_blank";
435
-
436
- if (href.startsWith("@")) {
437
- return {
438
- type: "Link",
439
- openInNewWindow: openInNewTab,
440
- openInNewTab: openInNewTab,
441
- documentationItemId: href.split(":")[1],
442
- };
443
- } else {
444
- return {
445
- type: "Link",
446
- openInNewWindow: openInNewTab,
447
- openInNewTab: openInNewTab,
448
- link: href,
449
- };
450
- }
451
- }
452
-
453
- //
454
- // Embed
455
- //
456
-
457
- function parseAsTable(
458
- prosemirrorNode: ProsemirrorNode,
459
- definition: PageBlockDefinition,
460
- property: PageBlockDefinitionProperty
461
- ): PageBlockEditorModel | null {
462
- const id = getProsemirrorBlockId(prosemirrorNode);
463
- if (!id) return null;
464
-
465
- const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
466
- const hasBorder = getProsemirrorAttribute(prosemirrorNode, "hasBorder", z.boolean().optional()) !== false;
467
-
468
- const tableChild = prosemirrorNode.content?.find(c => c.type === "table");
469
- if (!tableChild) {
470
- return emptyTable(id, variantId, 0);
471
- }
472
-
473
- // Nodes can are considered rows if they have correct node type + their content is non-empty
474
- const rows = tableChild.content?.filter(c => c.type === "tableRow" && !!c.content?.length) ?? [];
475
- if (!rows.length) {
476
- return emptyTable(id, variantId, 0);
477
- }
478
-
479
- const rowHeaderCells = rows[0].content?.filter(c => c.type === "tableHeader").length ?? 0;
480
- const columnHeaderCells = rows.filter(r => r.content?.[0]?.type === "tableHeader").length;
481
-
482
- const hasHeaderRow = rows[0].content?.length === rowHeaderCells;
483
- const hasHeaderColumn = rows.length === columnHeaderCells;
484
-
485
- const tableValue: PageBlockItemTableValue = {
486
- showBorder: hasBorder,
487
- highlightHeaderRow: hasHeaderRow,
488
- highlightHeaderColumn: hasHeaderColumn,
489
- value: [],
490
- };
491
-
492
- tableValue.value = rows.map(row => {
493
- return {
494
- cells: (row.content ?? []).map(parseAsTableCell).filter(nonNullFilter),
495
- };
496
- });
497
-
498
- return {
499
- id: id,
500
- type: "Block",
501
- data: {
502
- packageId: "io.supernova.block.table",
503
- indentLevel: 0,
504
- variantId: "default",
505
-
506
- ...(variantId && { variantId: variantId }),
507
-
508
- items: [
509
- {
510
- id: id,
511
- props: {
512
- table: tableValue,
513
- },
514
- },
515
- ],
516
- },
517
- };
518
- }
519
-
520
- function parseAsTableCell(prosemirrorNode: ProsemirrorNode): PageBlockItemTableCell | null {
521
- const id = getProsemirrorBlockId(prosemirrorNode);
522
- if (!id) return null;
523
-
524
- const textAlign = getProsemirrorAttribute(prosemirrorNode, "textAlign", z.string().optional());
525
-
526
- let columnWidth: number | undefined;
527
- const columnWidthArray = getProsemirrorAttribute(prosemirrorNode, "colwidth", z.array(z.number()).optional());
528
- if (columnWidthArray) {
529
- columnWidth = roundDimension(columnWidthArray[0]);
530
- }
531
-
532
- const tableNodes = (prosemirrorNode.content ?? []).map(parseAsTableNode).filter(nonNullFilter);
533
-
534
- return {
535
- id: id,
536
- alignment: parseTableCellAlignment(textAlign),
537
- ...(columnWidth && { columnWidth }),
538
- nodes: tableNodes.length ? tableNodes : emptyTableCellContent(),
539
- };
540
- }
541
-
542
- function parseTableCellAlignment(alignment: unknown): PageBlockTableCellAlignment {
543
- if (!alignment) return "Left";
544
-
545
- switch (alignment) {
546
- case "left":
547
- return "Left";
548
- case "center":
549
- return "Center";
550
- case "right":
551
- return "Right";
552
-
553
- default:
554
- return "Left";
555
- }
556
- }
557
-
558
- function parseAsTableNode(prosemirrorNode: ProsemirrorNode): PageBlockItemTableNode | null {
559
- const id = getProsemirrorBlockId(prosemirrorNode);
560
- if (!id) return null;
561
-
562
- switch (prosemirrorNode.type) {
563
- case "paragraph":
564
- return {
565
- type: "RichText",
566
- id: id,
567
- value: parseRichText(prosemirrorNode.content ?? []),
568
- };
569
-
570
- case "image":
571
- const items = getProsemirrorAttribute(prosemirrorNode, "items", z.string());
572
- if (!items) return null;
573
-
574
- const parsedItems = PageBlockItemV2.array().safeParse(JSON.parse(items));
575
- if (!parsedItems.success) return null;
576
-
577
- const rawImagePropertyValue = parsedItems.data[0]?.props.image;
578
- if (!rawImagePropertyValue) return null;
579
-
580
- const imagePropertyValueParseResult = PageBlockItemImageValue.safeParse(rawImagePropertyValue);
581
- if (!imagePropertyValueParseResult.success) return null;
582
-
583
- return {
584
- type: "Image",
585
- id: id,
586
- value: imagePropertyValueParseResult.data.value,
587
- caption: imagePropertyValueParseResult.data.caption,
588
- };
589
-
590
- default:
591
- return null;
592
- }
593
- }
594
-
595
- function emptyTable(id: string, variantId?: string, indentLevel?: number): PageBlockEditorModel {
596
- const tableValue: PageBlockItemTableValue = {
597
- showBorder: true,
598
- highlightHeaderColumn: false,
599
- highlightHeaderRow: false,
600
- value: [],
601
- };
602
-
603
- for (let i = 0; i < 2; i++) {
604
- tableValue.value.push({
605
- cells: [],
606
- });
607
-
608
- for (let j = 0; j < 3; j++) {
609
- tableValue.value[i].cells.push({
610
- id: "",
611
- alignment: "Left",
612
- nodes: emptyTableCellContent(),
613
- });
614
- }
615
- }
616
-
617
- return {
618
- id: id,
619
- type: "Block",
620
- data: {
621
- packageId: "io.supernova.block.table",
622
- indentLevel: indentLevel ?? 0,
623
- variantId: variantId,
624
- items: [
625
- {
626
- id: id,
627
- props: {
628
- table: tableValue,
629
- },
630
- },
631
- ],
632
- },
633
- };
634
- }
635
-
636
- function emptyTableCellContent(): PageBlockItemTableNode[] {
637
- return [
638
- {
639
- type: "RichText",
640
- id: "",
641
- value: { spans: [] },
642
- },
643
- ];
644
- }
645
-
646
- //
647
- // Divider
648
- //
649
-
650
- function parseAsDivider(
651
- prosemirrorNode: ProsemirrorNode,
652
- definition: PageBlockDefinition
653
- ): PageBlockEditorModel | null {
654
- const id = getProsemirrorBlockId(prosemirrorNode);
655
- if (!id) return null;
656
-
657
- const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
658
-
659
- return {
660
- id: id,
661
- type: "Block",
662
- data: {
663
- packageId: definition.id,
664
- indentLevel: 0,
665
-
666
- ...(variantId && { variantId: variantId }),
667
-
668
- items: [{ id: id, props: {} }],
669
- },
670
- };
671
- }
672
-
673
- //
674
- // Custom block
675
- //
676
-
677
- function parseAsCustomBlock(
678
- prosemirrorNode: ProsemirrorNode,
679
- definition: PageBlockDefinition
680
- ): PageBlockEditorModel | null {
681
- const id = getProsemirrorBlockId(prosemirrorNode);
682
- if (!id) return null;
683
-
684
- const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
685
-
686
- const appearance = parseAppearance(prosemirrorNode);
687
-
688
- const parsedItems = parseBlockItems(prosemirrorNode, definition) ?? [];
689
-
690
- // Some blocks can expect at least one item to be always present. In such case we fallback to an item
691
- // without any properties defined which should be a valid item for any combination of block settings.
692
- if (!parsedItems.length && definitionExpectsPlaceholderItem(definition)) {
693
- parsedItems.push({
694
- id: id,
695
- props: {},
696
- });
697
- }
698
-
699
- return {
700
- id: id,
701
- type: "Block",
702
- data: {
703
- packageId: definition.id,
704
- indentLevel: 0,
705
-
706
- ...(variantId && { variantId: variantId }),
707
- ...(appearance && { appearance: appearance }),
708
-
709
- items: parsedItems,
710
- },
711
- };
712
- }
713
-
714
- function definitionExpectsPlaceholderItem(definition: PageBlockDefinition): boolean {
715
- return (
716
- // Block needs an item when it's a data block (tokens, components, etc)
717
- !definition.behavior.items ||
718
- // Block needs an item when number of items is expected to be 1
719
- definition.behavior.items.numberOfItems === 1
720
- );
721
- }
722
-
723
- function parseBlockItems(prosemirrorNode: ProsemirrorNode, definition: PageBlockDefinition): PageBlockItemV2[] | null {
724
- const itemsString = getProsemirrorAttribute(prosemirrorNode, "items", z.string());
725
- if (!itemsString) return null;
726
-
727
- const itemsJson: unknown = JSON.parse(itemsString);
728
- if (!Array.isArray(itemsJson)) {
729
- console.error("Block `items` property must be a json array");
730
- return null;
731
- }
732
-
733
- return itemsJson.map(i => parseItem(i, definition)).filter(nonNullFilter);
734
- }
735
-
736
- function parseAppearance(prosemirrorNode: ProsemirrorNode): PageBlockAppearanceV2 | undefined {
737
- let appearance: PageBlockAppearanceV2 = {};
738
-
739
- const rawAppearanceString = getProsemirrorAttribute(prosemirrorNode, "appearance", z.string().optional());
740
- if (rawAppearanceString) {
741
- const parsedAppearance = PageBlockAppearanceV2.safeParse(JSON.parse(rawAppearanceString));
742
- if (parsedAppearance.success) {
743
- appearance = parsedAppearance.data;
744
- }
745
- }
746
-
747
- const columns = getProsemirrorAttribute(prosemirrorNode, "columns", z.number().optional());
748
- if (columns) {
749
- appearance.numberOfColumns = columns;
750
- }
751
-
752
- const backgroundColor = getProsemirrorAttribute(prosemirrorNode, "backgroundColor", z.string().optional());
753
- if (backgroundColor) {
754
- const parsedColor = PageBlockColorV2.safeParse(JSON.parse(backgroundColor));
755
- if (parsedColor.success) {
756
- appearance.itemBackgroundColor = parsedColor.data;
757
- }
758
- }
759
-
760
- if (!Object.keys(appearance).length) return undefined;
761
-
762
- return appearance;
763
- }
764
-
765
- function parseItem(rawItem: unknown, definition: PageBlockDefinition): PageBlockItemV2 | null {
766
- const parsedItem = PageBlockItemV2.safeParse(rawItem);
767
- if (!parsedItem.success) {
768
- return null;
769
- }
770
-
771
- const sanitizedProps: Record<string, PageBlockItemUntypedValue> = {};
772
-
773
- for (const property of definition.item.properties) {
774
- const value = parsedItem.data.props[property.id];
775
- if (!value) {
776
- continue;
777
- }
778
-
779
- const valueSchema = valueSchemaForPropertyType(property.type);
780
- const validationResult = valueSchema.safeParse(value);
781
-
782
- if (validationResult.success) {
783
- sanitizedProps[property.id] = validationResult.data;
784
- } else {
785
- console.log(validationResult.error.toString());
786
- }
787
- }
788
-
789
- return {
790
- id: parsedItem.data.id,
791
- ...(parsedItem.data.linksTo && { linksTo: parsedItem.data.linksTo }),
792
- props: sanitizedProps,
793
- };
794
- }
795
-
796
- function valueSchemaForPropertyType(type: PageBlockDefinitionPropertyType) {
797
- switch (type) {
798
- case "RichText":
799
- return PageBlockItemRichTextValue;
800
- case "MultiRichText":
801
- return PageBlockItemMultiRichTextValue;
802
- case "Text":
803
- return PageBlockItemTextValue;
804
- case "Boolean":
805
- return PageBlockItemBooleanValue;
806
- case "Number":
807
- return PageBlockItemNumberValue;
808
- case "SingleSelect":
809
- return PageBlockItemSingleSelectValue;
810
- case "MultiSelect":
811
- return PageBlockItemMultiSelectValue;
812
- case "Image":
813
- return PageBlockItemImageValue;
814
- case "Token":
815
- return PageBlockItemTokenValue;
816
- case "TokenType":
817
- return PageBlockItemTokenTypeValue;
818
- case "TokenProperty":
819
- return PageBlockItemTokenPropertyValue;
820
- case "Component":
821
- return PageBlockItemComponentValue;
822
- case "ComponentProperty":
823
- return PageBlockItemComponentPropertyValue;
824
- case "Asset":
825
- return PageBlockItemAssetValue;
826
- case "AssetProperty":
827
- return PageBlockItemAssetPropertyValue;
828
- case "FigmaNode":
829
- return PageBlockItemFigmaNodeValue;
830
- case "EmbedURL":
831
- return PageBlockItemEmbedValue;
832
- case "URL":
833
- return PageBlockItemUrlValue;
834
- case "Markdown":
835
- return PageBlockItemMarkdownValue;
836
- case "Code":
837
- return PageBlockItemCodeValue;
838
- case "CodeSandbox":
839
- return PageBlockItemSandboxValue;
840
- case "Table":
841
- return PageBlockItemTableValue;
842
- case "Divider":
843
- return PageBlockItemDividerValue;
844
- case "Storybook":
845
- return PageBlockItemStorybookValue;
846
- case "Color":
847
- return PageBlockItemColorValue;
848
- case "FigmaComponent":
849
- return PageBlockItemFigmaComponentValue;
850
- }
851
- }
852
-
853
- //
854
- // Attributes
855
- //
856
-
857
- function getProsemirrorBlockId(prosemirrorNode: ProsemirrorNode): string | undefined {
858
- const id = getProsemirrorAttribute(prosemirrorNode, "id", z.string());
859
- if (!id) console.warn(`Prosemirror attribute "id" on ${prosemirrorNode.type} is required`);
860
- return id;
861
- }
862
-
863
- function getProsemirrorBlockVariantId(prosemirrorNode: ProsemirrorNode): string | undefined {
864
- return getProsemirrorAttribute(prosemirrorNode, "variantId", nullishToOptional(z.string()));
865
- }
866
-
867
- function getProsemirrorAttribute<I, O>(
868
- prosemirrorNode: ProsemirrorNode,
869
- attributeName: string,
870
- validationSchema: ZodSchema<O, ZodTypeDef, I>
871
- ): O | undefined {
872
- const parsedAttr = validationSchema.safeParse(prosemirrorNode.attrs?.[attributeName]);
873
- if (parsedAttr.success) {
874
- return parsedAttr.data;
875
- } else {
876
- console.warn(`Prosemirror attribute ${attributeName} on ${prosemirrorNode.type} is invalid`);
877
- console.warn(parsedAttr.error.flatten());
878
- return undefined;
879
- }
880
- }
881
-
882
- //
883
- // Utils
884
- //
885
-
886
- function nonNullFilter<T>(item: T | null): item is T {
887
- return item !== null;
888
- }
889
-
890
- function mapByUnique<V, K>(items: V[], keyFn: (item: V) => K): Map<K, V> {
891
- const result = new Map<K, V>();
892
- for (const item of items) {
893
- result.set(keyFn(item), item);
894
- }
895
- return result;
896
- }
897
-
898
- function roundDimension(dimension: number) {
899
- return Math.round(dimension * 100) / 100;
900
- }