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