notion-to-jsx 1.2.16 → 1.3.1

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,24 @@ 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
+ setIsLoaded(false);
209
+ const img = imgRef.current;
210
+ if (img && img.complete && img.naturalHeight !== 0) {
211
+ setIsLoaded(true);
212
+ }
213
+ }, [src]);
214
+ const handleLoad = (0, import_react2.useCallback)(() => {
215
+ setIsLoaded(true);
216
+ }, []);
217
+ return { isLoaded, imgRef, handleLoad };
218
+ };
219
+
196
220
  // src/components/Renderer/components/Image/Image.tsx
197
221
  var import_jsx_runtime4 = require("react/jsx-runtime");
198
222
  var MAX_WIDTH = 720;
@@ -206,17 +230,7 @@ var Image = ({
206
230
  format,
207
231
  isColumn = false
208
232
  }) => {
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
- };
233
+ const { isLoaded, imgRef, handleLoad } = useImageLoad(src);
220
234
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: imageContainer, children: [
221
235
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: imageWrapper, style: getImageStyles(format, isColumn), children: [
222
236
  /* @__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 +319,7 @@ var Bookmark = ({ url, metadata }) => {
305
319
  var Bookmark_default = Bookmark;
306
320
 
307
321
  // src/components/Renderer/components/LinkPreview/LinkPreview.tsx
308
- var import_react2 = require("react");
322
+ var import_react3 = require("react");
309
323
 
310
324
  // src/components/Renderer/components/LinkPreview/styles.css.ts
311
325
  var content2 = "styles_content__o1p3m12";
@@ -320,16 +334,19 @@ var title2 = "styles_title__o1p3m15";
320
334
 
321
335
  // src/components/Renderer/components/LinkPreview/LinkPreview.tsx
322
336
  var import_jsx_runtime6 = require("react/jsx-runtime");
323
- var fetchGitHubRepoData = async (repoPath) => {
337
+ var fetchGitHubRepoData = async (repoPath, signal) => {
324
338
  try {
325
339
  const apiUrl = `https://api.github.com/repos/${repoPath}`;
326
- const response = await fetch(apiUrl);
340
+ const response = await fetch(apiUrl, { signal });
327
341
  if (!response.ok) {
328
342
  throw new Error("Failed to fetch GitHub repo data");
329
343
  }
330
344
  const data = await response.json();
331
345
  return data;
332
346
  } catch (error) {
347
+ if (error instanceof Error && error.name === "AbortError") {
348
+ return null;
349
+ }
333
350
  console.error("Error fetching GitHub repo data:", error);
334
351
  return null;
335
352
  }
@@ -343,10 +360,7 @@ var extractFigmaData = (url) => {
343
360
  (segment) => segment.includes("file")
344
361
  );
345
362
  if (!fileSegment) return null;
346
- const fileIdMatch = fileSegment.match(/file\/([^/]+)/);
347
- const fileId = fileIdMatch ? fileIdMatch[1] : "";
348
363
  let fileName = "";
349
- let mode = "";
350
364
  if (pathSegments.length > 3) {
351
365
  const encodedName = pathSegments[3];
352
366
  if (encodedName) {
@@ -355,14 +369,10 @@ var extractFigmaData = (url) => {
355
369
  }
356
370
  if (!fileName && parsedUrl.pathname.includes("-")) {
357
371
  const nameMatch = parsedUrl.pathname.match(/\/([^/]+)(?:\?|$)/);
358
- if (nameMatch && nameMatch[1]) {
372
+ if (nameMatch?.[1]) {
359
373
  fileName = decodeURIComponent(nameMatch[1].replace(/-/g, " "));
360
374
  }
361
375
  }
362
- if (parsedUrl.search) {
363
- const searchParams = new URLSearchParams(parsedUrl.search);
364
- mode = searchParams.get("mode") || "";
365
- }
366
376
  fileName = fileName || "Figma Design";
367
377
  return {
368
378
  name: fileName,
@@ -405,6 +415,20 @@ var getLinkType = (url) => {
405
415
  return "unknown";
406
416
  }
407
417
  };
418
+ var MONTH_NAMES = [
419
+ "Jan",
420
+ "Feb",
421
+ "Mar",
422
+ "Apr",
423
+ "May",
424
+ "Jun",
425
+ "Jul",
426
+ "Aug",
427
+ "Sep",
428
+ "Oct",
429
+ "Nov",
430
+ "Dec"
431
+ ];
408
432
  var formatUpdatedTime = (dateString) => {
409
433
  const date = new Date(dateString);
410
434
  const now = /* @__PURE__ */ new Date();
@@ -413,39 +437,57 @@ var formatUpdatedTime = (dateString) => {
413
437
  );
414
438
  if (diffInHours < 24) {
415
439
  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
440
  }
441
+ const diffInDays = Math.floor(diffInHours / 24);
442
+ if (diffInDays === 1) {
443
+ return "Updated yesterday";
444
+ }
445
+ if (diffInDays < 30) {
446
+ return `Updated ${diffInDays} days ago`;
447
+ }
448
+ return `Updated on ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
440
449
  };
450
+ var FigmaPreview = (0, import_react3.memo)(({ data }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: preview, children: [
451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
452
+ "img",
453
+ {
454
+ src: data.thumbnailUrl || "https://static.figma.com/app/icon/1/favicon.svg",
455
+ alt: "Figma icon",
456
+ className: icon
457
+ }
458
+ ) }),
459
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: content2, children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: data.name }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: description2, children: "www.figma.com" })
462
+ ] })
463
+ ] }));
464
+ FigmaPreview.displayName = "FigmaPreview";
465
+ var GitHubPreview = (0, import_react3.memo)(({ repoData, repoName, updatedTimeText, loading }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `${preview} ${githubPreview}`, children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
467
+ "img",
468
+ {
469
+ src: repoData?.owner?.avatar_url || "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
470
+ alt: "Repository icon",
471
+ className: icon
472
+ }
473
+ ) }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `${content2} ${githubContent}`, children: [
475
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: title2, children: repoName }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: description2, children: loading ? "Loading..." : `${repoName} \u2022 ${updatedTimeText}` })
477
+ ] })
478
+ ] }));
479
+ GitHubPreview.displayName = "GitHubPreview";
480
+ 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 }) }) }));
481
+ DefaultPreview.displayName = "DefaultPreview";
441
482
  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)(
483
+ const [repoData, setRepoData] = (0, import_react3.useState)(null);
484
+ const [figmaData, setFigmaData] = (0, import_react3.useState)(null);
485
+ const [loading, setLoading] = (0, import_react3.useState)(true);
486
+ const [linkType, setLinkType] = (0, import_react3.useState)(
446
487
  "unknown"
447
488
  );
448
- (0, import_react2.useEffect)(() => {
489
+ (0, import_react3.useEffect)(() => {
490
+ const abortController = new AbortController();
449
491
  const loadLinkData = async () => {
450
492
  setLoading(true);
451
493
  const type = getLinkType(url);
@@ -453,19 +495,45 @@ var LinkPreview = ({ url }) => {
453
495
  if (type === "github") {
454
496
  const repoPath = extractRepoPathFromUrl(url);
455
497
  if (repoPath) {
456
- const data = await fetchGitHubRepoData(repoPath);
457
- setRepoData(data);
498
+ const data = await fetchGitHubRepoData(repoPath, abortController.signal);
499
+ if (!abortController.signal.aborted) {
500
+ setRepoData(data);
501
+ }
458
502
  }
459
503
  } else if (type === "figma") {
460
504
  const data = extractFigmaData(url);
461
- setFigmaData(data);
505
+ if (!abortController.signal.aborted) {
506
+ setFigmaData(data);
507
+ }
508
+ }
509
+ if (!abortController.signal.aborted) {
510
+ setLoading(false);
462
511
  }
463
- setLoading(false);
464
512
  };
465
513
  loadLinkData();
514
+ return () => {
515
+ abortController.abort();
516
+ };
466
517
  }, [url]);
467
518
  const repoName = repoData?.name || extractRepoPathFromUrl(url)?.split("/")[1] || "Repository";
468
519
  const updatedTimeText = repoData?.updated_at ? formatUpdatedTime(repoData.updated_at) : "";
520
+ const renderPreview = () => {
521
+ if (linkType === "figma" && figmaData) {
522
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FigmaPreview, { data: figmaData });
523
+ }
524
+ if (linkType === "github") {
525
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
526
+ GitHubPreview,
527
+ {
528
+ repoData,
529
+ repoName,
530
+ updatedTimeText,
531
+ loading
532
+ }
533
+ );
534
+ }
535
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DefaultPreview, { url });
536
+ };
469
537
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
470
538
  "a",
471
539
  {
@@ -473,58 +541,31 @@ var LinkPreview = ({ url }) => {
473
541
  target: "_blank",
474
542
  rel: "noopener noreferrer",
475
543
  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
- )
544
+ children: renderPreview()
512
545
  }
513
546
  );
514
547
  };
515
548
  var LinkPreview_default = LinkPreview;
516
549
 
517
550
  // 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);
551
+ var areRichTextsEqual = (prev, next) => {
552
+ if (prev === next) return true;
553
+ if (!prev || !next) return prev === next;
554
+ if (prev.length !== next.length) return false;
555
+ return prev.every(
556
+ (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
557
+ );
558
+ };
559
+ var MemoizedRichText = (0, import_react4.memo)(RichTexts_default, (prev, next) => {
560
+ return areRichTextsEqual(prev.richTexts, next.richTexts);
520
561
  });
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);
562
+ var MemoizedImage = (0, import_react4.memo)(Image_default, (prev, next) => {
563
+ return prev.src === next.src && prev.alt === next.alt && areRichTextsEqual(prev.caption, next.caption);
523
564
  });
524
- var MemoizedBookmark = (0, import_react3.memo)(Bookmark_default, (prev, next) => {
565
+ var MemoizedBookmark = (0, import_react4.memo)(Bookmark_default, (prev, next) => {
525
566
  return prev.url === next.url;
526
567
  });
527
- var MemoizedLinkPreview = (0, import_react3.memo)(
568
+ var MemoizedLinkPreview = (0, import_react4.memo)(
528
569
  LinkPreview_default,
529
570
  (prev, next) => {
530
571
  return prev.url === next.url;
@@ -533,7 +574,7 @@ var MemoizedLinkPreview = (0, import_react3.memo)(
533
574
 
534
575
  // src/components/Renderer/components/List/ListItemGroup.tsx
535
576
  var import_jsx_runtime7 = require("react/jsx-runtime");
536
- var RecursiveListItem = ({ block, renderBlock }) => {
577
+ var RecursiveListItem = (0, import_react5.memo)(({ block, renderBlock }) => {
537
578
  let content3;
538
579
  if (block.type === "bulleted_list_item") {
539
580
  content3 = block.bulleted_list_item;
@@ -593,8 +634,9 @@ var RecursiveListItem = ({ block, renderBlock }) => {
593
634
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MemoizedRichText, { richTexts }),
594
635
  renderedChildren.length > 0 && renderedChildren
595
636
  ] });
596
- };
597
- var ListGroup = ({ blocks, type, renderBlock }) => {
637
+ });
638
+ RecursiveListItem.displayName = "RecursiveListItem";
639
+ var ListGroup = (0, import_react5.memo)(({ blocks, type, renderBlock }) => {
598
640
  if (blocks.length === 0) return null;
599
641
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
600
642
  List,
@@ -613,10 +655,14 @@ var ListGroup = ({ blocks, type, renderBlock }) => {
613
655
  ))
614
656
  }
615
657
  );
616
- };
658
+ });
659
+ ListGroup.displayName = "ListGroup";
660
+
661
+ // src/components/Renderer/components/Block/BlockRenderer.tsx
662
+ var import_react15 = require("react");
617
663
 
618
664
  // src/components/Renderer/components/Code/CodeBlock.tsx
619
- var import_react4 = require("react");
665
+ var import_react6 = require("react");
620
666
 
621
667
  // src/components/Renderer/components/Code/styles.css.ts
622
668
  var codeBlock = "styles_codeBlock__1qn42yc0";
@@ -645,8 +691,8 @@ var renderToken = (token, i) => {
645
691
  }
646
692
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `token ${token.type}`, children: tokenContent }, i);
647
693
  };
648
- var CodeBlock = ({ code, language, caption: caption2 }) => {
649
- const tokens = (0, import_react4.useMemo)(() => {
694
+ var CodeBlock = (0, import_react6.memo)(({ code, language, caption: caption2 }) => {
695
+ const tokens = (0, import_react6.useMemo)(() => {
650
696
  const prismLanguage = import_prismjs.default.languages[language] || import_prismjs.default.languages.plaintext;
651
697
  return import_prismjs.default.tokenize(code, prismLanguage);
652
698
  }, [code, language]);
@@ -654,9 +700,13 @@ var CodeBlock = ({ code, language, caption: caption2 }) => {
654
700
  /* @__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
701
  caption2 && caption2.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MemoizedRichText, { richTexts: caption2 }) })
656
702
  ] });
657
- };
703
+ });
704
+ CodeBlock.displayName = "CodeBlock";
658
705
  var CodeBlock_default = CodeBlock;
659
706
 
707
+ // src/components/Renderer/components/Typography/Typography.tsx
708
+ var import_react7 = require("react");
709
+
660
710
  // src/components/Renderer/components/Typography/styles.css.ts
661
711
  var heading1 = "styles_heading1__90a95g1";
662
712
  var heading2 = "styles_heading2__90a95g2";
@@ -665,34 +715,25 @@ var paragraph = "styles_paragraph__90a95g0";
665
715
 
666
716
  // src/components/Renderer/components/Typography/Typography.tsx
667
717
  var import_jsx_runtime9 = require("react/jsx-runtime");
668
- var Paragraph = ({
669
- className,
670
- children,
671
- ...props
672
- }) => {
718
+ var Paragraph = (0, import_react7.memo)(({ children, ...props }) => {
673
719
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: paragraph, ...props, children });
674
- };
675
- var Heading1 = ({
676
- className,
677
- children,
678
- ...props
679
- }) => {
720
+ });
721
+ Paragraph.displayName = "Paragraph";
722
+ var Heading1 = (0, import_react7.memo)(({ children, ...props }) => {
680
723
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: heading1, ...props, children });
681
- };
682
- var Heading2 = ({
683
- className,
684
- children,
685
- ...props
686
- }) => {
724
+ });
725
+ Heading1.displayName = "Heading1";
726
+ var Heading2 = (0, import_react7.memo)(({ children, ...props }) => {
687
727
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: heading2, ...props, children });
688
- };
689
- var Heading3 = ({
690
- className,
691
- children,
692
- ...props
693
- }) => {
728
+ });
729
+ Heading2.displayName = "Heading2";
730
+ var Heading3 = (0, import_react7.memo)(({ children, ...props }) => {
694
731
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: heading3, ...props, children });
695
- };
732
+ });
733
+ Heading3.displayName = "Heading3";
734
+
735
+ // src/components/Renderer/components/Column/Column.tsx
736
+ var import_react8 = require("react");
696
737
 
697
738
  // src/components/Renderer/components/Column/styles.css.ts
698
739
  var columnContainer = "styles_columnContainer__wle6we0";
@@ -700,30 +741,40 @@ var columnListContainer = "styles_columnListContainer__wle6we1";
700
741
 
701
742
  // src/components/Renderer/components/Column/Column.tsx
702
743
  var import_jsx_runtime10 = require("react/jsx-runtime");
703
- var Column = ({ block }) => {
744
+ var Column = (0, import_react8.memo)(({ block }) => {
704
745
  if (!block || !block.children) return null;
705
746
  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
- };
747
+ });
748
+ Column.displayName = "Column";
707
749
  var Column_default = Column;
708
750
 
709
751
  // src/components/Renderer/components/Column/ColumnList.tsx
752
+ var import_react9 = require("react");
710
753
  var import_jsx_runtime11 = require("react/jsx-runtime");
711
- var ColumnList = ({ block }) => {
754
+ var ColumnList = (0, import_react9.memo)(({ block }) => {
712
755
  if (!block || !block.children) return null;
713
756
  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
- };
757
+ });
758
+ ColumnList.displayName = "ColumnList";
715
759
  var ColumnList_default = ColumnList;
716
760
 
761
+ // src/components/Renderer/components/Quote/Quote.tsx
762
+ var import_react10 = require("react");
763
+
717
764
  // src/components/Renderer/components/Quote/styles.css.ts
718
765
  var container = "styles_container__mra9n0";
719
766
 
720
767
  // src/components/Renderer/components/Quote/Quote.tsx
721
768
  var import_jsx_runtime12 = require("react/jsx-runtime");
722
- var Quote = ({ richTexts, tabIndex }) => {
769
+ var Quote = (0, import_react10.memo)(({ richTexts, tabIndex }) => {
723
770
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("blockquote", { className: container, tabIndex, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MemoizedRichText, { richTexts }) });
724
- };
771
+ });
772
+ Quote.displayName = "Quote";
725
773
  var Quote_default = Quote;
726
774
 
775
+ // src/components/Renderer/components/Table/Table.tsx
776
+ var import_react12 = require("react");
777
+
727
778
  // src/components/Renderer/components/Table/styles.css.ts
728
779
  var firstCell = "styles_firstCell__1rvbzfo4";
729
780
  var hasRowHeader = "styles_hasRowHeader__1rvbzfo6";
@@ -734,8 +785,9 @@ var tableCell = "styles_tableCell__1rvbzfo3";
734
785
  var tableContainer = "styles_tableContainer__1rvbzfo0";
735
786
 
736
787
  // src/components/Renderer/components/Table/TableRow.tsx
788
+ var import_react11 = require("react");
737
789
  var import_jsx_runtime13 = require("react/jsx-runtime");
738
- var TableRow = ({
790
+ var TableRow = (0, import_react11.memo)(({
739
791
  rowBlock,
740
792
  cellClassName = "",
741
793
  rowHeaderIndex = -1
@@ -761,12 +813,13 @@ var TableRow = ({
761
813
  `${rowBlock.id}-cell-${index}`
762
814
  );
763
815
  }) });
764
- };
816
+ });
817
+ TableRow.displayName = "TableRow";
765
818
  var TableRow_default = TableRow;
766
819
 
767
820
  // src/components/Renderer/components/Table/Table.tsx
768
821
  var import_jsx_runtime14 = require("react/jsx-runtime");
769
- var Table = ({ block }) => {
822
+ var Table = (0, import_react12.memo)(({ block }) => {
770
823
  if (!block.table || !block.children) {
771
824
  return null;
772
825
  }
@@ -788,14 +841,15 @@ var Table = ({ block }) => {
788
841
  );
789
842
  }) })
790
843
  ] }) }) });
791
- };
844
+ });
845
+ Table.displayName = "Table";
792
846
  var Table_default = Table;
793
847
 
794
848
  // src/components/Renderer/components/Table/index.ts
795
849
  var Table_default2 = Table_default;
796
850
 
797
851
  // src/components/Renderer/components/Toggle/Toggle.tsx
798
- var import_react5 = require("react");
852
+ var import_react13 = require("react");
799
853
 
800
854
  // src/components/Renderer/components/Toggle/styles.css.ts
801
855
  var toggleContainer = "styles_toggleContainer__p7ue1z0";
@@ -807,7 +861,7 @@ var toggleIconOpen = "styles_toggleIconOpen__p7ue1z3";
807
861
  // src/components/Renderer/components/Toggle/Toggle.tsx
808
862
  var import_jsx_runtime15 = require("react/jsx-runtime");
809
863
  var Toggle = ({ block }) => {
810
- const [isOpen, setIsOpen] = (0, import_react5.useState)(false);
864
+ const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
811
865
  if (!block.toggle || !block.children) {
812
866
  return null;
813
867
  }
@@ -840,6 +894,9 @@ var Toggle = ({ block }) => {
840
894
  };
841
895
  var Toggle_default = Toggle;
842
896
 
897
+ // src/components/Renderer/components/Video/Video.tsx
898
+ var import_react14 = require("react");
899
+
843
900
  // src/components/Renderer/components/Video/styles.css.ts
844
901
  var videoCaption = "styles_videoCaption__15b9vkb2";
845
902
  var videoContainer = "styles_videoContainer__15b9vkb0";
@@ -847,7 +904,7 @@ var videoPlayer = "styles_videoPlayer__15b9vkb1";
847
904
 
848
905
  // src/components/Renderer/components/Video/Video.tsx
849
906
  var import_jsx_runtime16 = require("react/jsx-runtime");
850
- var Video = ({ block }) => {
907
+ var Video = (0, import_react14.memo)(({ block }) => {
851
908
  if (block.type !== "video" || !block.video) {
852
909
  return null;
853
910
  }
@@ -876,19 +933,19 @@ var Video = ({ block }) => {
876
933
  ] }),
877
934
  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
935
  ] });
879
- };
936
+ });
937
+ Video.displayName = "Video";
938
+ var YOUTUBE_SHORT_URL_REGEX = /youtu\.be\/([a-zA-Z0-9_-]+)/;
939
+ var YOUTUBE_WATCH_URL_REGEX = /youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/;
880
940
  var getVideoEmbedUrl = (url) => {
881
941
  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
- }
942
+ const shortMatch = url.match(YOUTUBE_SHORT_URL_REGEX);
943
+ if (shortMatch?.[1]) {
944
+ return `https://www.youtube.com/embed/${shortMatch[1]}`;
945
+ }
946
+ const watchMatch = url.match(YOUTUBE_WATCH_URL_REGEX);
947
+ if (watchMatch?.[1]) {
948
+ return `https://www.youtube.com/embed/${watchMatch[1]}`;
892
949
  }
893
950
  return url;
894
951
  };
@@ -896,7 +953,7 @@ var Video_default = Video;
896
953
 
897
954
  // src/components/Renderer/components/Block/BlockRenderer.tsx
898
955
  var import_jsx_runtime17 = require("react/jsx-runtime");
899
- var BlockRenderer = ({ block, isColumn = false }) => {
956
+ var BlockRenderer = (0, import_react15.memo)(({ block, isColumn = false }) => {
900
957
  if (!block) return null;
901
958
  switch (block.type) {
902
959
  case "link_preview":
@@ -952,7 +1009,8 @@ var BlockRenderer = ({ block, isColumn = false }) => {
952
1009
  default:
953
1010
  return null;
954
1011
  }
955
- };
1012
+ });
1013
+ BlockRenderer.displayName = "BlockRenderer";
956
1014
  var BlockRenderer_default = BlockRenderer;
957
1015
 
958
1016
  // src/components/Title/index.tsx
@@ -962,9 +1020,6 @@ var Title = ({ title: title3 }) => {
962
1020
  };
963
1021
  var Title_default = Title;
964
1022
 
965
- // src/components/Cover/index.tsx
966
- var import_react6 = require("react");
967
-
968
1023
  // src/components/Cover/styles.css.ts
969
1024
  var import_createRuntimeFn4 = require("@vanilla-extract/recipes/createRuntimeFn");
970
1025
  var coverContainer = "styles_coverContainer__p0cp8d0";
@@ -974,17 +1029,7 @@ var skeletonWrapper2 = (0, import_createRuntimeFn4.createRuntimeFn)({ defaultCla
974
1029
  // src/components/Cover/index.tsx
975
1030
  var import_jsx_runtime19 = require("react/jsx-runtime");
976
1031
  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
- };
1032
+ const { isLoaded, imgRef, handleLoad } = useImageLoad(src);
988
1033
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: coverContainer, children: [
989
1034
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: skeletonWrapper2({ isLoaded }), children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Skeleton_default, { variant: "image", isLoading: !isLoaded }) }),
990
1035
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -1011,9 +1056,13 @@ var lightTheme = "theme_lightTheme__sq3jkb1m";
1011
1056
 
1012
1057
  // src/components/Renderer/index.tsx
1013
1058
  var import_jsx_runtime20 = require("react/jsx-runtime");
1014
- var Renderer = (0, import_react7.memo)(({ blocks, isDarkMode = false, title: title3, cover }) => {
1059
+ var Renderer = (0, import_react16.memo)(({ blocks, isDarkMode = false, title: title3, cover }) => {
1015
1060
  const theme = isDarkMode ? darkTheme : lightTheme;
1016
- const renderedBlocks = (0, import_react7.useMemo)(() => {
1061
+ const renderBlock = (0, import_react16.useCallback)(
1062
+ (childBlock) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BlockRenderer_default, { block: childBlock }),
1063
+ []
1064
+ );
1065
+ const renderedBlocks = (0, import_react16.useMemo)(() => {
1017
1066
  const result = [];
1018
1067
  for (let i = 0; i < blocks.length; i++) {
1019
1068
  const block = blocks[i];
@@ -1035,7 +1084,7 @@ var Renderer = (0, import_react7.memo)(({ blocks, isDarkMode = false, title: tit
1035
1084
  {
1036
1085
  blocks: listItems,
1037
1086
  type: listItemType,
1038
- renderBlock: (childBlock) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BlockRenderer_default, { block: childBlock })
1087
+ renderBlock
1039
1088
  },
1040
1089
  block.id
1041
1090
  )
@@ -1052,7 +1101,7 @@ var Renderer = (0, import_react7.memo)(({ blocks, isDarkMode = false, title: tit
1052
1101
  }
1053
1102
  }
1054
1103
  return result;
1055
- }, [blocks]);
1104
+ }, [blocks, renderBlock]);
1056
1105
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1057
1106
  cover && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Cover_default, { src: cover, alt: title3 || "Notion page content" }),
1058
1107
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(