@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 +1 -0
- package/package.json +3 -4
- package/src/api/index.ts +1 -0
- package/src/api/responses/get-block-definitions.ts +8 -0
- package/src/api/responses/index.ts +1 -0
- package/src/docs-editor/blocks-to-prosemirror.ts +372 -0
- package/src/docs-editor/index.ts +5 -0
- package/src/docs-editor/model/block.ts +9 -0
- package/src/docs-editor/model/index.ts +2 -0
- package/src/docs-editor/model/page.ts +8 -0
- package/src/docs-editor/prosemirror/index.ts +2 -0
- package/src/docs-editor/prosemirror/schema.ts +1253 -0
- package/src/docs-editor/prosemirror/types.ts +23 -0
- package/src/docs-editor/prosemirror-to-blocks.ts +170 -0
- package/src/docs-editor/utils.ts +81 -0
- package/src/index.ts +2 -0
- package/dist/index.d.mts +0 -1409
- package/dist/index.mjs +0 -5688
- package/dist/index.mjs.map +0 -1
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.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Supernova Data Models",
|
|
5
|
-
"main": "
|
|
6
|
-
"types": "dist/index.d.mts",
|
|
5
|
+
"main": "index.ts",
|
|
7
6
|
"source": "index.ts",
|
|
8
7
|
"files": [
|
|
9
|
-
"
|
|
8
|
+
"src"
|
|
10
9
|
],
|
|
11
10
|
"scripts": {
|
|
12
11
|
"build": "tsc"
|
package/src/api/index.ts
ADDED
|
@@ -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,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>;
|