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.mjs CHANGED
@@ -1,5 +1,11 @@
1
1
  // src/components/Renderer/index.tsx
2
- import { useMemo as useMemo2, memo as memo2 } from "react";
2
+ import { useMemo as useMemo2, memo as memo14, useCallback as useCallback2 } from "react";
3
+
4
+ // src/components/Renderer/components/List/ListItemGroup.tsx
5
+ import { memo as memo4 } from "react";
6
+
7
+ // src/components/Renderer/components/List/List.tsx
8
+ import { memo } from "react";
3
9
 
4
10
  // src/components/Renderer/components/List/styles.css.ts
5
11
  import { createRuntimeFn as _7a468 } from "@vanilla-extract/recipes/createRuntimeFn";
@@ -8,25 +14,25 @@ var listItem = "styles_listItem__es8vim3";
8
14
 
9
15
  // src/components/Renderer/components/List/List.tsx
10
16
  import { jsx } from "react/jsx-runtime";
11
- var List = ({
17
+ var List = memo(({
12
18
  as: Component = "ul",
13
19
  type,
14
- className,
15
20
  children,
16
21
  ...props
17
22
  }) => {
18
23
  return /* @__PURE__ */ jsx(Component, { className: list({ type }), ...props, children });
19
- };
20
- var ListItem = ({
21
- className,
24
+ });
25
+ List.displayName = "List";
26
+ var ListItem = memo(({
22
27
  children,
23
28
  ...props
24
29
  }) => {
25
30
  return /* @__PURE__ */ jsx("li", { className: listItem, ...props, children });
26
- };
31
+ });
32
+ ListItem.displayName = "ListItem";
27
33
 
28
34
  // src/components/Renderer/components/MemoizedComponents.tsx
29
- import { memo } from "react";
35
+ import { memo as memo3 } from "react";
30
36
 
31
37
  // src/components/Renderer/components/RichText/styles.css.ts
32
38
  import { createRuntimeFn as _7a4682 } from "@vanilla-extract/recipes/createRuntimeFn";
@@ -36,6 +42,30 @@ var richText = _7a4682({ defaultClassName: "styles__fdf3tw0", variantClassNames:
36
42
 
37
43
  // src/components/Renderer/components/RichText/RichTexts.tsx
38
44
  import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
45
+ var NOTION_COLORS = [
46
+ "default",
47
+ "gray",
48
+ "brown",
49
+ "orange",
50
+ "yellow",
51
+ "green",
52
+ "blue",
53
+ "purple",
54
+ "pink",
55
+ "red",
56
+ "gray_background",
57
+ "brown_background",
58
+ "orange_background",
59
+ "yellow_background",
60
+ "green_background",
61
+ "blue_background",
62
+ "purple_background",
63
+ "pink_background",
64
+ "red_background"
65
+ ];
66
+ var isNotionColor = (color) => {
67
+ return NOTION_COLORS.includes(color);
68
+ };
39
69
  var renderLink = (href, content3) => /* @__PURE__ */ jsx2("a", { href, target: "_blank", rel: "noopener noreferrer", className: link, children: content3 });
40
70
  var EmptyRichText = () => /* @__PURE__ */ jsx2("div", { className: emptyRichText });
41
71
  var RichTexts = ({ richTexts }) => {
@@ -63,28 +93,7 @@ var RichTexts = ({ richTexts }) => {
63
93
  content3 = text.plain_text;
64
94
  }
65
95
  }
66
- const notionColors = [
67
- "default",
68
- "gray",
69
- "brown",
70
- "orange",
71
- "yellow",
72
- "green",
73
- "blue",
74
- "purple",
75
- "pink",
76
- "red",
77
- "gray_background",
78
- "brown_background",
79
- "orange_background",
80
- "yellow_background",
81
- "green_background",
82
- "blue_background",
83
- "purple_background",
84
- "pink_background",
85
- "red_background"
86
- ];
87
- const safeColor = notionColors.includes(color) ? color : void 0;
96
+ const safeColor = isNotionColor(color) ? color : void 0;
88
97
  return /* @__PURE__ */ jsx2(
89
98
  "span",
90
99
  {
@@ -104,9 +113,6 @@ var RichTexts = ({ richTexts }) => {
104
113
  };
105
114
  var RichTexts_default = RichTexts;
106
115
 
107
- // src/components/Renderer/components/Image/Image.tsx
108
- import { useState, useRef, useEffect } from "react";
109
-
110
116
  // src/components/Renderer/components/Image/styles.css.ts
111
117
  import { createRuntimeFn as _7a4683 } from "@vanilla-extract/recipes/createRuntimeFn";
112
118
  var caption = "styles_caption__2f3ncf7";
@@ -157,6 +163,26 @@ var Skeleton = ({
157
163
  };
158
164
  var Skeleton_default = Skeleton;
159
165
 
166
+ // src/hooks/useImageLoad.ts
167
+ import { useState, useRef, useEffect, useCallback } from "react";
168
+ var useImageLoad = (src) => {
169
+ const [isLoaded, setIsLoaded] = useState(false);
170
+ const imgRef = useRef(null);
171
+ useEffect(() => {
172
+ const img = imgRef.current;
173
+ if (img && img.complete && img.naturalHeight !== 0) {
174
+ setIsLoaded(true);
175
+ }
176
+ }, [src]);
177
+ useEffect(() => {
178
+ setIsLoaded(false);
179
+ }, [src]);
180
+ const handleLoad = useCallback(() => {
181
+ setIsLoaded(true);
182
+ }, []);
183
+ return { isLoaded, imgRef, handleLoad };
184
+ };
185
+
160
186
  // src/components/Renderer/components/Image/Image.tsx
161
187
  import { jsx as jsx4, jsxs } from "react/jsx-runtime";
162
188
  var MAX_WIDTH = 720;
@@ -170,17 +196,7 @@ var Image = ({
170
196
  format,
171
197
  isColumn = false
172
198
  }) => {
173
- const [isLoaded, setIsLoaded] = useState(false);
174
- const imgRef = useRef(null);
175
- useEffect(() => {
176
- const img = imgRef.current;
177
- if (img && img.complete && img.naturalHeight !== 0) {
178
- setIsLoaded(true);
179
- }
180
- }, [src]);
181
- const handleLoad = () => {
182
- setIsLoaded(true);
183
- };
199
+ const { isLoaded, imgRef, handleLoad } = useImageLoad(src);
184
200
  return /* @__PURE__ */ jsxs("div", { className: imageContainer, children: [
185
201
  /* @__PURE__ */ jsxs("div", { className: imageWrapper, style: getImageStyles(format, isColumn), children: [
186
202
  /* @__PURE__ */ jsx4("div", { className: skeletonWrapper({ isLoaded }), children: /* @__PURE__ */ jsx4(Skeleton_default, { variant: "image", isLoading: !isLoaded }) }),
@@ -269,7 +285,7 @@ var Bookmark = ({ url, metadata }) => {
269
285
  var Bookmark_default = Bookmark;
270
286
 
271
287
  // src/components/Renderer/components/LinkPreview/LinkPreview.tsx
272
- import { useState as useState2, useEffect as useEffect2 } from "react";
288
+ import { useState as useState2, useEffect as useEffect2, memo as memo2 } from "react";
273
289
 
274
290
  // src/components/Renderer/components/LinkPreview/styles.css.ts
275
291
  var content2 = "styles_content__o1p3m12";
@@ -284,16 +300,19 @@ var title2 = "styles_title__o1p3m15";
284
300
 
285
301
  // src/components/Renderer/components/LinkPreview/LinkPreview.tsx
286
302
  import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
287
- var fetchGitHubRepoData = async (repoPath) => {
303
+ var fetchGitHubRepoData = async (repoPath, signal) => {
288
304
  try {
289
305
  const apiUrl = `https://api.github.com/repos/${repoPath}`;
290
- const response = await fetch(apiUrl);
306
+ const response = await fetch(apiUrl, { signal });
291
307
  if (!response.ok) {
292
308
  throw new Error("Failed to fetch GitHub repo data");
293
309
  }
294
310
  const data = await response.json();
295
311
  return data;
296
312
  } catch (error) {
313
+ if (error instanceof Error && error.name === "AbortError") {
314
+ return null;
315
+ }
297
316
  console.error("Error fetching GitHub repo data:", error);
298
317
  return null;
299
318
  }
@@ -307,10 +326,7 @@ var extractFigmaData = (url) => {
307
326
  (segment) => segment.includes("file")
308
327
  );
309
328
  if (!fileSegment) return null;
310
- const fileIdMatch = fileSegment.match(/file\/([^/]+)/);
311
- const fileId = fileIdMatch ? fileIdMatch[1] : "";
312
329
  let fileName = "";
313
- let mode = "";
314
330
  if (pathSegments.length > 3) {
315
331
  const encodedName = pathSegments[3];
316
332
  if (encodedName) {
@@ -319,14 +335,10 @@ var extractFigmaData = (url) => {
319
335
  }
320
336
  if (!fileName && parsedUrl.pathname.includes("-")) {
321
337
  const nameMatch = parsedUrl.pathname.match(/\/([^/]+)(?:\?|$)/);
322
- if (nameMatch && nameMatch[1]) {
338
+ if (nameMatch?.[1]) {
323
339
  fileName = decodeURIComponent(nameMatch[1].replace(/-/g, " "));
324
340
  }
325
341
  }
326
- if (parsedUrl.search) {
327
- const searchParams = new URLSearchParams(parsedUrl.search);
328
- mode = searchParams.get("mode") || "";
329
- }
330
342
  fileName = fileName || "Figma Design";
331
343
  return {
332
344
  name: fileName,
@@ -369,6 +381,20 @@ var getLinkType = (url) => {
369
381
  return "unknown";
370
382
  }
371
383
  };
384
+ var MONTH_NAMES = [
385
+ "Jan",
386
+ "Feb",
387
+ "Mar",
388
+ "Apr",
389
+ "May",
390
+ "Jun",
391
+ "Jul",
392
+ "Aug",
393
+ "Sep",
394
+ "Oct",
395
+ "Nov",
396
+ "Dec"
397
+ ];
372
398
  var formatUpdatedTime = (dateString) => {
373
399
  const date = new Date(dateString);
374
400
  const now = /* @__PURE__ */ new Date();
@@ -377,31 +403,48 @@ var formatUpdatedTime = (dateString) => {
377
403
  );
378
404
  if (diffInHours < 24) {
379
405
  return `Updated ${diffInHours} hours ago`;
380
- } else {
381
- const diffInDays = Math.floor(diffInHours / 24);
382
- if (diffInDays === 1) {
383
- return "Updated yesterday";
384
- } else if (diffInDays < 30) {
385
- return `Updated ${diffInDays} days ago`;
386
- } else {
387
- const months = [
388
- "Jan",
389
- "Feb",
390
- "Mar",
391
- "Apr",
392
- "May",
393
- "Jun",
394
- "Jul",
395
- "Aug",
396
- "Sep",
397
- "Oct",
398
- "Nov",
399
- "Dec"
400
- ];
401
- return `Updated on ${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
402
- }
403
406
  }
407
+ const diffInDays = Math.floor(diffInHours / 24);
408
+ if (diffInDays === 1) {
409
+ return "Updated yesterday";
410
+ }
411
+ if (diffInDays < 30) {
412
+ return `Updated ${diffInDays} days ago`;
413
+ }
414
+ return `Updated on ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
404
415
  };
416
+ var FigmaPreview = memo2(({ data }) => /* @__PURE__ */ jsxs3("div", { className: preview, children: [
417
+ /* @__PURE__ */ jsx6("div", { className: iconContainer, children: /* @__PURE__ */ jsx6(
418
+ "img",
419
+ {
420
+ src: data.thumbnailUrl || "https://static.figma.com/app/icon/1/favicon.svg",
421
+ alt: "Figma icon",
422
+ className: icon
423
+ }
424
+ ) }),
425
+ /* @__PURE__ */ jsxs3("div", { className: content2, children: [
426
+ /* @__PURE__ */ jsx6("div", { className: title2, children: data.name }),
427
+ /* @__PURE__ */ jsx6("div", { className: description2, children: "www.figma.com" })
428
+ ] })
429
+ ] }));
430
+ FigmaPreview.displayName = "FigmaPreview";
431
+ var GitHubPreview = memo2(({ repoData, repoName, updatedTimeText, loading }) => /* @__PURE__ */ jsxs3("div", { className: `${preview} ${githubPreview}`, children: [
432
+ /* @__PURE__ */ jsx6("div", { className: iconContainer, children: /* @__PURE__ */ jsx6(
433
+ "img",
434
+ {
435
+ src: repoData?.owner?.avatar_url || "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
436
+ alt: "Repository icon",
437
+ className: icon
438
+ }
439
+ ) }),
440
+ /* @__PURE__ */ jsxs3("div", { className: `${content2} ${githubContent}`, children: [
441
+ /* @__PURE__ */ jsx6("div", { className: title2, children: repoName }),
442
+ /* @__PURE__ */ jsx6("div", { className: description2, children: loading ? "Loading..." : `${repoName} \u2022 ${updatedTimeText}` })
443
+ ] })
444
+ ] }));
445
+ GitHubPreview.displayName = "GitHubPreview";
446
+ var DefaultPreview = memo2(({ url }) => /* @__PURE__ */ jsx6("div", { className: preview, children: /* @__PURE__ */ jsx6("div", { className: content2, children: /* @__PURE__ */ jsx6("div", { className: title2, children: url }) }) }));
447
+ DefaultPreview.displayName = "DefaultPreview";
405
448
  var LinkPreview = ({ url }) => {
406
449
  const [repoData, setRepoData] = useState2(null);
407
450
  const [figmaData, setFigmaData] = useState2(null);
@@ -410,6 +453,7 @@ var LinkPreview = ({ url }) => {
410
453
  "unknown"
411
454
  );
412
455
  useEffect2(() => {
456
+ const abortController = new AbortController();
413
457
  const loadLinkData = async () => {
414
458
  setLoading(true);
415
459
  const type = getLinkType(url);
@@ -417,19 +461,45 @@ var LinkPreview = ({ url }) => {
417
461
  if (type === "github") {
418
462
  const repoPath = extractRepoPathFromUrl(url);
419
463
  if (repoPath) {
420
- const data = await fetchGitHubRepoData(repoPath);
421
- setRepoData(data);
464
+ const data = await fetchGitHubRepoData(repoPath, abortController.signal);
465
+ if (!abortController.signal.aborted) {
466
+ setRepoData(data);
467
+ }
422
468
  }
423
469
  } else if (type === "figma") {
424
470
  const data = extractFigmaData(url);
425
- setFigmaData(data);
471
+ if (!abortController.signal.aborted) {
472
+ setFigmaData(data);
473
+ }
474
+ }
475
+ if (!abortController.signal.aborted) {
476
+ setLoading(false);
426
477
  }
427
- setLoading(false);
428
478
  };
429
479
  loadLinkData();
480
+ return () => {
481
+ abortController.abort();
482
+ };
430
483
  }, [url]);
431
484
  const repoName = repoData?.name || extractRepoPathFromUrl(url)?.split("/")[1] || "Repository";
432
485
  const updatedTimeText = repoData?.updated_at ? formatUpdatedTime(repoData.updated_at) : "";
486
+ const renderPreview = () => {
487
+ if (linkType === "figma" && figmaData) {
488
+ return /* @__PURE__ */ jsx6(FigmaPreview, { data: figmaData });
489
+ }
490
+ if (linkType === "github") {
491
+ return /* @__PURE__ */ jsx6(
492
+ GitHubPreview,
493
+ {
494
+ repoData,
495
+ repoName,
496
+ updatedTimeText,
497
+ loading
498
+ }
499
+ );
500
+ }
501
+ return /* @__PURE__ */ jsx6(DefaultPreview, { url });
502
+ };
433
503
  return /* @__PURE__ */ jsx6(
434
504
  "a",
435
505
  {
@@ -437,58 +507,31 @@ var LinkPreview = ({ url }) => {
437
507
  target: "_blank",
438
508
  rel: "noopener noreferrer",
439
509
  className: link3,
440
- children: linkType === "figma" && figmaData ? (
441
- // Figma 프리뷰 렌더링
442
- /* @__PURE__ */ jsxs3("div", { className: preview, children: [
443
- /* @__PURE__ */ jsx6("div", { className: iconContainer, children: /* @__PURE__ */ jsx6(
444
- "img",
445
- {
446
- src: figmaData.thumbnailUrl || "https://static.figma.com/app/icon/1/favicon.svg",
447
- alt: "Figma icon",
448
- className: icon
449
- }
450
- ) }),
451
- /* @__PURE__ */ jsxs3("div", { className: content2, children: [
452
- /* @__PURE__ */ jsx6("div", { className: title2, children: figmaData.name }),
453
- /* @__PURE__ */ jsx6("div", { className: description2, children: "www.figma.com" })
454
- ] })
455
- ] })
456
- ) : linkType === "github" ? (
457
- // GitHub 프리뷰 렌더링
458
- /* @__PURE__ */ jsxs3("div", { className: `${preview} ${githubPreview}`, children: [
459
- /* @__PURE__ */ jsx6("div", { className: iconContainer, children: /* @__PURE__ */ jsx6(
460
- "img",
461
- {
462
- src: repoData?.owner?.avatar_url || "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
463
- alt: "Repository icon",
464
- className: icon
465
- }
466
- ) }),
467
- /* @__PURE__ */ jsxs3("div", { className: `${content2} ${githubContent}`, children: [
468
- /* @__PURE__ */ jsx6("div", { className: title2, children: repoName }),
469
- /* @__PURE__ */ jsx6("div", { className: description2, children: loading ? "Loading..." : `${repoName} \u2022 ${updatedTimeText}` })
470
- ] })
471
- ] })
472
- ) : (
473
- // 기본 링크 프리뷰 렌더링
474
- /* @__PURE__ */ jsx6("div", { className: preview, children: /* @__PURE__ */ jsx6("div", { className: content2, children: /* @__PURE__ */ jsx6("div", { className: title2, children: url }) }) })
475
- )
510
+ children: renderPreview()
476
511
  }
477
512
  );
478
513
  };
479
514
  var LinkPreview_default = LinkPreview;
480
515
 
481
516
  // src/components/Renderer/components/MemoizedComponents.tsx
482
- var MemoizedRichText = memo(RichTexts_default, (prev, next) => {
483
- return JSON.stringify(prev.richTexts) === JSON.stringify(next.richTexts);
517
+ var areRichTextsEqual = (prev, next) => {
518
+ if (prev === next) return true;
519
+ if (!prev || !next) return prev === next;
520
+ if (prev.length !== next.length) return false;
521
+ return prev.every(
522
+ (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
523
+ );
524
+ };
525
+ var MemoizedRichText = memo3(RichTexts_default, (prev, next) => {
526
+ return areRichTextsEqual(prev.richTexts, next.richTexts);
484
527
  });
485
- var MemoizedImage = memo(Image_default, (prev, next) => {
486
- return prev.src === next.src && prev.alt === next.alt && JSON.stringify(prev.caption) === JSON.stringify(next.caption);
528
+ var MemoizedImage = memo3(Image_default, (prev, next) => {
529
+ return prev.src === next.src && prev.alt === next.alt && areRichTextsEqual(prev.caption, next.caption);
487
530
  });
488
- var MemoizedBookmark = memo(Bookmark_default, (prev, next) => {
531
+ var MemoizedBookmark = memo3(Bookmark_default, (prev, next) => {
489
532
  return prev.url === next.url;
490
533
  });
491
- var MemoizedLinkPreview = memo(
534
+ var MemoizedLinkPreview = memo3(
492
535
  LinkPreview_default,
493
536
  (prev, next) => {
494
537
  return prev.url === next.url;
@@ -497,7 +540,7 @@ var MemoizedLinkPreview = memo(
497
540
 
498
541
  // src/components/Renderer/components/List/ListItemGroup.tsx
499
542
  import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
500
- var RecursiveListItem = ({ block, renderBlock }) => {
543
+ var RecursiveListItem = memo4(({ block, renderBlock }) => {
501
544
  let content3;
502
545
  if (block.type === "bulleted_list_item") {
503
546
  content3 = block.bulleted_list_item;
@@ -557,8 +600,9 @@ var RecursiveListItem = ({ block, renderBlock }) => {
557
600
  /* @__PURE__ */ jsx7(MemoizedRichText, { richTexts }),
558
601
  renderedChildren.length > 0 && renderedChildren
559
602
  ] });
560
- };
561
- var ListGroup = ({ blocks, type, renderBlock }) => {
603
+ });
604
+ RecursiveListItem.displayName = "RecursiveListItem";
605
+ var ListGroup = memo4(({ blocks, type, renderBlock }) => {
562
606
  if (blocks.length === 0) return null;
563
607
  return /* @__PURE__ */ jsx7(
564
608
  List,
@@ -577,10 +621,14 @@ var ListGroup = ({ blocks, type, renderBlock }) => {
577
621
  ))
578
622
  }
579
623
  );
580
- };
624
+ });
625
+ ListGroup.displayName = "ListGroup";
626
+
627
+ // src/components/Renderer/components/Block/BlockRenderer.tsx
628
+ import { memo as memo13 } from "react";
581
629
 
582
630
  // src/components/Renderer/components/Code/CodeBlock.tsx
583
- import { useMemo } from "react";
631
+ import { useMemo, memo as memo5 } from "react";
584
632
 
585
633
  // src/components/Renderer/components/Code/styles.css.ts
586
634
  var codeBlock = "styles_codeBlock__1qn42yc0";
@@ -609,7 +657,7 @@ var renderToken = (token, i) => {
609
657
  }
610
658
  return /* @__PURE__ */ jsx8("span", { className: `token ${token.type}`, children: tokenContent }, i);
611
659
  };
612
- var CodeBlock = ({ code, language, caption: caption2 }) => {
660
+ var CodeBlock = memo5(({ code, language, caption: caption2 }) => {
613
661
  const tokens = useMemo(() => {
614
662
  const prismLanguage = Prism.languages[language] || Prism.languages.plaintext;
615
663
  return Prism.tokenize(code, prismLanguage);
@@ -618,9 +666,13 @@ var CodeBlock = ({ code, language, caption: caption2 }) => {
618
666
  /* @__PURE__ */ jsx8("pre", { className: `${codeBlock} language-${language}`, children: /* @__PURE__ */ jsx8("code", { className: `language-${language}`, children: tokens.map((token, i) => renderToken(token, i)) }) }),
619
667
  caption2 && caption2.length > 0 && /* @__PURE__ */ jsx8("figcaption", { children: /* @__PURE__ */ jsx8(MemoizedRichText, { richTexts: caption2 }) })
620
668
  ] });
621
- };
669
+ });
670
+ CodeBlock.displayName = "CodeBlock";
622
671
  var CodeBlock_default = CodeBlock;
623
672
 
673
+ // src/components/Renderer/components/Typography/Typography.tsx
674
+ import { memo as memo6 } from "react";
675
+
624
676
  // src/components/Renderer/components/Typography/styles.css.ts
625
677
  var heading1 = "styles_heading1__90a95g1";
626
678
  var heading2 = "styles_heading2__90a95g2";
@@ -629,34 +681,25 @@ var paragraph = "styles_paragraph__90a95g0";
629
681
 
630
682
  // src/components/Renderer/components/Typography/Typography.tsx
631
683
  import { jsx as jsx9 } from "react/jsx-runtime";
632
- var Paragraph = ({
633
- className,
634
- children,
635
- ...props
636
- }) => {
684
+ var Paragraph = memo6(({ children, ...props }) => {
637
685
  return /* @__PURE__ */ jsx9("p", { className: paragraph, ...props, children });
638
- };
639
- var Heading1 = ({
640
- className,
641
- children,
642
- ...props
643
- }) => {
686
+ });
687
+ Paragraph.displayName = "Paragraph";
688
+ var Heading1 = memo6(({ children, ...props }) => {
644
689
  return /* @__PURE__ */ jsx9("h1", { className: heading1, ...props, children });
645
- };
646
- var Heading2 = ({
647
- className,
648
- children,
649
- ...props
650
- }) => {
690
+ });
691
+ Heading1.displayName = "Heading1";
692
+ var Heading2 = memo6(({ children, ...props }) => {
651
693
  return /* @__PURE__ */ jsx9("h2", { className: heading2, ...props, children });
652
- };
653
- var Heading3 = ({
654
- className,
655
- children,
656
- ...props
657
- }) => {
694
+ });
695
+ Heading2.displayName = "Heading2";
696
+ var Heading3 = memo6(({ children, ...props }) => {
658
697
  return /* @__PURE__ */ jsx9("h3", { className: heading3, ...props, children });
659
- };
698
+ });
699
+ Heading3.displayName = "Heading3";
700
+
701
+ // src/components/Renderer/components/Column/Column.tsx
702
+ import { memo as memo7 } from "react";
660
703
 
661
704
  // src/components/Renderer/components/Column/styles.css.ts
662
705
  var columnContainer = "styles_columnContainer__wle6we0";
@@ -664,30 +707,40 @@ var columnListContainer = "styles_columnListContainer__wle6we1";
664
707
 
665
708
  // src/components/Renderer/components/Column/Column.tsx
666
709
  import { jsx as jsx10 } from "react/jsx-runtime";
667
- var Column = ({ block }) => {
710
+ var Column = memo7(({ block }) => {
668
711
  if (!block || !block.children) return null;
669
712
  return /* @__PURE__ */ jsx10("div", { className: columnContainer, children: block.children.map((childBlock) => /* @__PURE__ */ jsx10(BlockRenderer_default, { block: childBlock, isColumn: true }, childBlock.id)) });
670
- };
713
+ });
714
+ Column.displayName = "Column";
671
715
  var Column_default = Column;
672
716
 
673
717
  // src/components/Renderer/components/Column/ColumnList.tsx
718
+ import { memo as memo8 } from "react";
674
719
  import { jsx as jsx11 } from "react/jsx-runtime";
675
- var ColumnList = ({ block }) => {
720
+ var ColumnList = memo8(({ block }) => {
676
721
  if (!block || !block.children) return null;
677
722
  return /* @__PURE__ */ jsx11("div", { className: columnListContainer, children: block.children.map((column) => /* @__PURE__ */ jsx11(Column_default, { block: column }, column.id)) });
678
- };
723
+ });
724
+ ColumnList.displayName = "ColumnList";
679
725
  var ColumnList_default = ColumnList;
680
726
 
727
+ // src/components/Renderer/components/Quote/Quote.tsx
728
+ import { memo as memo9 } from "react";
729
+
681
730
  // src/components/Renderer/components/Quote/styles.css.ts
682
731
  var container = "styles_container__mra9n0";
683
732
 
684
733
  // src/components/Renderer/components/Quote/Quote.tsx
685
734
  import { jsx as jsx12 } from "react/jsx-runtime";
686
- var Quote = ({ richTexts, tabIndex }) => {
735
+ var Quote = memo9(({ richTexts, tabIndex }) => {
687
736
  return /* @__PURE__ */ jsx12("blockquote", { className: container, tabIndex, children: /* @__PURE__ */ jsx12(MemoizedRichText, { richTexts }) });
688
- };
737
+ });
738
+ Quote.displayName = "Quote";
689
739
  var Quote_default = Quote;
690
740
 
741
+ // src/components/Renderer/components/Table/Table.tsx
742
+ import { memo as memo11 } from "react";
743
+
691
744
  // src/components/Renderer/components/Table/styles.css.ts
692
745
  var firstCell = "styles_firstCell__1rvbzfo4";
693
746
  var hasRowHeader = "styles_hasRowHeader__1rvbzfo6";
@@ -698,8 +751,9 @@ var tableCell = "styles_tableCell__1rvbzfo3";
698
751
  var tableContainer = "styles_tableContainer__1rvbzfo0";
699
752
 
700
753
  // src/components/Renderer/components/Table/TableRow.tsx
754
+ import { memo as memo10 } from "react";
701
755
  import { jsx as jsx13 } from "react/jsx-runtime";
702
- var TableRow = ({
756
+ var TableRow = memo10(({
703
757
  rowBlock,
704
758
  cellClassName = "",
705
759
  rowHeaderIndex = -1
@@ -725,12 +779,13 @@ var TableRow = ({
725
779
  `${rowBlock.id}-cell-${index}`
726
780
  );
727
781
  }) });
728
- };
782
+ });
783
+ TableRow.displayName = "TableRow";
729
784
  var TableRow_default = TableRow;
730
785
 
731
786
  // src/components/Renderer/components/Table/Table.tsx
732
787
  import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
733
- var Table = ({ block }) => {
788
+ var Table = memo11(({ block }) => {
734
789
  if (!block.table || !block.children) {
735
790
  return null;
736
791
  }
@@ -752,7 +807,8 @@ var Table = ({ block }) => {
752
807
  );
753
808
  }) })
754
809
  ] }) }) });
755
- };
810
+ });
811
+ Table.displayName = "Table";
756
812
  var Table_default = Table;
757
813
 
758
814
  // src/components/Renderer/components/Table/index.ts
@@ -804,6 +860,9 @@ var Toggle = ({ block }) => {
804
860
  };
805
861
  var Toggle_default = Toggle;
806
862
 
863
+ // src/components/Renderer/components/Video/Video.tsx
864
+ import { memo as memo12 } from "react";
865
+
807
866
  // src/components/Renderer/components/Video/styles.css.ts
808
867
  var videoCaption = "styles_videoCaption__15b9vkb2";
809
868
  var videoContainer = "styles_videoContainer__15b9vkb0";
@@ -811,7 +870,7 @@ var videoPlayer = "styles_videoPlayer__15b9vkb1";
811
870
 
812
871
  // src/components/Renderer/components/Video/Video.tsx
813
872
  import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
814
- var Video = ({ block }) => {
873
+ var Video = memo12(({ block }) => {
815
874
  if (block.type !== "video" || !block.video) {
816
875
  return null;
817
876
  }
@@ -840,19 +899,19 @@ var Video = ({ block }) => {
840
899
  ] }),
841
900
  caption2 && caption2.length > 0 && /* @__PURE__ */ jsx16("figcaption", { className: videoCaption, children: caption2.map((c, i) => /* @__PURE__ */ jsx16("span", { children: c.plain_text }, i)) })
842
901
  ] });
843
- };
902
+ });
903
+ Video.displayName = "Video";
904
+ var YOUTUBE_SHORT_URL_REGEX = /youtu\.be\/([a-zA-Z0-9_-]+)/;
905
+ var YOUTUBE_WATCH_URL_REGEX = /youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/;
844
906
  var getVideoEmbedUrl = (url) => {
845
907
  if (!url) return "";
846
- if (url.includes("youtu.be/")) {
847
- const videoId = url.split("youtu.be/")[1]?.split("?")[0];
848
- if (videoId) {
849
- return `https://www.youtube.com/embed/${videoId}`;
850
- }
851
- } else if (url.includes("youtube.com/watch?v=")) {
852
- const videoId = url.split("watch?v=")[1]?.split("&")[0];
853
- if (videoId) {
854
- return `https://www.youtube.com/embed/${videoId}`;
855
- }
908
+ const shortMatch = url.match(YOUTUBE_SHORT_URL_REGEX);
909
+ if (shortMatch?.[1]) {
910
+ return `https://www.youtube.com/embed/${shortMatch[1]}`;
911
+ }
912
+ const watchMatch = url.match(YOUTUBE_WATCH_URL_REGEX);
913
+ if (watchMatch?.[1]) {
914
+ return `https://www.youtube.com/embed/${watchMatch[1]}`;
856
915
  }
857
916
  return url;
858
917
  };
@@ -860,7 +919,7 @@ var Video_default = Video;
860
919
 
861
920
  // src/components/Renderer/components/Block/BlockRenderer.tsx
862
921
  import { jsx as jsx17 } from "react/jsx-runtime";
863
- var BlockRenderer = ({ block, isColumn = false }) => {
922
+ var BlockRenderer = memo13(({ block, isColumn = false }) => {
864
923
  if (!block) return null;
865
924
  switch (block.type) {
866
925
  case "link_preview":
@@ -916,7 +975,8 @@ var BlockRenderer = ({ block, isColumn = false }) => {
916
975
  default:
917
976
  return null;
918
977
  }
919
- };
978
+ });
979
+ BlockRenderer.displayName = "BlockRenderer";
920
980
  var BlockRenderer_default = BlockRenderer;
921
981
 
922
982
  // src/components/Title/index.tsx
@@ -926,9 +986,6 @@ var Title = ({ title: title3 }) => {
926
986
  };
927
987
  var Title_default = Title;
928
988
 
929
- // src/components/Cover/index.tsx
930
- import { useState as useState4, useRef as useRef2, useEffect as useEffect3 } from "react";
931
-
932
989
  // src/components/Cover/styles.css.ts
933
990
  import { createRuntimeFn as _7a4684 } from "@vanilla-extract/recipes/createRuntimeFn";
934
991
  var coverContainer = "styles_coverContainer__p0cp8d0";
@@ -938,17 +995,7 @@ var skeletonWrapper2 = _7a4684({ defaultClassName: "styles_skeletonWrapper__p0cp
938
995
  // src/components/Cover/index.tsx
939
996
  import { jsx as jsx19, jsxs as jsxs9 } from "react/jsx-runtime";
940
997
  var Cover = ({ src, alt }) => {
941
- const [isLoaded, setIsLoaded] = useState4(false);
942
- const imgRef = useRef2(null);
943
- useEffect3(() => {
944
- const img = imgRef.current;
945
- if (img && img.complete && img.naturalHeight !== 0) {
946
- setIsLoaded(true);
947
- }
948
- }, [src]);
949
- const handleLoad = () => {
950
- setIsLoaded(true);
951
- };
998
+ const { isLoaded, imgRef, handleLoad } = useImageLoad(src);
952
999
  return /* @__PURE__ */ jsxs9("div", { className: coverContainer, children: [
953
1000
  /* @__PURE__ */ jsx19("div", { className: skeletonWrapper2({ isLoaded }), children: /* @__PURE__ */ jsx19(Skeleton_default, { variant: "image", isLoading: !isLoaded }) }),
954
1001
  /* @__PURE__ */ jsx19(
@@ -975,8 +1022,12 @@ var lightTheme = "theme_lightTheme__sq3jkb1m";
975
1022
 
976
1023
  // src/components/Renderer/index.tsx
977
1024
  import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs10 } from "react/jsx-runtime";
978
- var Renderer = memo2(({ blocks, isDarkMode = false, title: title3, cover }) => {
1025
+ var Renderer = memo14(({ blocks, isDarkMode = false, title: title3, cover }) => {
979
1026
  const theme = isDarkMode ? darkTheme : lightTheme;
1027
+ const renderBlock = useCallback2(
1028
+ (childBlock) => /* @__PURE__ */ jsx20(BlockRenderer_default, { block: childBlock }),
1029
+ []
1030
+ );
980
1031
  const renderedBlocks = useMemo2(() => {
981
1032
  const result = [];
982
1033
  for (let i = 0; i < blocks.length; i++) {
@@ -999,7 +1050,7 @@ var Renderer = memo2(({ blocks, isDarkMode = false, title: title3, cover }) => {
999
1050
  {
1000
1051
  blocks: listItems,
1001
1052
  type: listItemType,
1002
- renderBlock: (childBlock) => /* @__PURE__ */ jsx20(BlockRenderer_default, { block: childBlock })
1053
+ renderBlock
1003
1054
  },
1004
1055
  block.id
1005
1056
  )
@@ -1016,7 +1067,7 @@ var Renderer = memo2(({ blocks, isDarkMode = false, title: title3, cover }) => {
1016
1067
  }
1017
1068
  }
1018
1069
  return result;
1019
- }, [blocks]);
1070
+ }, [blocks, renderBlock]);
1020
1071
  return /* @__PURE__ */ jsxs10(Fragment4, { children: [
1021
1072
  cover && /* @__PURE__ */ jsx20(Cover_default, { src: cover, alt: title3 || "Notion page content" }),
1022
1073
  /* @__PURE__ */ jsxs10(