@supernova-studio/client 0.0.2 → 0.0.4

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/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src";
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@supernova-studio/client",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Supernova Data Models",
5
- "main": "dist/index.mjs",
6
- "types": "dist/index.d.mts",
5
+ "main": "index.ts",
7
6
  "source": "index.ts",
8
7
  "files": [
9
- "dist"
8
+ "src"
10
9
  ],
11
10
  "scripts": {
12
11
  "build": "tsc"
@@ -0,0 +1 @@
1
+ export * from "./responses";
@@ -0,0 +1,8 @@
1
+ import { PageBlockDefinition } from "@supernova-studio/model";
2
+ import { z } from "zod";
3
+
4
+ export const GetBlockDefinitionsResponse = z.object({
5
+ definitions: z.array(PageBlockDefinition),
6
+ });
7
+
8
+ export type GetBlockDefinitionsResponse = z.infer<typeof GetBlockDefinitionsResponse>;
@@ -0,0 +1 @@
1
+ export * from "./get-block-definitions";
@@ -0,0 +1,372 @@
1
+ import * as Y from "yjs";
2
+ import {
3
+ PageBlockDefinition,
4
+ PageBlockDefinitionProperty,
5
+ PageBlockItemV2,
6
+ PageBlockText,
7
+ PageBlockTextSpan,
8
+ PageBlockTextSpanAttribute,
9
+ PageBlockDefinitionRichTextPropertyStyle,
10
+ } from "@supernova-studio/model";
11
+ import { PageBlockEditorModel } from "./model/block";
12
+ import {
13
+ ProsemirrorBlockItem,
14
+ ProsemirrorBlockItemPropertyValue,
15
+ ProsemirrorNode,
16
+ ProsemirrorMark,
17
+ } from "./prosemirror/types";
18
+ import { BlockDefinitionUtils, SDKBlockParsingUtils } from "./utils";
19
+ import { DocumentationPageEditorModel } from "./model";
20
+ import { prosemirrorJSONToYXmlFragment } from "y-prosemirror";
21
+ import { pmSchema } from "./prosemirror";
22
+
23
+ type Input = {
24
+ block: PageBlockEditorModel;
25
+ definition: PageBlockDefinition;
26
+ richTextProperty: PageBlockDefinitionProperty;
27
+ };
28
+
29
+ type InputWithValue = Input & {
30
+ richTextPropertyValue: PageBlockText;
31
+ };
32
+
33
+ export function pageToYXmlFragment(
34
+ page: DocumentationPageEditorModel,
35
+ definitions: PageBlockDefinition[],
36
+ fragment: Y.XmlFragment
37
+ ): Y.XmlFragment {
38
+ const doc = pageToProsemirrorDoc(page, definitions);
39
+ return prosemirrorJSONToYXmlFragment(pmSchema, doc, fragment);
40
+ }
41
+
42
+ export function pageToProsemirrorDoc(
43
+ page: DocumentationPageEditorModel,
44
+ definitions: PageBlockDefinition[]
45
+ ): ProsemirrorNode {
46
+ return {
47
+ type: "doc",
48
+ content: page.blocks
49
+ .map(b => {
50
+ const definition = blockDefinitionForBlock(b, definitions);
51
+ return blockToProsemirrorNode(b, definition);
52
+ })
53
+ .filter(nonNullFilter),
54
+ };
55
+ }
56
+
57
+ export function blockDefinitionForBlock(block: PageBlockEditorModel, definitions: PageBlockDefinition[]) {
58
+ const definition = definitions.find(d => d.id === block.data.packageId);
59
+ if (!definition) throw new Error(`Could not find definition for ${block.id} (${block.data.packageId})`);
60
+
61
+ return definition;
62
+ }
63
+
64
+ export function blockToProsemirrorNode(
65
+ block: PageBlockEditorModel,
66
+ definition: PageBlockDefinition
67
+ ): ProsemirrorNode | null {
68
+ const richTextProperty = BlockDefinitionUtils.firstRichTextProperty(definition);
69
+
70
+ if (richTextProperty) {
71
+ return serializeAsRichTextBlock({
72
+ block: block,
73
+ definition: definition,
74
+ richTextProperty: richTextProperty,
75
+ });
76
+ }
77
+
78
+ return serializeAsCustomBlock(block, definition);
79
+ }
80
+
81
+ export function serializeAsRichTextBlock(input: Input): ProsemirrorNode {
82
+ const { block, definition, richTextProperty } = input;
83
+
84
+ const blockItem = SDKBlockParsingUtils.singleBlockItem(block);
85
+ const textPropertyValue = SDKBlockParsingUtils.richTextPropertyValue(blockItem, richTextProperty.id);
86
+
87
+ const enrichedInput: InputWithValue = { ...input, richTextPropertyValue: textPropertyValue };
88
+
89
+ const style = richTextProperty.options?.style ?? PageBlockDefinitionRichTextPropertyStyle.default;
90
+
91
+ switch (style) {
92
+ case PageBlockDefinitionRichTextPropertyStyle.callout:
93
+ return serializeAsCallout(enrichedInput);
94
+ case PageBlockDefinitionRichTextPropertyStyle.default:
95
+ return serializeAsParagraph(enrichedInput);
96
+
97
+ case PageBlockDefinitionRichTextPropertyStyle.quote:
98
+ return serializeAsBlockquote(enrichedInput);
99
+
100
+ case PageBlockDefinitionRichTextPropertyStyle.title1:
101
+ case PageBlockDefinitionRichTextPropertyStyle.title2:
102
+ case PageBlockDefinitionRichTextPropertyStyle.title3:
103
+ case PageBlockDefinitionRichTextPropertyStyle.title4:
104
+ case PageBlockDefinitionRichTextPropertyStyle.title5:
105
+ return serializeAsHeading(enrichedInput);
106
+
107
+ // Todo: verify
108
+ case PageBlockDefinitionRichTextPropertyStyle.ol:
109
+ case PageBlockDefinitionRichTextPropertyStyle.ul:
110
+ throw new Error(`Not allowed!`);
111
+ }
112
+ }
113
+
114
+ //
115
+ // Blocks
116
+ //
117
+
118
+ function serializeAsParagraph(input: InputWithValue): ProsemirrorNode {
119
+ return {
120
+ type: "paragraph",
121
+ attrs: serializeBlockNodeAttributes(input),
122
+ content: serializeRichText(input.richTextPropertyValue),
123
+ };
124
+ }
125
+
126
+ function serializeAsHeading(input: InputWithValue): ProsemirrorNode {
127
+ return {
128
+ type: "heading",
129
+ attrs: {
130
+ ...serializeBlockNodeAttributes(input),
131
+ level: richTextHeadingLevel(input.richTextProperty),
132
+ },
133
+ content: serializeRichText(input.richTextPropertyValue),
134
+ };
135
+ }
136
+
137
+ function serializeAsBlockquote(input: InputWithValue): ProsemirrorNode {
138
+ return {
139
+ type: "blockquote",
140
+ attrs: serializeBlockNodeAttributes(input),
141
+ content: serializeRichText(input.richTextPropertyValue),
142
+ };
143
+ }
144
+
145
+ function serializeAsCallout(input: InputWithValue): ProsemirrorNode {
146
+ return {
147
+ type: "callout",
148
+ attrs: {
149
+ ...serializeBlockNodeAttributes(input),
150
+
151
+ // TODO Docs: type
152
+ type: "neutral",
153
+ },
154
+ content: serializeRichText(input.richTextPropertyValue),
155
+ };
156
+ }
157
+
158
+ //
159
+ // Attributes
160
+ //
161
+
162
+ function serializeBlockNodeAttributes(input: InputWithValue) {
163
+ const { block } = input;
164
+
165
+ return {
166
+ id: block.id,
167
+ definitionId: block.data.packageId,
168
+ ...(input.block.data.variantId && { variantId: input.block.data.variantId }),
169
+ };
170
+ }
171
+
172
+ function richTextHeadingLevel(property: PageBlockDefinitionProperty): number | undefined {
173
+ const style = property.options?.style;
174
+ if (!style) return undefined;
175
+
176
+ switch (style) {
177
+ case PageBlockDefinitionRichTextPropertyStyle.title1:
178
+ return 1;
179
+ case PageBlockDefinitionRichTextPropertyStyle.title2:
180
+ return 2;
181
+ case PageBlockDefinitionRichTextPropertyStyle.title3:
182
+ return 3;
183
+ case PageBlockDefinitionRichTextPropertyStyle.title4:
184
+ return 4;
185
+ case PageBlockDefinitionRichTextPropertyStyle.title5:
186
+ return 5;
187
+
188
+ case PageBlockDefinitionRichTextPropertyStyle.ol:
189
+ case PageBlockDefinitionRichTextPropertyStyle.ul:
190
+ case PageBlockDefinitionRichTextPropertyStyle.callout:
191
+ case PageBlockDefinitionRichTextPropertyStyle.default:
192
+ case PageBlockDefinitionRichTextPropertyStyle.quote:
193
+ return undefined;
194
+ }
195
+ }
196
+
197
+ //
198
+ // Rich text
199
+ //
200
+
201
+ function serializeRichText(richText: PageBlockText) {
202
+ return richText.spans.map(serializeTextSpan).flat();
203
+ }
204
+
205
+ function serializeTextSpan(span: PageBlockTextSpan): ProsemirrorNode[] {
206
+ const marks = span.attributes.map(serializeTextSpanAttribute);
207
+
208
+ const textParts = span.text.split("\n");
209
+ const interspersed: ProsemirrorNode[] = [
210
+ {
211
+ type: "text",
212
+ text: textParts[0],
213
+ marks: marks,
214
+ },
215
+ ];
216
+
217
+ for (let i = 1; i < textParts.length; i++) {
218
+ interspersed.push(
219
+ {
220
+ type: "hardBreak",
221
+ },
222
+ {
223
+ type: "text",
224
+ text: textParts[i],
225
+ marks: marks,
226
+ }
227
+ );
228
+ }
229
+
230
+ return interspersed;
231
+ }
232
+
233
+ function serializeTextSpanAttribute(spanAttribute: PageBlockTextSpanAttribute): ProsemirrorMark {
234
+ switch (spanAttribute.type) {
235
+ case "Bold":
236
+ return { type: "bold", attrs: {} };
237
+ case "Italic":
238
+ return { type: "italic", attrs: {} };
239
+ case "Strikethrough":
240
+ return { type: "strikethrough", attrs: {} };
241
+ case "Code":
242
+ return { type: "code", attrs: {} };
243
+ case "Link":
244
+ return {
245
+ type: "link",
246
+ attrs: {
247
+ itemId: spanAttribute.documentationItemId,
248
+ href: spanAttribute.link,
249
+ target: spanAttribute.openInNewWindow ? "_blank" : "_self",
250
+ rel: "noopener noreferrer nofollow",
251
+ class: null,
252
+ },
253
+ };
254
+ }
255
+ }
256
+
257
+ export function serializeAsCustomBlock(block: PageBlockEditorModel, definition: PageBlockDefinition): ProsemirrorNode {
258
+ const items = block.data.items.map(i => serializeItem(i, block, definition));
259
+
260
+ return {
261
+ type: "blockNode",
262
+ attrs: {
263
+ id: block.id,
264
+ definitionId: block.data.packageId,
265
+ variantId: "default",
266
+ columns: 1,
267
+ items: JSON.stringify(items),
268
+
269
+ // TODO Docs: variant, columns
270
+ },
271
+ };
272
+ }
273
+
274
+ function serializeItem(
275
+ item: PageBlockItemV2,
276
+ block: PageBlockEditorModel,
277
+ definition: PageBlockDefinition
278
+ ): ProsemirrorBlockItem {
279
+ const result: ProsemirrorBlockItem = {
280
+ properties: [],
281
+ };
282
+
283
+ for (const property of definition.item.properties) {
284
+ const serializedValue = serializePropertyValue(item, property);
285
+ serializedValue && result.properties.push(serializedValue);
286
+ }
287
+
288
+ return result;
289
+ }
290
+
291
+ function serializePropertyValue(
292
+ item: PageBlockItemV2,
293
+ property: PageBlockDefinitionProperty
294
+ ): ProsemirrorBlockItemPropertyValue | undefined {
295
+ const value = item.props[property.id] ?? undefined;
296
+ if (!value) {
297
+ return undefined;
298
+ }
299
+
300
+ // TODO Docs: actual property serialization
301
+
302
+ const result: ProsemirrorBlockItemPropertyValue = {
303
+ id: property.id,
304
+ data: {},
305
+ };
306
+
307
+ if (typeof value === "string") {
308
+ result.data.value = value;
309
+ }
310
+
311
+ return result;
312
+ }
313
+
314
+ function nonNullFilter<T>(item: T | null): item is T {
315
+ return !!item;
316
+ }
317
+
318
+ // function serializePropertyValue(item: SDKBlockItem, property: SDKBlockDefinitionProperty) {
319
+ // switch (property.type) {
320
+ // case SDK.DocsBlockItemPropertyType.text: return value as string;
321
+
322
+ // case SDK.DocsBlockItemPropertyType.richText: return {}
323
+
324
+ // case SDK.DocsBlockItemPropertyType.boolean: return {}
325
+ // case SDK.DocsBlockItemPropertyType.number: return {}
326
+ // case SDK.DocsBlockItemPropertyType.singleSelect: return {}
327
+ // case SDK.DocsBlockItemPropertyType.multiSelect: return {}
328
+ // case SDK.DocsBlockItemPropertyType.image: return {}
329
+ // case SDK.DocsBlockItemPropertyType.token: return {}
330
+ // case SDK.DocsBlockItemPropertyType.tokenType: return {}
331
+ // case SDK.DocsBlockItemPropertyType.tokenProperty: return {}
332
+ // case SDK.DocsBlockItemPropertyType.component: return {}
333
+ // case SDK.DocsBlockItemPropertyType.componentProperty: return {}
334
+ // case SDK.DocsBlockItemPropertyType.asset: return {}
335
+ // case SDK.DocsBlockItemPropertyType.assetProperty: return {}
336
+ // case SDK.DocsBlockItemPropertyType.page: return {}
337
+ // case SDK.DocsBlockItemPropertyType.pageProperty: return {}
338
+ // case SDK.DocsBlockItemPropertyType.embedURL: return {}
339
+ // case SDK.DocsBlockItemPropertyType.embedFrame: return {}
340
+ // case SDK.DocsBlockItemPropertyType.markdown: return {}
341
+ // case SDK.DocsBlockItemPropertyType.code: return {}
342
+ // case SDK.DocsBlockItemPropertyType.codeSandbox: return {}
343
+ // case SDK.DocsBlockItemPropertyType.table: return {}
344
+ // case SDK.DocsBlockItemPropertyType.divider: return {}
345
+ // case SDK.DocsBlockItemPropertyType.storybook: return {}
346
+ // }
347
+ // }
348
+
349
+ // richText = "RichText",
350
+ // text = "Text",
351
+ // boolean = "Boolean",
352
+ // number = "Number",
353
+ // singleSelect = "SingleSelect",
354
+ // multiSelect = "MultiSelect",
355
+ // image = "Image",
356
+ // token = "Token",
357
+ // tokenType = "TokenType",
358
+ // tokenProperty = "TokenProperty",
359
+ // component = "Component",
360
+ // componentProperty = "ComponentProperty",
361
+ // asset = "Asset",
362
+ // assetProperty = "AssetProperty",
363
+ // page = "Page",
364
+ // pageProperty = "PageProperty",
365
+ // embedURL = "EmbedURL",
366
+ // embedFrame = "EmbedFrame",
367
+ // markdown = "Markdown",
368
+ // code = "Code",
369
+ // codeSandbox = "CodeSandbox",
370
+ // table = "Table",
371
+ // divider = "Divider",
372
+ // storybook = "Storybook"
@@ -0,0 +1,5 @@
1
+ export * from "./model";
2
+ export * from "./prosemirror";
3
+ export * from "./blocks-to-prosemirror";
4
+ export * from "./prosemirror-to-blocks";
5
+ export * from "./utils";
@@ -0,0 +1,9 @@
1
+ import { PageBlockDataV2 } from "@supernova-studio/model";
2
+ import { z } from "zod";
3
+
4
+ export const PageBlockEditorModel = z.object({
5
+ id: z.string(),
6
+ data: PageBlockDataV2,
7
+ });
8
+
9
+ export type PageBlockEditorModel = z.infer<typeof PageBlockEditorModel>;
@@ -0,0 +1,2 @@
1
+ export * from "./block";
2
+ export * from "./page";
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import { PageBlockEditorModel } from "./block";
3
+
4
+ export const DocumentationPageEditorModel = z.object({
5
+ blocks: z.array(PageBlockEditorModel),
6
+ });
7
+
8
+ export type DocumentationPageEditorModel = z.infer<typeof DocumentationPageEditorModel>;
@@ -0,0 +1,2 @@
1
+ export * from "./schema";
2
+ export * from "./types";