@supernova-studio/client 0.28.0 → 0.29.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.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Supernova Data Models",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,11 @@
1
1
  import { DocumentationPageV1, DocumentationPageV2, ElementGroup, slugify } from "@supernova-studio/model";
2
2
  import { mapByUnique } from "@supernova-studio/model";
3
3
 
4
+ type GroupLike = {
5
+ persistentId: string;
6
+ parentPersistentId?: string;
7
+ };
8
+
4
9
  // The fact that DTO conversion is located here instead of main backend code is due to the fact
5
10
  // that we store page and group data in YJS documents in the same way as they are stored in the database.
6
11
  // Therefore, we need to expose this conversion to the client so that it can consume data from YJS documents.
@@ -36,11 +41,11 @@ export function buildDocPagePublishPaths(
36
41
  return result;
37
42
  }
38
43
 
39
- export function calculateElementParentChain(
44
+ export function calculateElementParentChain<T extends GroupLike>(
40
45
  elementParentPersistentId: string,
41
- groupPersistentIdToGroupMap: Map<string, ElementGroup>
42
- ): ElementGroup[] {
43
- const result: ElementGroup[] = [];
46
+ groupPersistentIdToGroupMap: Map<string, T>
47
+ ): T[] {
48
+ const result: T[] = [];
44
49
 
45
50
  let parentId: string | undefined = elementParentPersistentId;
46
51
  while (parentId) {
@@ -0,0 +1 @@
1
+ export * from "./link-preview";
@@ -0,0 +1,23 @@
1
+ import { DocumentationLinkPreview } from "@supernova-studio/model";
2
+ import { z } from "zod";
3
+
4
+ //
5
+ // Read
6
+ //
7
+
8
+ export const DTODocumentationLinkPreviewResponse = z.object({
9
+ linkPreview: DocumentationLinkPreview,
10
+ });
11
+
12
+ export type DTODocumentationLinkPreviewResponse = z.infer<typeof DTODocumentationLinkPreviewResponse>;
13
+
14
+ //
15
+ // Write
16
+ //
17
+
18
+ export const DTODocumentationLinkPreviewRequest = z.object({
19
+ url: z.string().optional(),
20
+ documentationItemPersistentId: z.string().optional(),
21
+ });
22
+
23
+ export type DTODocumentationLinkPreviewRequest = z.infer<typeof DTODocumentationLinkPreviewRequest>;
@@ -50,7 +50,7 @@ export const DTOElementActionOutput = z.discriminatedUnion("type", [
50
50
  DTODocumentationGroupDeleteActionOutputV2,
51
51
  DTODocumentationTabGroupDeleteActionOutputV2,
52
52
 
53
- // // Figma frames
53
+ // Figma frames
54
54
  DTOFigmaNodeRenderActionOutput,
55
55
  ]);
56
56
 
@@ -1,2 +1,3 @@
1
1
  export * from "./documentation";
2
2
  export * from "./liveblocks";
3
+ export * from "./workspaces";
@@ -0,0 +1 @@
1
+ export * from "./workspace-configuration";
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ import { SsoProvider, WorkspaceIpSettings, WorkspaceProfile } from "@supernova-studio/model";
3
+
4
+ const prohibitedSsoKeys = ["providerId", "metadataXml", "emailDomains"];
5
+
6
+ export function validateSsoPayload(ssoPayload: Partial<SsoProvider> | undefined) {
7
+ const keys = [];
8
+ let valid = true;
9
+ if (!ssoPayload) {
10
+ valid = true;
11
+ return {
12
+ valid,
13
+ keys: [],
14
+ };
15
+ }
16
+ for (const key of Object.keys(ssoPayload)) {
17
+ if (prohibitedSsoKeys.includes(key)) {
18
+ keys.push(key);
19
+ valid = false;
20
+ }
21
+ }
22
+ return {
23
+ valid,
24
+ keys,
25
+ };
26
+ }
27
+
28
+ export const NpmRegistryInput = z.object({
29
+ enabledScopes: z.array(z.string()),
30
+ customRegistryUrl: z.string().optional(),
31
+ bypassProxy: z.boolean().optional(),
32
+ npmProxyRegistryConfigId: z.string().optional(),
33
+ npmProxyVersion: z.number().optional(),
34
+ registryType: z.string(),
35
+ authType: z.string(),
36
+ authHeaderName: z.string(),
37
+ authHeaderValue: z.string(),
38
+ accessToken: z.string(),
39
+ username: z.string(),
40
+ password: z.string(),
41
+ });
42
+
43
+ export const WorkspaceConfigurationPayload = z.object({
44
+ ipWhitelist: WorkspaceIpSettings.partial().optional(),
45
+ sso: SsoProvider.partial().optional(),
46
+ npmRegistrySettings: NpmRegistryInput.partial().optional(),
47
+ profile: WorkspaceProfile.partial().optional(),
48
+ });
49
+
50
+ export type WorkspaceConfigurationPayload = z.infer<typeof WorkspaceConfigurationPayload>;
@@ -592,19 +592,28 @@ function serializeTextSpanAttribute(spanAttribute: PageBlockTextSpanAttribute):
592
592
  case "Code":
593
593
  return { type: "code", attrs: {} };
594
594
  case "Link":
595
- return {
596
- type: "link",
597
- attrs: {
598
- itemId: spanAttribute.documentationItemId,
599
- href: spanAttribute.link,
600
- target: spanAttribute.openInNewWindow ? "_blank" : "_self",
601
- rel: "noopener noreferrer nofollow",
602
- class: null,
603
- },
604
- };
595
+ if (spanAttribute.link) {
596
+ return serializeLinkMark(spanAttribute.link, spanAttribute.openInNewWindow ?? false);
597
+ } else if (spanAttribute.documentationItemId) {
598
+ return serializeLinkMark(`@page:${spanAttribute.documentationItemId}`, spanAttribute.openInNewWindow ?? false);
599
+ } else {
600
+ return serializeLinkMark("about:blank", spanAttribute.openInNewWindow ?? false);
601
+ }
605
602
  }
606
603
  }
607
604
 
605
+ function serializeLinkMark(href: string, openInNewWindow: boolean): ProsemirrorMark {
606
+ return {
607
+ type: "link",
608
+ attrs: {
609
+ href: href,
610
+ target: openInNewWindow ? "_blank" : "_self",
611
+ rel: "noopener noreferrer nofollow",
612
+ class: "tiptap-link",
613
+ },
614
+ };
615
+ }
616
+
608
617
  export function serializeAsCustomBlock(block: PageBlockEditorModel, definition: PageBlockDefinition): ProsemirrorNode {
609
618
  const items = block.data.items.map(i => {
610
619
  return {
@@ -1,8 +1,17 @@
1
1
  import { PageBlockDefinition } from "@supernova-studio/model";
2
2
 
3
3
  export function getMockPageBlockDefinitions(): PageBlockDefinition[] {
4
- // TODO Blocks
5
- return blocks;
4
+ return blocks.map(block => {
5
+ return {
6
+ ...block,
7
+ item: {
8
+ ...block.item,
9
+
10
+ // Make sure variatns array is copied
11
+ variants: [...block.item.variants],
12
+ },
13
+ };
14
+ });
6
15
  }
7
16
 
8
17
  const blocks: PageBlockDefinition[] = [
@@ -11,6 +20,7 @@ const blocks: PageBlockDefinition[] = [
11
20
  name: "Text",
12
21
  description: "Plain text",
13
22
  category: "Text",
23
+ icon: "https://cdn-assets.supernova.io/blocks/icons/text.svg",
14
24
  searchKeywords: ["paragraph", "rich text"],
15
25
  item: {
16
26
  properties: [
@@ -72,6 +82,7 @@ const blocks: PageBlockDefinition[] = [
72
82
  name: "Title 1",
73
83
  description: "Main sections within the page",
74
84
  category: "Text",
85
+ icon: "https://cdn-assets.supernova.io/blocks/icons/title-1.svg",
75
86
  searchKeywords: ["heading", "h1"],
76
87
  item: {
77
88
  properties: [
@@ -134,6 +145,7 @@ const blocks: PageBlockDefinition[] = [
134
145
  name: "Title 2",
135
146
  description: "Section subheadings",
136
147
  category: "Text",
148
+ icon: "https://cdn-assets.supernova.io/blocks/icons/title-2.svg",
137
149
  searchKeywords: ["heading", "h2"],
138
150
  item: {
139
151
  properties: [
@@ -196,6 +208,7 @@ const blocks: PageBlockDefinition[] = [
196
208
  name: "Title 3",
197
209
  description: "Further subsections",
198
210
  category: "Text",
211
+ icon: "https://cdn-assets.supernova.io/blocks/icons/title-3.svg",
199
212
  searchKeywords: ["heading", "h3"],
200
213
  item: {
201
214
  properties: [
@@ -258,6 +271,7 @@ const blocks: PageBlockDefinition[] = [
258
271
  name: "Title 4",
259
272
  description: "Details in subsections",
260
273
  category: "Text",
274
+ icon: "https://cdn-assets.supernova.io/blocks/icons/title-4.svg",
261
275
  searchKeywords: ["heading", "h4"],
262
276
  item: {
263
277
  properties: [
@@ -320,6 +334,7 @@ const blocks: PageBlockDefinition[] = [
320
334
  name: "Title 5",
321
335
  description: "Nuanced details or sub-points",
322
336
  category: "Text",
337
+ icon: "https://cdn-assets.supernova.io/blocks/icons/title-5.svg",
323
338
  searchKeywords: ["heading", "h5"],
324
339
  item: {
325
340
  properties: [
@@ -382,6 +397,7 @@ const blocks: PageBlockDefinition[] = [
382
397
  name: "Ordered list",
383
398
  description: "A list with numbers",
384
399
  category: "Text",
400
+ icon: "https://cdn-assets.supernova.io/blocks/icons/list-ordered.svg",
385
401
  searchKeywords: ["ol"],
386
402
  item: {
387
403
  properties: [
@@ -443,6 +459,7 @@ const blocks: PageBlockDefinition[] = [
443
459
  name: "Unordered list",
444
460
  description: "A list with bullet points",
445
461
  category: "Text",
462
+ icon: "https://cdn-assets.supernova.io/blocks/icons/list-unordered.svg",
446
463
  searchKeywords: ["ul", "bullet points"],
447
464
  item: {
448
465
  properties: [
@@ -504,6 +521,7 @@ const blocks: PageBlockDefinition[] = [
504
521
  name: "Divider",
505
522
  description: "A section divider",
506
523
  category: "Layout",
524
+ icon: "https://cdn-assets.supernova.io/blocks/icons/divider.svg",
507
525
  searchKeywords: ["hr", "line", "rule", "separator"],
508
526
  item: {
509
527
  properties: [
@@ -563,6 +581,7 @@ const blocks: PageBlockDefinition[] = [
563
581
  name: "Blockquote",
564
582
  description: "Display a quotation",
565
583
  category: "Text",
584
+ icon: "https://cdn-assets.supernova.io/blocks/icons/blockquote.svg",
566
585
  searchKeywords: ["cite"],
567
586
  item: {
568
587
  properties: [
@@ -625,6 +644,7 @@ const blocks: PageBlockDefinition[] = [
625
644
  name: "Callout",
626
645
  description: "Highlight a section of text",
627
646
  category: "Text",
647
+ icon: "https://cdn-assets.supernova.io/blocks/icons/callout.svg",
628
648
  searchKeywords: ["banner", "alert", "note", "tip", "warning"],
629
649
  item: {
630
650
  properties: [
@@ -687,6 +707,7 @@ const blocks: PageBlockDefinition[] = [
687
707
  name: "Image",
688
708
  description: "Display an image or Figma frame",
689
709
  category: "Media",
710
+ icon: "https://cdn-assets.supernova.io/blocks/icons/image.svg",
690
711
  searchKeywords: ["image", "figma", "frame", "picture", "photo"],
691
712
  item: {
692
713
  properties: [
@@ -749,6 +770,7 @@ const blocks: PageBlockDefinition[] = [
749
770
  name: "Shortcut links",
750
771
  description: "Link to a page or external URL",
751
772
  category: "Media",
773
+ icon: "https://cdn-assets.supernova.io/blocks/icons/shortcuts.svg",
752
774
  searchKeywords: [],
753
775
  item: {
754
776
  properties: [
@@ -765,6 +787,9 @@ const blocks: PageBlockDefinition[] = [
765
787
  width: "Icon",
766
788
  aspectRatio: "Square",
767
789
  },
790
+ imageOnLeft: {
791
+ width: "Medium",
792
+ },
768
793
  iconOnLeft: {
769
794
  width: "Icon",
770
795
  aspectRatio: "Square",
@@ -929,6 +954,7 @@ const blocks: PageBlockDefinition[] = [
929
954
  name: "Accessibility Color Grid",
930
955
  description: "Visualize accessibility of your color tokens.",
931
956
  category: "Tokens",
957
+ icon: "https://cdn-assets.supernova.io/blocks/icons/token-a11y-contrast-grid.svg",
932
958
  searchKeywords: ["color", "accessibility", "grid", "contrast", "blind", "impairment"],
933
959
  item: {
934
960
  properties: [
@@ -992,6 +1018,7 @@ const blocks: PageBlockDefinition[] = [
992
1018
  name: "Embed",
993
1019
  description: "Embed a generic URL",
994
1020
  category: "Media",
1021
+ icon: "https://cdn-assets.supernova.io/blocks/icons/embed.svg",
995
1022
  searchKeywords: ["embed", "url", "iframe", "site", "import"],
996
1023
  item: {
997
1024
  properties: [
@@ -1057,6 +1084,7 @@ const blocks: PageBlockDefinition[] = [
1057
1084
  name: "YouTube",
1058
1085
  description: "Embed a Youtube video",
1059
1086
  category: "Media",
1087
+ icon: "https://cdn-assets.supernova.io/blocks/icons/embed-youtube.svg",
1060
1088
  searchKeywords: ["embed", "video", "player", "upload"],
1061
1089
  item: {
1062
1090
  properties: [
@@ -1122,6 +1150,7 @@ const blocks: PageBlockDefinition[] = [
1122
1150
  name: "Lottie animation",
1123
1151
  description: "Preview a Lottie animation",
1124
1152
  category: "Media",
1153
+ icon: "https://cdn-assets.supernova.io/blocks/icons/lottie.svg",
1125
1154
  searchKeywords: ["embed", "lottie", "animation", "rive", "json"],
1126
1155
  item: {
1127
1156
  properties: [
@@ -1225,6 +1254,7 @@ const blocks: PageBlockDefinition[] = [
1225
1254
  name: "Storybook",
1226
1255
  description: "Embed Storybook canvas",
1227
1256
  category: "Media",
1257
+ icon: "https://cdn-assets.supernova.io/blocks/icons/storybook.svg",
1228
1258
  searchKeywords: ["storybook", "story", "stories", "example", "preview", "code", "react"],
1229
1259
  item: {
1230
1260
  properties: [
@@ -1288,6 +1318,7 @@ const blocks: PageBlockDefinition[] = [
1288
1318
  name: "Figma embed",
1289
1319
  description: "Embed a Figma canvas or prototype",
1290
1320
  category: "Media",
1321
+ icon: "https://cdn-assets.supernova.io/blocks/icons/embed-figma.svg",
1291
1322
  searchKeywords: ["embed", "figma", "design", "prototype", "canvas"],
1292
1323
  item: {
1293
1324
  properties: [
@@ -1352,6 +1383,7 @@ const blocks: PageBlockDefinition[] = [
1352
1383
  name: "Markdown",
1353
1384
  description: "Render Markdown URL",
1354
1385
  category: "Other",
1386
+ icon: "https://cdn-assets.supernova.io/blocks/icons/markdown.svg",
1355
1387
  searchKeywords: ["md", "embed", "api", "table", "mdx"],
1356
1388
  item: {
1357
1389
  properties: [
@@ -1453,6 +1485,7 @@ const blocks: PageBlockDefinition[] = [
1453
1485
  name: "Table",
1454
1486
  description: "Display a simple table",
1455
1487
  category: "Layout",
1488
+ icon: "https://cdn-assets.supernova.io/blocks/icons/table.svg",
1456
1489
  searchKeywords: ["grid", "data", "spreadsheet", "api"],
1457
1490
  item: {
1458
1491
  properties: [
@@ -1512,6 +1545,7 @@ const blocks: PageBlockDefinition[] = [
1512
1545
  name: "Token detail",
1513
1546
  description: "Show a single design tokens",
1514
1547
  category: "Tokens",
1548
+ icon: "https://cdn-assets.supernova.io/blocks/icons/token-detail.svg",
1515
1549
  searchKeywords: ["color", "typography", "spacing", "grid", "material", "theme"],
1516
1550
  item: {
1517
1551
  properties: [
@@ -1615,6 +1649,7 @@ const blocks: PageBlockDefinition[] = [
1615
1649
  name: "Token list",
1616
1650
  description: "Show a list of design tokens",
1617
1651
  category: "Tokens",
1652
+ icon: "https://cdn-assets.supernova.io/blocks/icons/token-list.svg",
1618
1653
  searchKeywords: ["color", "typography", "spacing", "grid", "material", "theme"],
1619
1654
  item: {
1620
1655
  properties: [
@@ -1724,6 +1759,7 @@ const blocks: PageBlockDefinition[] = [
1724
1759
  name: "Token group",
1725
1760
  description: "Show a group of design tokens",
1726
1761
  category: "Tokens",
1762
+ icon: "https://cdn-assets.supernova.io/blocks/icons/token-list.svg",
1727
1763
  searchKeywords: ["color", "typography", "spacing", "grid", "material", "theme"],
1728
1764
  item: {
1729
1765
  properties: [
@@ -1832,6 +1868,7 @@ const blocks: PageBlockDefinition[] = [
1832
1868
  name: "Code",
1833
1869
  description: "Code description",
1834
1870
  category: "Code",
1871
+ icon: "https://cdn-assets.supernova.io/blocks/icons/code.svg",
1835
1872
  searchKeywords: ["code"],
1836
1873
  item: {
1837
1874
  properties: [
@@ -1890,6 +1927,7 @@ const blocks: PageBlockDefinition[] = [
1890
1927
  name: "React code",
1891
1928
  description: "Render React code",
1892
1929
  category: "Code",
1930
+ icon: "https://cdn-assets.supernova.io/blocks/icons/code-react.svg",
1893
1931
  searchKeywords: ["code", "react", "snippet", "storybook", "editor", "example"],
1894
1932
  item: {
1895
1933
  properties: [
@@ -2025,6 +2063,7 @@ const blocks: PageBlockDefinition[] = [
2025
2063
  name: "Link",
2026
2064
  description: "Preview of a link",
2027
2065
  category: "Media",
2066
+ icon: "https://cdn-assets.supernova.io/blocks/icons/shortcut-link.svg",
2028
2067
  searchKeywords: [],
2029
2068
  item: {
2030
2069
  properties: [
@@ -2082,6 +2121,7 @@ const blocks: PageBlockDefinition[] = [
2082
2121
  name: "Assets",
2083
2122
  description: "Display icons or illustrations",
2084
2123
  category: "Assets",
2124
+ icon: "https://cdn-assets.supernova.io/blocks/icons/assets.svg",
2085
2125
  searchKeywords: ["icons", "illustrations", "grid", "svg", "logos", "theme"],
2086
2126
  item: {
2087
2127
  properties: [
@@ -2184,6 +2224,7 @@ const blocks: PageBlockDefinition[] = [
2184
2224
  name: "Figma frames",
2185
2225
  description: "Display Figma frames as images",
2186
2226
  category: "Figma",
2227
+ icon: "https://cdn-assets.supernova.io/blocks/icons/figma-frames.svg",
2187
2228
  searchKeywords: ["figma", "frames", "image"],
2188
2229
  item: {
2189
2230
  properties: [
@@ -2266,6 +2307,7 @@ const blocks: PageBlockDefinition[] = [
2266
2307
  name: "Release notes",
2267
2308
  description: "Show version release notes",
2268
2309
  category: "Other",
2310
+ icon: "https://cdn-assets.supernova.io/blocks/icons/release-notes.svg",
2269
2311
  searchKeywords: ["version", "changelog", "history"],
2270
2312
  item: {
2271
2313
  properties: [],
@@ -2318,6 +2360,7 @@ const blocks: PageBlockDefinition[] = [
2318
2360
  name: "Component checklist",
2319
2361
  description: "Highlight specific features of your components",
2320
2362
  category: "Components",
2363
+ icon: "https://cdn-assets.supernova.io/blocks/icons/component-checklist.svg",
2321
2364
  searchKeywords: ["components", "health", "properties", "overview", "status"],
2322
2365
  item: {
2323
2366
  properties: [
@@ -2397,6 +2440,7 @@ const blocks: PageBlockDefinition[] = [
2397
2440
  name: "Component overview table",
2398
2441
  description: "Show the overview of all your components",
2399
2442
  category: "Components",
2443
+ icon: "https://cdn-assets.supernova.io/blocks/icons/component-overview-table.svg",
2400
2444
  searchKeywords: ["components", "health", "properties", "overview", "status"],
2401
2445
  item: {
2402
2446
  properties: [
@@ -2468,6 +2512,7 @@ const blocks: PageBlockDefinition[] = [
2468
2512
  name: "Component health",
2469
2513
  description: "Show component health and additional attributes",
2470
2514
  category: "Components",
2515
+ icon: "https://cdn-assets.supernova.io/blocks/icons/component-health.svg",
2471
2516
  searchKeywords: ["components", "health", "properties", "overview", "status"],
2472
2517
  item: {
2473
2518
  properties: [
@@ -472,56 +472,34 @@ const newSchema = {
472
472
  },
473
473
  },
474
474
  image: {
475
- name: "image",
476
- spec: {
477
- group: "block",
478
- inline: false,
479
- draggable: true,
480
- attrs: {
481
- src: {
482
- default: null,
483
- },
484
- alt: {
485
- default: null,
486
- },
487
- title: {
488
- default: null,
489
- },
490
- },
491
- parseDOM: [
492
- {
493
- tag: 'img[src]:not([src^="data:"])',
494
- },
495
- ],
496
- },
497
- markSet: [],
498
- groups: ["block"],
475
+ group: "block",
476
+ atom: true,
477
+ draggable: true,
499
478
  attrs: {
500
- src: {
501
- hasDefault: true,
479
+ id: {
502
480
  default: null,
503
481
  },
504
- alt: {
505
- hasDefault: true,
482
+ definitionId: {
506
483
  default: null,
507
484
  },
508
- title: {
509
- hasDefault: true,
485
+ variantId: {
510
486
  default: null,
511
487
  },
488
+ appearance: {
489
+ default: "{}",
490
+ },
491
+ columns: {
492
+ default: 1,
493
+ },
494
+ items: {
495
+ default: "[]",
496
+ },
512
497
  },
513
- defaultAttrs: {
514
- src: null,
515
- alt: null,
516
- title: null,
517
- },
518
- contentMatch: {
519
- validEnd: true,
520
- wrapCache: [],
521
- },
522
- inlineContent: false,
523
- isBlock: true,
524
- isText: false,
498
+ parseDOM: [
499
+ {
500
+ tag: "block-node",
501
+ },
502
+ ],
525
503
  },
526
504
  },
527
505
  marks: {
@@ -177,13 +177,18 @@ function internalProsemirrorNodeToBlock(
177
177
  prosemirrorNode: ProsemirrorNode,
178
178
  definitionsMap: Map<string, PageBlockDefinition>
179
179
  ) {
180
- const definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
180
+ let definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
181
181
  if (!definitionId) {
182
182
  console.warn(`definitionId on ${prosemirrorNode.type} is required to be interpreted as a block, skipping node`);
183
183
 
184
184
  return null;
185
185
  }
186
186
 
187
+ // TODO: remove
188
+ if (definitionId === "io.supernova.block.token-detail") {
189
+ definitionId = "io.supernova.block.token-list";
190
+ }
191
+
187
192
  const definition = definitionsMap.get(definitionId);
188
193
  if (!definition) {
189
194
  console.warn(
@@ -363,20 +368,34 @@ function parseRichTextAttribute(mark: ProsemirrorMark): PageBlockTextSpanAttribu
363
368
  case "code":
364
369
  return { type: "Code" };
365
370
  case "link":
366
- const itemId = getProsemirrorAttribute(mark, "itemId", z.string().optional());
367
- const href = getProsemirrorAttribute(mark, "href", z.string().optional());
368
-
369
- return {
370
- type: "Link",
371
- openInNewWindow: mark.attrs?.target !== "_self",
372
- documentationItemId: itemId,
373
- link: href,
374
- };
371
+ return parseProsemirrorLink(mark);
375
372
  }
376
373
 
377
374
  return null;
378
375
  }
379
376
 
377
+ function parseProsemirrorLink(mark: ProsemirrorMark): PageBlockTextSpanAttribute | null {
378
+ const href = getProsemirrorAttribute(mark, "href", z.string().optional());
379
+ if (!href) return null;
380
+
381
+ const target = getProsemirrorAttribute(mark, "target", z.string().optional());
382
+ const openInNewWindow = target === "_blank";
383
+
384
+ if (href.startsWith("@")) {
385
+ return {
386
+ type: "Link",
387
+ openInNewWindow: openInNewWindow,
388
+ documentationItemId: href.split(":")[1],
389
+ };
390
+ } else {
391
+ return {
392
+ type: "Link",
393
+ openInNewWindow: openInNewWindow,
394
+ link: href,
395
+ };
396
+ }
397
+ }
398
+
380
399
  //
381
400
  // Embed
382
401
  //