notion-to-jsx 1.2.16 → 1.3.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/dist/index.js CHANGED
@@ -35,7 +35,13 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/components/Renderer/index.tsx
38
- var import_react7 = require("react");
38
+ var import_react16 = require("react");
39
+
40
+ // src/components/Renderer/components/List/ListItemGroup.tsx
41
+ var import_react5 = require("react");
42
+
43
+ // src/components/Renderer/components/List/List.tsx
44
+ var import_react = require("react");
39
45
 
40
46
  // src/components/Renderer/components/List/styles.css.ts
41
47
  var import_createRuntimeFn = require("@vanilla-extract/recipes/createRuntimeFn");
@@ -44,25 +50,25 @@ var listItem = "styles_listItem__es8vim3";
44
50
 
45
51
  // src/components/Renderer/components/List/List.tsx
46
52
  var import_jsx_runtime = require("react/jsx-runtime");
47
- var List = ({
53
+ var List = (0, import_react.memo)(({
48
54
  as: Component = "ul",
49
55
  type,
50
- className,
51
56
  children,
52
57
  ...props
53
58
  }) => {
54
59
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { className: list({ type }), ...props, children });
55
- };
56
- var ListItem = ({
57
- className,
60
+ });
61
+ List.displayName = "List";
62
+ var ListItem = (0, import_react.memo)(({
58
63
  children,
59
64
  ...props
60
65
  }) => {
61
66
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: listItem, ...props, children });
62
- };
67
+ });
68
+ ListItem.displayName = "ListItem";
63
69
 
64
70
  // src/components/Renderer/components/MemoizedComponents.tsx
65
- var import_react3 = require("react");
71
+ var import_react4 = require("react");
66
72
 
67
73
  // src/components/Renderer/components/RichText/styles.css.ts
68
74
  var import_createRuntimeFn2 = require("@vanilla-extract/recipes/createRuntimeFn");
@@ -72,6 +78,30 @@ var richText = (0, import_createRuntimeFn2.createRuntimeFn)({ defaultClassName:
72
78
 
73
79
  // src/components/Renderer/components/RichText/RichTexts.tsx
74
80
  var import_jsx_runtime2 = require("react/jsx-runtime");
81
+ var NOTION_COLORS = [
82
+ "default",
83
+ "gray",
84
+ "brown",
85
+ "orange",
86
+ "yellow",
87
+ "green",
88
+ "blue",
89
+ "purple",
90
+ "pink",
91
+ "red",
92
+ "gray_background",
93
+ "brown_background",
94
+ "orange_background",
95
+ "yellow_background",
96
+ "green_background",
97
+ "blue_background",
98
+ "purple_background",
99
+ "pink_background",
100
+ "red_background"
101
+ ];
102
+ var isNotionColor = (color) => {
103
+ return NOTION_COLORS.includes(color);
104
+ };
75
105
  var renderLink = (href, content3) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: link, children: content3 });
76
106
  var EmptyRichText = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: emptyRichText });
77
107
  var RichTexts = ({ richTexts }) => {
@@ -99,28 +129,7 @@ var RichTexts = ({ richTexts }) => {
99
129
  content3 = text.plain_text;
100
130
  }
101
131
  }
102
- const notionColors = [
103
- "default",
104
- "gray",
105
- "brown",
106
- "orange",
107
- "yellow",
108
- "green",
109
- "blue",
110
- "purple",
111
- "pink",
112
- "red",
113
- "gray_background",
114
- "brown_background",
115
- "orange_background",
116
- "yellow_background",
117
- "green_background",
118
- "blue_background",
119
- "purple_background",
120
- "pink_background",
121
- "red_background"
122
- ];
123
- const safeColor = notionColors.includes(color) ? color : void 0;
132
+ const safeColor = isNotionColor(color) ? color : void 0;
124
133
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
125
134
  "span",
126
135
  {
@@ -140,9 +149,6 @@ var RichTexts = ({ richTexts }) => {
140
149
  };
141
150
  var RichTexts_default = RichTexts;
142
151
 
143
- // src/components/Renderer/components/Image/Image.tsx
144
- var import_react = require("react");
145
-
146
152
  // src/components/Renderer/components/Image/styles.css.ts
147
153
  var import_createRuntimeFn3 = require("@vanilla-extract/recipes/createRuntimeFn");
148
154
  var caption = "styles_caption__2f3ncf7";
@@ -193,6 +199,26 @@ var Skeleton = ({
193
199
  };
194
200
  var Skeleton_default = Skeleton;
195
201
 
202
+ // src/hooks/useImageLoad.ts
203
+ var import_react2 = require("react");
204
+ var useImageLoad = (src) => {
205
+ const [isLoaded, setIsLoaded] = (0, import_react2.useState)(false);
206
+ const imgRef = (0, import_react2.useRef)(null);
207
+ (0, import_react2.useEffect)(() => {
208
+ const img = imgRef.current;
209
+ if (img && img.complete && img.naturalHeight !== 0) {
210
+ setIsLoaded(true);
211
+ }
212
+ }, [src]);
213
+ (0, import_react2.useEffect)(() => {
214
+ setIsLoaded(false);
215
+ }, [src]);
216
+ const handleLoad = (0, import_react2.useCallback)(() => {
217
+ setIsLoaded(true);
218
+ }, []);
219
+ return { isLoaded, imgRef, handleLoad };
220
+ };
221
+
196
222
  // src/components/Renderer/components/Image/Image.tsx
197
223
  var import_jsx_runtime4 = require("react/jsx-runtime");
198
224
  var MAX_WIDTH = 720;
@@ -206,17 +232,7 @@ var Image = ({
206
232
  format,
207
233
  isColumn = false
208
234
  }) => {
209
- const [isLoaded, setIsLoaded] = (0, import_react.useState)(false);
210
- const imgRef = (0, import_react.useRef)(null);
211
- (0, import_react.useEffect)(() => {
212
- const img = imgRef.current;
213
- if (img && img.complete && img.naturalHeight !== 0) {
214
- setIsLoaded(true);
215
- }
216
- }, [src]);
217
- const handleLoad = () => {
218
- setIsLoaded(true);
219
- };
235
+ const { isLoaded, imgRef, handleLoad } = useImageLoad(src);
220
236
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: imageContainer, children: [
221
237
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: imageWrapper, style: getImageStyles(format, isColumn), children: [
222
238
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: skeletonWrapper({ isLoaded }), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skeleton_default, { variant: "image", isLoading: !isLoaded }) }),
@@ -305,7 +321,7 @@ var Bookmark = ({ url, metadata }) => {
305
321
  var Bookmark_default = Bookmark;
306
322
 
307
323
  // src/components/Renderer/components/LinkPreview/LinkPreview.tsx
308
- var import_react2 = require("react");
324
+ var import_react3 = require("react");
309
325
 
310
326
  // src/components/Renderer/components/LinkPreview/styles.css.ts
311
327
  var content2 = "styles_content__o1p3m12";
@@ -320,16 +336,19 @@ var title2 = "styles_title__o1p3m15";
320
336
 
321
337
  // src/components/Renderer/components/LinkPreview/LinkPreview.tsx
322
338
  var import_jsx_runtime6 = require("react/jsx-runtime");
323
- var fetchGitHubRepoData = async (repoPath) => {
339
+ var fetchGitHubRepoData = async (repoPath, signal) => {
324
340
  try {
325
341
  const apiUrl = `https://api.github.com/repos/${repoPath}`;
326
- const response = await fetch(apiUrl);
342
+ const response = await fetch(apiUrl, { signal });
327
343
  if (!response.ok) {
328
344
  throw new Error("Failed to fetch GitHub repo data");
329
345
  }
330
346
  const data = await response.json();
331
347
  return data;
332
348
  } catch (error) {
349
+ if (error instanceof Error && error.name === "AbortError") {
350
+ return null;
351
+ }
333
352
  console.error("Error fetching GitHub repo data:", error);
334
353
  return null;
335
354
  }
@@ -343,10 +362,7 @@ var extractFigmaData = (url) => {
343
362
  (segment) => segment.includes("file")
344
363
  );
345
364
  if (!fileSegment) return null;
346
- const fileIdMatch = fileSegment.match(/file\/([^/]+)/);
347
- const fileId = fileIdMatch ? fileIdMatch[1] : "";
348
365
  let fileName = "";
349
- let mode = "";
350
366
  if (pathSegments.length > 3) {
351
367
  const encodedName = pathSegments[3];
352
368
  if (encodedName) {
@@ -355,14 +371,10 @@ var extractFigmaData = (url) => {
355
371
  }
356
372
  if (!fileName && parsedUrl.pathname.includes("-")) {
357
373
  const nameMatch = parsedUrl.pathname.match(/\/([^/]+)(?:\?|$)/);
358
- if (nameMatch && nameMatch[1]) {
374
+ if (nameMatch?.[1]) {
359
375
  fileName = decodeURIComponent(nameMatch[1].replace(/-/g, " "));
360
376
  }
361
377
  }
362
- if (parsedUrl.search) {
363
- const searchParams = new URLSearchParams(parsedUrl.search);
364
- mode = searchParams.get("mode") || "";
365
- }
366
378
  fileName = fileName || "Figma Design";
367
379
  return {
368
380
  name: fileName,
@@ -405,6 +417,20 @@ var getLinkType = (url) => {
405
417
  return "unknown";
406
418
  }
407
419
  };
420
+ var MONTH_NAMES = [
421
+ "Jan",
422
+ "Feb",
423
+ "Mar",
424
+ "Apr",
425
+ "May",
426
+ "Jun",
427
+ "Jul",
428
+ "Aug",
429
+ "Sep",
430
+ "Oct",
431
+ "Nov",
432
+ "Dec"
433
+ ];
408
434
  var formatUpdatedTime = (dateString) => {
409
435
  const date = new Date(dateString);
410
436
  const now = /* @__PURE__ */ new Date();
@@ -413,39 +439,57 @@ var formatUpdatedTime = (dateString) => {
413
439
  );
414
440
  if (diffInHours < 24) {
415
441
  return `Updated ${diffInHours} hours ago`;
416
- } else {
417
- const diffInDays = Math.floor(diffInHours / 24);
418
- if (diffInDays === 1) {
419
- return "Updated yesterday";
420
- } else if (diffInDays < 30) {
421
- return `Updated ${diffInDays} days ago`;
422
- } else {
423
- const months = [
424
- "Jan",
425
- "Feb",
426
- "Mar",
427
- "Apr",
428
- "May",
429
- "Jun",
430
- "Jul",
431
- "Aug",
432
- "Sep",
433
- "Oct",
434
- "Nov",
435
- "Dec"
436
- ];
437
- return `Updated on ${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
438
- }
439
442
  }
443
+ const diffInDays = Math.floor(diffInHours / 24);
444
+ if (diffInDays === 1) {
445
+ return "Updated yesterday";
446
+ }
447
+ if (diffInDays < 30) {
448
+ return `Updated ${diffInDays} days ago`;
449
+ }
450
+ return `Updated on ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
440
451
  };
452
+ var FigmaPreview = (0, import_react3.memo)(({ data }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: preview, children: [
453
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
454
+ "img",
455
+ {
456
+ src: data.thumbnailUrl || "https://static.figma.com/app/icon/1/favicon.svg",
457
+ alt: "Figma icon",
458
+ className: icon
459
+ }
460
+ ) }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: content2, children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: data.name }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: description2, children: "www.figma.com" })
464
+ ] })
465
+ ] }));
466
+ FigmaPreview.displayName = "FigmaPreview";
467
+ var GitHubPreview = (0, import_react3.memo)(({ repoData, repoName, updatedTimeText, loading }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `${preview} ${githubPreview}`, children: [
468
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
469
+ "img",
470
+ {
471
+ src: repoData?.owner?.avatar_url || "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
472
+ alt: "Repository icon",
473
+ className: icon
474
+ }
475
+ ) }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `${content2} ${githubContent}`, children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: repoName }),
478
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: description2, children: loading ? "Loading..." : `${repoName} \u2022 ${updatedTimeText}` })
479
+ ] })
480
+ ] }));
481
+ GitHubPreview.displayName = "GitHubPreview";
482
+ var DefaultPreview = (0, import_react3.memo)(({ url }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: preview, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: content2, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: url }) }) }));
483
+ DefaultPreview.displayName = "DefaultPreview";
441
484
  var LinkPreview = ({ url }) => {
442
- const [repoData, setRepoData] = (0, import_react2.useState)(null);
443
- const [figmaData, setFigmaData] = (0, import_react2.useState)(null);
444
- const [loading, setLoading] = (0, import_react2.useState)(true);
445
- const [linkType, setLinkType] = (0, import_react2.useState)(
485
+ const [repoData, setRepoData] = (0, import_react3.useState)(null);
486
+ const [figmaData, setFigmaData] = (0, import_react3.useState)(null);
487
+ const [loading, setLoading] = (0, import_react3.useState)(true);
488
+ const [linkType, setLinkType] = (0, import_react3.useState)(
446
489
  "unknown"
447
490
  );
448
- (0, import_react2.useEffect)(() => {
491
+ (0, import_react3.useEffect)(() => {
492
+ const abortController = new AbortController();
449
493
  const loadLinkData = async () => {
450
494
  setLoading(true);
451
495
  const type = getLinkType(url);
@@ -453,19 +497,45 @@ var LinkPreview = ({ url }) => {
453
497
  if (type === "github") {
454
498
  const repoPath = extractRepoPathFromUrl(url);
455
499
  if (repoPath) {
456
- const data = await fetchGitHubRepoData(repoPath);
457
- setRepoData(data);
500
+ const data = await fetchGitHubRepoData(repoPath, abortController.signal);
501
+ if (!abortController.signal.aborted) {
502
+ setRepoData(data);
503
+ }
458
504
  }
459
505
  } else if (type === "figma") {
460
506
  const data = extractFigmaData(url);
461
- setFigmaData(data);
507
+ if (!abortController.signal.aborted) {
508
+ setFigmaData(data);
509
+ }
510
+ }
511
+ if (!abortController.signal.aborted) {
512
+ setLoading(false);
462
513
  }
463
- setLoading(false);
464
514
  };
465
515
  loadLinkData();
516
+ return () => {
517
+ abortController.abort();
518
+ };
466
519
  }, [url]);
467
520
  const repoName = repoData?.name || extractRepoPathFromUrl(url)?.split("/")[1] || "Repository";
468
521
  const updatedTimeText = repoData?.updated_at ? formatUpdatedTime(repoData.updated_at) : "";
522
+ const renderPreview = () => {
523
+ if (linkType === "figma" && figmaData) {
524
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FigmaPreview, { data: figmaData });
525
+ }
526
+ if (linkType === "github") {
527
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
528
+ GitHubPreview,
529
+ {
530
+ repoData,
531
+ repoName,
532
+ updatedTimeText,
533
+ loading
534
+ }
535
+ );
536
+ }
537
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DefaultPreview, { url });
538
+ };
469
539
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
470
540
  "a",
471
541
  {
@@ -473,58 +543,31 @@ var LinkPreview = ({ url }) => {
473
543
  target: "_blank",
474
544
  rel: "noopener noreferrer",
475
545
  className: link3,
476
- children: linkType === "figma" && figmaData ? (
477
- // Figma 프리뷰 렌더링
478
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: preview, children: [
479
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
480
- "img",
481
- {
482
- src: figmaData.thumbnailUrl || "https://static.figma.com/app/icon/1/favicon.svg",
483
- alt: "Figma icon",
484
- className: icon
485
- }
486
- ) }),
487
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: content2, children: [
488
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: figmaData.name }),
489
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: description2, children: "www.figma.com" })
490
- ] })
491
- ] })
492
- ) : linkType === "github" ? (
493
- // GitHub 프리뷰 렌더링
494
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `${preview} ${githubPreview}`, children: [
495
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
496
- "img",
497
- {
498
- src: repoData?.owner?.avatar_url || "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
499
- alt: "Repository icon",
500
- className: icon
501
- }
502
- ) }),
503
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `${content2} ${githubContent}`, children: [
504
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: repoName }),
505
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: description2, children: loading ? "Loading..." : `${repoName} \u2022 ${updatedTimeText}` })
506
- ] })
507
- ] })
508
- ) : (
509
- // 기본 링크 프리뷰 렌더링
510
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: preview, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: content2, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: url }) }) })
511
- )
546
+ children: renderPreview()
512
547
  }
513
548
  );
514
549
  };
515
550
  var LinkPreview_default = LinkPreview;
516
551
 
517
552
  // src/components/Renderer/components/MemoizedComponents.tsx
518
- var MemoizedRichText = (0, import_react3.memo)(RichTexts_default, (prev, next) => {
519
- return JSON.stringify(prev.richTexts) === JSON.stringify(next.richTexts);
553
+ var areRichTextsEqual = (prev, next) => {
554
+ if (prev === next) return true;
555
+ if (!prev || !next) return prev === next;
556
+ if (prev.length !== next.length) return false;
557
+ return prev.every(
558
+ (item, i) => item.plain_text === next[i]?.plain_text && item.href === next[i]?.href && item.annotations?.bold === next[i]?.annotations?.bold && item.annotations?.italic === next[i]?.annotations?.italic && item.annotations?.code === next[i]?.annotations?.code && item.annotations?.color === next[i]?.annotations?.color
559
+ );
560
+ };
561
+ var MemoizedRichText = (0, import_react4.memo)(RichTexts_default, (prev, next) => {
562
+ return areRichTextsEqual(prev.richTexts, next.richTexts);
520
563
  });
521
- var MemoizedImage = (0, import_react3.memo)(Image_default, (prev, next) => {
522
- return prev.src === next.src && prev.alt === next.alt && JSON.stringify(prev.caption) === JSON.stringify(next.caption);
564
+ var MemoizedImage = (0, import_react4.memo)(Image_default, (prev, next) => {
565
+ return prev.src === next.src && prev.alt === next.alt && areRichTextsEqual(prev.caption, next.caption);
523
566
  });
524
- var MemoizedBookmark = (0, import_react3.memo)(Bookmark_default, (prev, next) => {
567
+ var MemoizedBookmark = (0, import_react4.memo)(Bookmark_default, (prev, next) => {
525
568
  return prev.url === next.url;
526
569
  });
527
- var MemoizedLinkPreview = (0, import_react3.memo)(
570
+ var MemoizedLinkPreview = (0, import_react4.memo)(
528
571
  LinkPreview_default,
529
572
  (prev, next) => {
530
573
  return prev.url === next.url;
@@ -533,7 +576,7 @@ var MemoizedLinkPreview = (0, import_react3.memo)(
533
576
 
534
577
  // src/components/Renderer/components/List/ListItemGroup.tsx
535
578
  var import_jsx_runtime7 = require("react/jsx-runtime");
536
- var RecursiveListItem = ({ block, renderBlock }) => {
579
+ var RecursiveListItem = (0, import_react5.memo)(({ block, renderBlock }) => {
537
580
  let content3;
538
581
  if (block.type === "bulleted_list_item") {
539
582
  content3 = block.bulleted_list_item;
@@ -593,8 +636,9 @@ var RecursiveListItem = ({ block, renderBlock }) => {
593
636
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MemoizedRichText, { richTexts }),
594
637
  renderedChildren.length > 0 && renderedChildren
595
638
  ] });
596
- };
597
- var ListGroup = ({ blocks, type, renderBlock }) => {
639
+ });
640
+ RecursiveListItem.displayName = "RecursiveListItem";
641
+ var ListGroup = (0, import_react5.memo)(({ blocks, type, renderBlock }) => {
598
642
  if (blocks.length === 0) return null;
599
643
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
600
644
  List,
@@ -613,10 +657,14 @@ var ListGroup = ({ blocks, type, renderBlock }) => {
613
657
  ))
614
658
  }
615
659
  );
616
- };
660
+ });
661
+ ListGroup.displayName = "ListGroup";
662
+
663
+ // src/components/Renderer/components/Block/BlockRenderer.tsx
664
+ var import_react15 = require("react");
617
665
 
618
666
  // src/components/Renderer/components/Code/CodeBlock.tsx
619
- var import_react4 = require("react");
667
+ var import_react6 = require("react");
620
668
 
621
669
  // src/components/Renderer/components/Code/styles.css.ts
622
670
  var codeBlock = "styles_codeBlock__1qn42yc0";
@@ -645,8 +693,8 @@ var renderToken = (token, i) => {
645
693
  }
646
694
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `token ${token.type}`, children: tokenContent }, i);
647
695
  };
648
- var CodeBlock = ({ code, language, caption: caption2 }) => {
649
- const tokens = (0, import_react4.useMemo)(() => {
696
+ var CodeBlock = (0, import_react6.memo)(({ code, language, caption: caption2 }) => {
697
+ const tokens = (0, import_react6.useMemo)(() => {
650
698
  const prismLanguage = import_prismjs.default.languages[language] || import_prismjs.default.languages.plaintext;
651
699
  return import_prismjs.default.tokenize(code, prismLanguage);
652
700
  }, [code, language]);
@@ -654,9 +702,13 @@ var CodeBlock = ({ code, language, caption: caption2 }) => {
654
702
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { className: `${codeBlock} language-${language}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { className: `language-${language}`, children: tokens.map((token, i) => renderToken(token, i)) }) }),
655
703
  caption2 && caption2.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MemoizedRichText, { richTexts: caption2 }) })
656
704
  ] });
657
- };
705
+ });
706
+ CodeBlock.displayName = "CodeBlock";
658
707
  var CodeBlock_default = CodeBlock;
659
708
 
709
+ // src/components/Renderer/components/Typography/Typography.tsx
710
+ var import_react7 = require("react");
711
+
660
712
  // src/components/Renderer/components/Typography/styles.css.ts
661
713
  var heading1 = "styles_heading1__90a95g1";
662
714
  var heading2 = "styles_heading2__90a95g2";
@@ -665,34 +717,25 @@ var paragraph = "styles_paragraph__90a95g0";
665
717
 
666
718
  // src/components/Renderer/components/Typography/Typography.tsx
667
719
  var import_jsx_runtime9 = require("react/jsx-runtime");
668
- var Paragraph = ({
669
- className,
670
- children,
671
- ...props
672
- }) => {
720
+ var Paragraph = (0, import_react7.memo)(({ children, ...props }) => {
673
721
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: paragraph, ...props, children });
674
- };
675
- var Heading1 = ({
676
- className,
677
- children,
678
- ...props
679
- }) => {
722
+ });
723
+ Paragraph.displayName = "Paragraph";
724
+ var Heading1 = (0, import_react7.memo)(({ children, ...props }) => {
680
725
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: heading1, ...props, children });
681
- };
682
- var Heading2 = ({
683
- className,
684
- children,
685
- ...props
686
- }) => {
726
+ });
727
+ Heading1.displayName = "Heading1";
728
+ var Heading2 = (0, import_react7.memo)(({ children, ...props }) => {
687
729
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: heading2, ...props, children });
688
- };
689
- var Heading3 = ({
690
- className,
691
- children,
692
- ...props
693
- }) => {
730
+ });
731
+ Heading2.displayName = "Heading2";
732
+ var Heading3 = (0, import_react7.memo)(({ children, ...props }) => {
694
733
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: heading3, ...props, children });
695
- };
734
+ });
735
+ Heading3.displayName = "Heading3";
736
+
737
+ // src/components/Renderer/components/Column/Column.tsx
738
+ var import_react8 = require("react");
696
739
 
697
740
  // src/components/Renderer/components/Column/styles.css.ts
698
741
  var columnContainer = "styles_columnContainer__wle6we0";
@@ -700,30 +743,40 @@ var columnListContainer = "styles_columnListContainer__wle6we1";
700
743
 
701
744
  // src/components/Renderer/components/Column/Column.tsx
702
745
  var import_jsx_runtime10 = require("react/jsx-runtime");
703
- var Column = ({ block }) => {
746
+ var Column = (0, import_react8.memo)(({ block }) => {
704
747
  if (!block || !block.children) return null;
705
748
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: columnContainer, children: block.children.map((childBlock) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BlockRenderer_default, { block: childBlock, isColumn: true }, childBlock.id)) });
706
- };
749
+ });
750
+ Column.displayName = "Column";
707
751
  var Column_default = Column;
708
752
 
709
753
  // src/components/Renderer/components/Column/ColumnList.tsx
754
+ var import_react9 = require("react");
710
755
  var import_jsx_runtime11 = require("react/jsx-runtime");
711
- var ColumnList = ({ block }) => {
756
+ var ColumnList = (0, import_react9.memo)(({ block }) => {
712
757
  if (!block || !block.children) return null;
713
758
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: columnListContainer, children: block.children.map((column) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Column_default, { block: column }, column.id)) });
714
- };
759
+ });
760
+ ColumnList.displayName = "ColumnList";
715
761
  var ColumnList_default = ColumnList;
716
762
 
763
+ // src/components/Renderer/components/Quote/Quote.tsx
764
+ var import_react10 = require("react");
765
+
717
766
  // src/components/Renderer/components/Quote/styles.css.ts
718
767
  var container = "styles_container__mra9n0";
719
768
 
720
769
  // src/components/Renderer/components/Quote/Quote.tsx
721
770
  var import_jsx_runtime12 = require("react/jsx-runtime");
722
- var Quote = ({ richTexts, tabIndex }) => {
771
+ var Quote = (0, import_react10.memo)(({ richTexts, tabIndex }) => {
723
772
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("blockquote", { className: container, tabIndex, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MemoizedRichText, { richTexts }) });
724
- };
773
+ });
774
+ Quote.displayName = "Quote";
725
775
  var Quote_default = Quote;
726
776
 
777
+ // src/components/Renderer/components/Table/Table.tsx
778
+ var import_react12 = require("react");
779
+
727
780
  // src/components/Renderer/components/Table/styles.css.ts
728
781
  var firstCell = "styles_firstCell__1rvbzfo4";
729
782
  var hasRowHeader = "styles_hasRowHeader__1rvbzfo6";
@@ -734,8 +787,9 @@ var tableCell = "styles_tableCell__1rvbzfo3";
734
787
  var tableContainer = "styles_tableContainer__1rvbzfo0";
735
788
 
736
789
  // src/components/Renderer/components/Table/TableRow.tsx
790
+ var import_react11 = require("react");
737
791
  var import_jsx_runtime13 = require("react/jsx-runtime");
738
- var TableRow = ({
792
+ var TableRow = (0, import_react11.memo)(({
739
793
  rowBlock,
740
794
  cellClassName = "",
741
795
  rowHeaderIndex = -1
@@ -761,12 +815,13 @@ var TableRow = ({
761
815
  `${rowBlock.id}-cell-${index}`
762
816
  );
763
817
  }) });
764
- };
818
+ });
819
+ TableRow.displayName = "TableRow";
765
820
  var TableRow_default = TableRow;
766
821
 
767
822
  // src/components/Renderer/components/Table/Table.tsx
768
823
  var import_jsx_runtime14 = require("react/jsx-runtime");
769
- var Table = ({ block }) => {
824
+ var Table = (0, import_react12.memo)(({ block }) => {
770
825
  if (!block.table || !block.children) {
771
826
  return null;
772
827
  }
@@ -788,14 +843,15 @@ var Table = ({ block }) => {
788
843
  );
789
844
  }) })
790
845
  ] }) }) });
791
- };
846
+ });
847
+ Table.displayName = "Table";
792
848
  var Table_default = Table;
793
849
 
794
850
  // src/components/Renderer/components/Table/index.ts
795
851
  var Table_default2 = Table_default;
796
852
 
797
853
  // src/components/Renderer/components/Toggle/Toggle.tsx
798
- var import_react5 = require("react");
854
+ var import_react13 = require("react");
799
855
 
800
856
  // src/components/Renderer/components/Toggle/styles.css.ts
801
857
  var toggleContainer = "styles_toggleContainer__p7ue1z0";
@@ -807,7 +863,7 @@ var toggleIconOpen = "styles_toggleIconOpen__p7ue1z3";
807
863
  // src/components/Renderer/components/Toggle/Toggle.tsx
808
864
  var import_jsx_runtime15 = require("react/jsx-runtime");
809
865
  var Toggle = ({ block }) => {
810
- const [isOpen, setIsOpen] = (0, import_react5.useState)(false);
866
+ const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
811
867
  if (!block.toggle || !block.children) {
812
868
  return null;
813
869
  }
@@ -840,6 +896,9 @@ var Toggle = ({ block }) => {
840
896
  };
841
897
  var Toggle_default = Toggle;
842
898
 
899
+ // src/components/Renderer/components/Video/Video.tsx
900
+ var import_react14 = require("react");
901
+
843
902
  // src/components/Renderer/components/Video/styles.css.ts
844
903
  var videoCaption = "styles_videoCaption__15b9vkb2";
845
904
  var videoContainer = "styles_videoContainer__15b9vkb0";
@@ -847,7 +906,7 @@ var videoPlayer = "styles_videoPlayer__15b9vkb1";
847
906
 
848
907
  // src/components/Renderer/components/Video/Video.tsx
849
908
  var import_jsx_runtime16 = require("react/jsx-runtime");
850
- var Video = ({ block }) => {
909
+ var Video = (0, import_react14.memo)(({ block }) => {
851
910
  if (block.type !== "video" || !block.video) {
852
911
  return null;
853
912
  }
@@ -876,19 +935,19 @@ var Video = ({ block }) => {
876
935
  ] }),
877
936
  caption2 && caption2.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("figcaption", { className: videoCaption, children: caption2.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: c.plain_text }, i)) })
878
937
  ] });
879
- };
938
+ });
939
+ Video.displayName = "Video";
940
+ var YOUTUBE_SHORT_URL_REGEX = /youtu\.be\/([a-zA-Z0-9_-]+)/;
941
+ var YOUTUBE_WATCH_URL_REGEX = /youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/;
880
942
  var getVideoEmbedUrl = (url) => {
881
943
  if (!url) return "";
882
- if (url.includes("youtu.be/")) {
883
- const videoId = url.split("youtu.be/")[1]?.split("?")[0];
884
- if (videoId) {
885
- return `https://www.youtube.com/embed/${videoId}`;
886
- }
887
- } else if (url.includes("youtube.com/watch?v=")) {
888
- const videoId = url.split("watch?v=")[1]?.split("&")[0];
889
- if (videoId) {
890
- return `https://www.youtube.com/embed/${videoId}`;
891
- }
944
+ const shortMatch = url.match(YOUTUBE_SHORT_URL_REGEX);
945
+ if (shortMatch?.[1]) {
946
+ return `https://www.youtube.com/embed/${shortMatch[1]}`;
947
+ }
948
+ const watchMatch = url.match(YOUTUBE_WATCH_URL_REGEX);
949
+ if (watchMatch?.[1]) {
950
+ return `https://www.youtube.com/embed/${watchMatch[1]}`;
892
951
  }
893
952
  return url;
894
953
  };
@@ -896,7 +955,7 @@ var Video_default = Video;
896
955
 
897
956
  // src/components/Renderer/components/Block/BlockRenderer.tsx
898
957
  var import_jsx_runtime17 = require("react/jsx-runtime");
899
- var BlockRenderer = ({ block, isColumn = false }) => {
958
+ var BlockRenderer = (0, import_react15.memo)(({ block, isColumn = false }) => {
900
959
  if (!block) return null;
901
960
  switch (block.type) {
902
961
  case "link_preview":
@@ -952,7 +1011,8 @@ var BlockRenderer = ({ block, isColumn = false }) => {
952
1011
  default:
953
1012
  return null;
954
1013
  }
955
- };
1014
+ });
1015
+ BlockRenderer.displayName = "BlockRenderer";
956
1016
  var BlockRenderer_default = BlockRenderer;
957
1017
 
958
1018
  // src/components/Title/index.tsx
@@ -962,9 +1022,6 @@ var Title = ({ title: title3 }) => {
962
1022
  };
963
1023
  var Title_default = Title;
964
1024
 
965
- // src/components/Cover/index.tsx
966
- var import_react6 = require("react");
967
-
968
1025
  // src/components/Cover/styles.css.ts
969
1026
  var import_createRuntimeFn4 = require("@vanilla-extract/recipes/createRuntimeFn");
970
1027
  var coverContainer = "styles_coverContainer__p0cp8d0";
@@ -974,17 +1031,7 @@ var skeletonWrapper2 = (0, import_createRuntimeFn4.createRuntimeFn)({ defaultCla
974
1031
  // src/components/Cover/index.tsx
975
1032
  var import_jsx_runtime19 = require("react/jsx-runtime");
976
1033
  var Cover = ({ src, alt }) => {
977
- const [isLoaded, setIsLoaded] = (0, import_react6.useState)(false);
978
- const imgRef = (0, import_react6.useRef)(null);
979
- (0, import_react6.useEffect)(() => {
980
- const img = imgRef.current;
981
- if (img && img.complete && img.naturalHeight !== 0) {
982
- setIsLoaded(true);
983
- }
984
- }, [src]);
985
- const handleLoad = () => {
986
- setIsLoaded(true);
987
- };
1034
+ const { isLoaded, imgRef, handleLoad } = useImageLoad(src);
988
1035
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: coverContainer, children: [
989
1036
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: skeletonWrapper2({ isLoaded }), children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Skeleton_default, { variant: "image", isLoading: !isLoaded }) }),
990
1037
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -1011,9 +1058,13 @@ var lightTheme = "theme_lightTheme__sq3jkb1m";
1011
1058
 
1012
1059
  // src/components/Renderer/index.tsx
1013
1060
  var import_jsx_runtime20 = require("react/jsx-runtime");
1014
- var Renderer = (0, import_react7.memo)(({ blocks, isDarkMode = false, title: title3, cover }) => {
1061
+ var Renderer = (0, import_react16.memo)(({ blocks, isDarkMode = false, title: title3, cover }) => {
1015
1062
  const theme = isDarkMode ? darkTheme : lightTheme;
1016
- const renderedBlocks = (0, import_react7.useMemo)(() => {
1063
+ const renderBlock = (0, import_react16.useCallback)(
1064
+ (childBlock) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BlockRenderer_default, { block: childBlock }),
1065
+ []
1066
+ );
1067
+ const renderedBlocks = (0, import_react16.useMemo)(() => {
1017
1068
  const result = [];
1018
1069
  for (let i = 0; i < blocks.length; i++) {
1019
1070
  const block = blocks[i];
@@ -1035,7 +1086,7 @@ var Renderer = (0, import_react7.memo)(({ blocks, isDarkMode = false, title: tit
1035
1086
  {
1036
1087
  blocks: listItems,
1037
1088
  type: listItemType,
1038
- renderBlock: (childBlock) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BlockRenderer_default, { block: childBlock })
1089
+ renderBlock
1039
1090
  },
1040
1091
  block.id
1041
1092
  )
@@ -1052,7 +1103,7 @@ var Renderer = (0, import_react7.memo)(({ blocks, isDarkMode = false, title: tit
1052
1103
  }
1053
1104
  }
1054
1105
  return result;
1055
- }, [blocks]);
1106
+ }, [blocks, renderBlock]);
1056
1107
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1057
1108
  cover && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Cover_default, { src: cover, alt: title3 || "Notion page content" }),
1058
1109
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(