@supernova-studio/client 0.25.0 → 0.26.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.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Supernova Data Models",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import {
3
3
  DTOCreateDocumentationGroupInput,
4
- DTOCreateDocumentationTabGroupInput,
4
+ DTOCreateDocumentationTabInput,
5
5
  DTODeleteDocumentationGroupInput,
6
6
  DTODeleteDocumentationTabGroupInput,
7
7
  DTODuplicateDocumentationGroupInput,
@@ -22,8 +22,8 @@ export const DTODocumentationGroupCreateActionOutputV2 = z.object({
22
22
  output: SuccessPayload,
23
23
  });
24
24
 
25
- export const DTODocumentationTabGroupCreateActionOutputV2 = z.object({
26
- type: z.literal("DocumentationTabGroupCreate"),
25
+ export const DTODocumentationTabCreateActionOutputV2 = z.object({
26
+ type: z.literal("DocumentationTabCreate"),
27
27
  output: SuccessPayload,
28
28
  });
29
29
 
@@ -53,7 +53,7 @@ export const DTODocumentationTabGroupDeleteActionOutputV2 = z.object({
53
53
  });
54
54
 
55
55
  export type DTODocumentationGroupCreateActionOutputV2 = z.infer<typeof DTODocumentationGroupCreateActionOutputV2>;
56
- export type DTODocumentationTabGroupCreateActionOutputV2 = z.infer<typeof DTODocumentationTabGroupCreateActionOutputV2>;
56
+ export type DTODocumentationTabCreateActionOutputV2 = z.infer<typeof DTODocumentationTabCreateActionOutputV2>;
57
57
  export type DTODocumentationGroupUpdateActionOutputV2 = z.infer<typeof DTODocumentationGroupUpdateActionOutputV2>;
58
58
  export type DTODocumentationGroupMoveActionOutputV2 = z.infer<typeof DTODocumentationGroupMoveActionOutputV2>;
59
59
  export type DTODocumentationGroupDuplicateActionOutputV2 = z.infer<typeof DTODocumentationGroupDuplicateActionOutputV2>;
@@ -69,9 +69,9 @@ export const DTODocumentationGroupCreateActionInputV2 = z.object({
69
69
  input: DTOCreateDocumentationGroupInput,
70
70
  });
71
71
 
72
- export const DTODocumentationTabGroupCreateActionInputV2 = z.object({
73
- type: z.literal("DocumentationTabGroupCreate"),
74
- input: DTOCreateDocumentationTabGroupInput,
72
+ export const DTODocumentationTabCreateActionInputV2 = z.object({
73
+ type: z.literal("DocumentationTabCreate"),
74
+ input: DTOCreateDocumentationTabInput,
75
75
  });
76
76
 
77
77
  export const DTODocumentationGroupUpdateActionInputV2 = z.object({
@@ -100,7 +100,7 @@ export const DTODocumentationTabGroupDeleteActionInputV2 = z.object({
100
100
  });
101
101
 
102
102
  export type DTODocumentationGroupCreateActionInputV2 = z.infer<typeof DTODocumentationGroupCreateActionInputV2>;
103
- export type DTODocumentationTabGroupCreateActionInputV2 = z.infer<typeof DTODocumentationTabGroupCreateActionInputV2>;
103
+ export type DTODocumentationTabGroupCreateActionInputV2 = z.infer<typeof DTODocumentationTabCreateActionInputV2>;
104
104
  export type DTODocumentationGroupUpdateActionInputV2 = z.infer<typeof DTODocumentationGroupUpdateActionInputV2>;
105
105
  export type DTODocumentationGroupMoveActionInputV2 = z.infer<typeof DTODocumentationGroupMoveActionInputV2>;
106
106
  export type DTODocumentationGroupDuplicateActionInputV2 = z.infer<typeof DTODocumentationGroupDuplicateActionInputV2>;
@@ -80,12 +80,12 @@ export const DTODuplicateDocumentationGroupInput = z.object({
80
80
  parentPersistentId: z.string().uuid(),
81
81
  });
82
82
 
83
- export const DTOCreateDocumentationTabGroupInput = z.object({
83
+ export const DTOCreateDocumentationTabInput = z.object({
84
84
  // New group persistent id
85
85
  persistentId: z.string().uuid(),
86
86
 
87
87
  // Page that will become first tab of the tab group
88
- fromPageId: z.string(),
88
+ fromPagePersistentId: z.string(),
89
89
  tabName: z.string(),
90
90
  });
91
91
 
@@ -106,6 +106,6 @@ export type DTOCreateDocumentationGroupInput = z.infer<typeof DTOCreateDocumenta
106
106
  export type DTOUpdateDocumentationGroupInput = z.infer<typeof DTOUpdateDocumentationGroupInput>;
107
107
  export type DTOMoveDocumentationGroupInput = z.infer<typeof DTOMoveDocumentationGroupInput>;
108
108
  export type DTODuplicateDocumentationGroupInput = z.infer<typeof DTODuplicateDocumentationGroupInput>;
109
- export type DTOCreateDocumentationTabGroupInput = z.infer<typeof DTOCreateDocumentationTabGroupInput>;
109
+ export type DTOCreateDocumentationTabInput = z.infer<typeof DTOCreateDocumentationTabInput>;
110
110
  export type DTODeleteDocumentationTabGroupInput = z.infer<typeof DTODeleteDocumentationTabGroupInput>;
111
111
  export type DTODeleteDocumentationGroupInput = z.infer<typeof DTODeleteDocumentationGroupInput>;
@@ -22,8 +22,8 @@ import {
22
22
  DTODocumentationGroupMoveActionOutputV2,
23
23
  DTODocumentationGroupUpdateActionInputV2,
24
24
  DTODocumentationGroupUpdateActionOutputV2,
25
- DTODocumentationTabGroupCreateActionInputV2,
26
- DTODocumentationTabGroupCreateActionOutputV2,
25
+ DTODocumentationTabCreateActionInputV2,
26
+ DTODocumentationTabCreateActionOutputV2,
27
27
  DTODocumentationTabGroupDeleteActionInputV2,
28
28
  DTODocumentationTabGroupDeleteActionOutputV2,
29
29
  } from "./documentation/group-action";
@@ -43,7 +43,7 @@ export const DTOElementActionOutput = z.discriminatedUnion("type", [
43
43
 
44
44
  // Documentation groups
45
45
  DTODocumentationGroupCreateActionOutputV2,
46
- DTODocumentationTabGroupCreateActionOutputV2,
46
+ DTODocumentationTabCreateActionOutputV2,
47
47
  DTODocumentationGroupUpdateActionOutputV2,
48
48
  DTODocumentationGroupMoveActionOutputV2,
49
49
  DTODocumentationGroupDuplicateActionOutputV2,
@@ -70,7 +70,7 @@ export const DTOElementActionInput = z.discriminatedUnion("type", [
70
70
 
71
71
  // Documentation groups
72
72
  DTODocumentationGroupCreateActionInputV2,
73
- DTODocumentationTabGroupCreateActionInputV2,
73
+ DTODocumentationTabCreateActionInputV2,
74
74
  DTODocumentationGroupUpdateActionInputV2,
75
75
  DTODocumentationGroupMoveActionInputV2,
76
76
  DTODocumentationGroupDuplicateActionInputV2,
@@ -16,8 +16,12 @@ import {
16
16
  PageBlockDefinitionRichTextOptions,
17
17
  PageBlockDefinitionMutiRichTextOptions,
18
18
  PageBlockItemImageValue,
19
+ SupernovaException,
20
+ PageSectionItemV2,
21
+ PageSectionColumnV2,
22
+ mapByUnique,
19
23
  } from "@supernova-studio/model";
20
- import { PageBlockEditorModel } from "./model/block";
24
+ import { PageBlockEditorModel, PageSectionEditorModel } from "./model/block";
21
25
  import { ProsemirrorNode, ProsemirrorMark } from "./prosemirror/types";
22
26
  import { BlockDefinitionUtils, BlockParsingUtils } from "./utils";
23
27
  import { DocumentationPageEditorModel } from "./model";
@@ -62,12 +66,18 @@ export function pageToProsemirrorDoc(
62
66
  page: DocumentationPageEditorModel,
63
67
  definitions: PageBlockDefinition[]
64
68
  ): ProsemirrorNode {
69
+ const definitionsMap = mapByUnique(definitions, d => d.id);
70
+
65
71
  return {
66
72
  type: "doc",
67
73
  content: page.blocks
68
74
  .map(b => {
69
- const definition = blockDefinitionForBlock(b, definitions);
70
- return blockToProsemirrorNode(b, definition);
75
+ switch (b.type) {
76
+ case "Block":
77
+ return blockToProsemirrorNode(b, blockDefinitionForBlock(b, definitions));
78
+ case "Section":
79
+ return internalSectionToProsemirrorNode(b, definitionsMap);
80
+ }
71
81
  })
72
82
  .filter(nonNullFilter),
73
83
  };
@@ -82,6 +92,88 @@ export function blockDefinitionForBlock(block: PageBlockEditorModel, definitions
82
92
  return definition;
83
93
  }
84
94
 
95
+ //
96
+ // Sections
97
+ //
98
+
99
+ export function sectionToProsemirrorNode(
100
+ section: PageSectionEditorModel,
101
+ definitions: PageBlockDefinition[]
102
+ ): ProsemirrorNode {
103
+ const definitionsMap = mapByUnique(definitions, d => d.id);
104
+ return internalSectionToProsemirrorNode(section, definitionsMap);
105
+ }
106
+
107
+ function internalSectionToProsemirrorNode(
108
+ section: PageSectionEditorModel,
109
+ definitionsMap: Map<string, PageBlockDefinition>
110
+ ): ProsemirrorNode {
111
+ return {
112
+ type: "tabsSection",
113
+ attrs: {
114
+ id: section.id,
115
+ ...(section.variantId && { variantId: section.variantId }),
116
+ },
117
+ content: section.items.map(item => sectionItemToProsemirrorNode(item, definitionsMap)),
118
+ };
119
+ }
120
+
121
+ function sectionItemToProsemirrorNode(
122
+ sectionItem: PageSectionItemV2,
123
+ definitionsMap: Map<string, PageBlockDefinition>
124
+ ): ProsemirrorNode {
125
+ return {
126
+ type: "sectionItem",
127
+ attrs: {
128
+ id: sectionItem.id,
129
+ title: sectionItem.title,
130
+ },
131
+ content: sectionItem.columns.map(column => sectionColumnToProsemirrorNode(column, definitionsMap)),
132
+ };
133
+ }
134
+
135
+ function sectionColumnToProsemirrorNode(
136
+ sectionColumn: PageSectionColumnV2,
137
+ definitionsMap: Map<string, PageBlockDefinition>
138
+ ): ProsemirrorNode {
139
+ const blocks = sectionColumn.blocks
140
+ .map(block => {
141
+ return internalBlockToProsemirrorNode(block, definitionsMap);
142
+ })
143
+ .filter(nonNullFilter);
144
+
145
+ if (!blocks.length) {
146
+ blocks.push({
147
+ type: "paragraph",
148
+ attrs: {
149
+ id: "id",
150
+ definitionId: "io.supernova.block.rich-text",
151
+ },
152
+ });
153
+ }
154
+
155
+ return {
156
+ type: "sectionItemColumn",
157
+ attrs: {
158
+ id: sectionColumn.id,
159
+ },
160
+ content: blocks,
161
+ };
162
+ }
163
+
164
+ //
165
+ // Blocks
166
+ //
167
+
168
+ function internalBlockToProsemirrorNode(block: PageBlockEditorModel, definitionsMap: Map<string, PageBlockDefinition>) {
169
+ const definition = definitionsMap.get(block.data.packageId);
170
+ if (!definition) {
171
+ throw SupernovaException.shouldNotHappen(`Could not find definition for ${block.id} (${block.data.packageId})`);
172
+ }
173
+
174
+ return blockToProsemirrorNode(block, definition);
175
+ }
176
+
85
177
  export function blockToProsemirrorNode(
86
178
  block: PageBlockEditorModel,
87
179
  definition: PageBlockDefinition
@@ -525,7 +617,7 @@ export function serializeAsCustomBlock(block: PageBlockEditorModel, definition:
525
617
  const columns = block.data.appearance?.numberOfColumns;
526
618
 
527
619
  return {
528
- type: "blockNode",
620
+ type: serializeCustomBlockNodeType(block, definition),
529
621
  attrs: {
530
622
  id: block.id,
531
623
  definitionId: block.data.packageId,
@@ -537,6 +629,16 @@ export function serializeAsCustomBlock(block: PageBlockEditorModel, definition:
537
629
  };
538
630
  }
539
631
 
632
+ function serializeCustomBlockNodeType(block: PageBlockEditorModel, definition: PageBlockDefinition): string {
633
+ switch (block.data.packageId) {
634
+ case "io.supernova.block.image":
635
+ return "image";
636
+
637
+ default:
638
+ return "blockNode";
639
+ }
640
+ }
641
+
540
642
  function nonNullFilter<T>(item: T | null): item is T {
541
643
  return !!item;
542
644
  }
@@ -1,5 +1,8 @@
1
- import { PageBlockEditorModel as ModelPageBlockEditorModel } from "@supernova-studio/model";
1
+ import { PageBlockEditorModelV2, PageSectionEditorModelV2 } from "@supernova-studio/model";
2
2
  import { z } from "zod";
3
3
 
4
- export const PageBlockEditorModel = ModelPageBlockEditorModel;
4
+ export const PageBlockEditorModel = PageBlockEditorModelV2;
5
5
  export type PageBlockEditorModel = z.infer<typeof PageBlockEditorModel>;
6
+
7
+ export const PageSectionEditorModel = PageSectionEditorModelV2;
8
+ export type PageSectionEditorModel = z.infer<typeof PageSectionEditorModel>;
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
- import { PageBlockEditorModel } from "./block";
2
+ import { PageBlockEditorModel, PageSectionEditorModel } from "./block";
3
3
 
4
4
  export const DocumentationPageEditorModel = z.object({
5
- blocks: z.array(PageBlockEditorModel),
5
+ blocks: z.array(PageBlockEditorModel.or(PageSectionEditorModel)),
6
6
  });
7
7
 
8
8
  export type DocumentationPageEditorModel = z.infer<typeof DocumentationPageEditorModel>;
@@ -42,8 +42,11 @@ import {
42
42
  nullishToOptional,
43
43
  PageBlockItemFigmaNodeValue,
44
44
  PageBlockColorV2,
45
+ SupernovaException,
46
+ PageSectionItemV2,
47
+ PageSectionColumnV2,
45
48
  } from "@supernova-studio/model";
46
- import { PageBlockEditorModel } from "./model/block";
49
+ import { PageBlockEditorModel, PageSectionEditorModel } from "./model/block";
47
50
  import { DocumentationPageEditorModel } from "./model/page";
48
51
  import { BlockDefinitionUtils } from "./utils";
49
52
  import { yXmlFragmentToProsemirrorJSON } from "y-prosemirror";
@@ -69,32 +72,111 @@ export function prosemirrorDocToPage(
69
72
  const definitionsById = mapByUnique(definitions, d => d.id);
70
73
 
71
74
  return {
72
- blocks: (prosemirrorDoc.content ?? [])
73
- .map(prosemirrorNode => {
74
- const definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
75
- if (!definitionId) {
76
- console.warn(
77
- `definitionId on ${prosemirrorNode.type} is required to be interpreted as a block, skipping node`
78
- );
79
-
80
- return null;
81
- }
82
-
83
- const definition = definitionsById.get(definitionId);
84
- if (!definition) {
85
- console.warn(
86
- `Block definitionId "${definitionId}" (prosemirror node ${prosemirrorNode.type}) is not among available definitions`
87
- );
88
- console.warn(prosemirrorNode);
89
- return null;
90
- }
91
-
92
- return prosemirrorNodeToBlock(prosemirrorNode, definition);
93
- })
75
+ blocks: internalProsemirrorNodesToBlocks(prosemirrorDoc.content ?? [], definitionsById),
76
+ };
77
+ }
78
+
79
+ //
80
+ // Sections
81
+ //
82
+
83
+ export function prosemirrorNodeToSection(
84
+ prosemirrorNode: ProsemirrorNode,
85
+ definitions: PageBlockDefinition[]
86
+ ): PageSectionEditorModel | null {
87
+ const definitionsById = mapByUnique(definitions, d => d.id);
88
+ return internalProsemirrorNodeToSection(prosemirrorNode, definitionsById);
89
+ }
90
+
91
+ function internalProsemirrorNodeToSection(
92
+ prosemirrorNode: ProsemirrorNode,
93
+ definitionsMap: Map<string, PageBlockDefinition>
94
+ ): PageSectionEditorModel | null {
95
+ const id = getProsemirrorBlockId(prosemirrorNode);
96
+ if (!id) return null;
97
+
98
+ return {
99
+ id: id,
100
+ type: "Section",
101
+ variantId: getProsemirrorBlockVariantId(prosemirrorNode),
102
+ sectionType: "Tabs",
103
+ appearance: {
104
+ contentExpandToEdges: true,
105
+ expandToEdges: true,
106
+ },
107
+ items: (prosemirrorNode.content ?? [])
108
+ .filter(c => c.type === "sectionItem")
109
+ .map(c => prosemirrorNodeToSectionItem(c, definitionsMap))
94
110
  .filter(nonNullFilter),
95
111
  };
96
112
  }
97
113
 
114
+ function prosemirrorNodeToSectionItem(
115
+ prosemirrorNode: ProsemirrorNode,
116
+ definitionsMap: Map<string, PageBlockDefinition>
117
+ ): PageSectionItemV2 | null {
118
+ const id = getProsemirrorBlockId(prosemirrorNode);
119
+ if (!id) return null;
120
+
121
+ return {
122
+ id: id,
123
+ title: getProsemirrorAttribute(prosemirrorNode, "title", z.string()) ?? "",
124
+ columns: (prosemirrorNode.content ?? [])
125
+ .filter(c => c.type === "sectionItemColumn")
126
+ .map(c => prosemirrorNodeToSectionColumns(c, definitionsMap))
127
+ .filter(nonNullFilter),
128
+ };
129
+ }
130
+
131
+ function prosemirrorNodeToSectionColumns(
132
+ prosemirrorNode: ProsemirrorNode,
133
+ definitionsMap: Map<string, PageBlockDefinition>
134
+ ): PageSectionColumnV2 | null {
135
+ const id = getProsemirrorBlockId(prosemirrorNode);
136
+ if (!id) return null;
137
+
138
+ return {
139
+ id: id,
140
+ blocks: internalProsemirrorNodesToBlocks(prosemirrorNode.content ?? [], definitionsMap),
141
+ };
142
+ }
143
+
144
+ //
145
+ // Blocks
146
+ //
147
+
148
+ export function prosemirrorNodesToBlocks(prosemirrorNodes: ProsemirrorNode[], definitions: PageBlockDefinition[]) {
149
+ const definitionsById = mapByUnique(definitions, d => d.id);
150
+ return internalProsemirrorNodesToBlocks(prosemirrorNodes, definitionsById);
151
+ }
152
+
153
+ function internalProsemirrorNodesToBlocks(
154
+ prosemirrorNodes: ProsemirrorNode[],
155
+ definitionsMap: Map<string, PageBlockDefinition>
156
+ ): PageBlockEditorModel[] {
157
+ return prosemirrorNodes
158
+ .map(prosemirrorNode => {
159
+ const definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
160
+ if (!definitionId) {
161
+ console.warn(`definitionId on ${prosemirrorNode.type} is required to be interpreted as a block, skipping node`);
162
+
163
+ return null;
164
+ }
165
+
166
+ const definition = definitionsMap.get(definitionId);
167
+ if (!definition) {
168
+ console.warn(
169
+ `Block definitionId "${definitionId}" (prosemirror node ${prosemirrorNode.type}) is not among available definitions`
170
+ );
171
+ console.warn(prosemirrorNode);
172
+ return null;
173
+ }
174
+
175
+ return prosemirrorNodeToBlock(prosemirrorNode, definition);
176
+ })
177
+ .filter(nonNullFilter);
178
+ }
179
+
98
180
  export function prosemirrorNodeToBlock(
99
181
  prosemirrorNode: ProsemirrorNode,
100
182
  definition: PageBlockDefinition
@@ -139,6 +221,7 @@ function parseAsRichText(
139
221
  return {
140
222
  // TODO Artem: indent
141
223
  id: id,
224
+ type: "Block",
142
225
 
143
226
  ...(variantId && { variantId: variantId }),
144
227
 
@@ -197,6 +280,7 @@ function parseAsMultiRichText(
197
280
  return {
198
281
  // TODO Artem: indent
199
282
  id: id,
283
+ type: "Block",
200
284
  data: {
201
285
  packageId: definition.id,
202
286
  indentLevel: 0,
@@ -321,6 +405,7 @@ function parseAsTable(
321
405
 
322
406
  return {
323
407
  id: id,
408
+ type: "Block",
324
409
  data: {
325
410
  packageId: "io.supernova.block.table",
326
411
  indentLevel: 0,
@@ -438,6 +523,7 @@ function emptyTable(id: string, variantId?: string, indentLevel?: number): PageB
438
523
 
439
524
  return {
440
525
  id: id,
526
+ type: "Block",
441
527
  data: {
442
528
  packageId: "io.supernova.block.table",
443
529
  indentLevel: indentLevel ?? 0,
@@ -479,6 +565,7 @@ function parseAsDivider(
479
565
 
480
566
  return {
481
567
  id: id,
568
+ type: "Block",
482
569
  data: {
483
570
  packageId: definition.id,
484
571
  indentLevel: 0,
@@ -509,6 +596,7 @@ function parseAsCustomBlock(
509
596
 
510
597
  return {
511
598
  id: id,
599
+ type: "Block",
512
600
  data: {
513
601
  packageId: definition.id,
514
602
  indentLevel: 0,