From 4e7a96c5a831882463802cdd4f84fe1464969cb0 Mon Sep 17 00:00:00 2001
From: Armand Philippot <git@armandphilippot.com>
Date: Wed, 23 Mar 2022 12:29:43 +0100
Subject: refactor: use formatjs swc plugin

I'm not able to configure SWC plugins in Next.js so to make it works,
all translation must have an id.
---
 src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx')

diff --git a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
index 6f42f86..9707097 100644
--- a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
+++ b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
@@ -37,6 +37,7 @@ const PrismThemeToggle = () => {
       label={intl.formatMessage({
         defaultMessage: 'Code blocks:',
         description: 'PrismThemeToggle: toggle label',
+        id: 'w0UfY0',
       })}
       leftChoice={<SunIcon />}
       rightChoice={<MoonIcon />}
-- 
cgit v1.2.3


From 9226671f49b507ce6f71e6e2c3621014f05f74e9 Mon Sep 17 00:00:00 2001
From: Armand Philippot <git@armandphilippot.com>
Date: Wed, 23 Mar 2022 22:05:30 +0100
Subject: refactor: load prism plugins without babel

---
 .babelrc                                           |  52 -------
 src/components/MDX/CodeBlock/CodeBlock.tsx         |  81 +++++++---
 .../Settings/PrismThemeToggle/PrismThemeToggle.tsx |   2 +-
 src/content                                        |   2 +-
 src/pages/_app.tsx                                 |   2 +-
 src/pages/article/[slug].tsx                       |  58 ++++++--
 src/pages/projet/[slug].tsx                        |   1 +
 src/styles/vendors/_prism.scss                     |  28 +---
 src/ts/types/prism.ts                              |  51 +++++++
 src/utils/helpers/prism.ts                         |  21 ++-
 src/utils/providers/prism-theme.tsx                | 165 +++++++++++++++++++++
 src/utils/providers/prism.tsx                      | 161 --------------------
 12 files changed, 338 insertions(+), 286 deletions(-)
 delete mode 100644 .babelrc
 create mode 100644 src/ts/types/prism.ts
 create mode 100644 src/utils/providers/prism-theme.tsx
 delete mode 100644 src/utils/providers/prism.tsx

(limited to 'src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx')

diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index 657e285..0000000
--- a/.babelrc
+++ /dev/null
@@ -1,52 +0,0 @@
-{
-  "presets": ["next/babel"],
-  "plugins": [
-    [
-      "prismjs",
-      {
-        "languages": [
-          "apacheconf",
-          "bash",
-          "css",
-          "diff",
-          "docker",
-          "editorconfig",
-          "ejs",
-          "git",
-          "html",
-          "ignore",
-          "ini",
-          "javascript",
-          "jsdoc",
-          "json",
-          "jsx",
-          "makefile",
-          "markup",
-          "php",
-          "phpdoc",
-          "regex",
-          "scss",
-          "shell-session",
-          "smarty",
-          "tcl",
-          "toml",
-          "tsx",
-          "twig",
-          "yaml"
-        ],
-        "plugins": [
-          "command-line",
-          "copy-to-clipboard",
-          "diff-highlight",
-          "inline-color",
-          "line-highlight",
-          "line-numbers",
-          "match-braces",
-          "normalize-whitespace",
-          "show-language",
-          "toolbar"
-        ]
-      }
-    ]
-  ]
-}
diff --git a/src/components/MDX/CodeBlock/CodeBlock.tsx b/src/components/MDX/CodeBlock/CodeBlock.tsx
index 69f0124..c330063 100644
--- a/src/components/MDX/CodeBlock/CodeBlock.tsx
+++ b/src/components/MDX/CodeBlock/CodeBlock.tsx
@@ -1,28 +1,25 @@
+import {
+  PrismDefaultPlugins,
+  PrismLanguages,
+  PrismPlugins,
+} from '@ts/types/prism';
+import { usePrismTheme } from '@utils/providers/prism-theme';
 import { useRouter } from 'next/router';
 import Prism from 'prismjs';
-import { ReactChildren, useEffect } from 'react';
+import { useCallback, useEffect, useMemo } from 'react';
 import { useIntl } from 'react-intl';
-import '@utils/plugins/prism-color-scheme';
-import { usePrismTheme } from '@utils/providers/prism';
 
 const CodeBlock = ({
-  className,
-  children,
+  code,
+  language,
+  plugins,
 }: {
-  className: string;
-  children: ReactChildren;
+  code: string;
+  language: PrismLanguages;
+  plugins: PrismPlugins[];
 }) => {
-  const classNames = className.split('+');
-  const languageClass = classNames.find((name: string) =>
-    name.startsWith('language-')
-  );
   const intl = useIntl();
   const router = useRouter();
-
-  useEffect(() => {
-    Prism.highlightAll();
-  }, []);
-
   const { setCodeBlocks } = usePrismTheme();
 
   useEffect(() => {
@@ -32,6 +29,46 @@ const CodeBlock = ({
     setCodeBlocks(allPre);
   }, [setCodeBlocks, router.asPath]);
 
+  const defaultPlugins: PrismDefaultPlugins[] = useMemo(
+    () => [
+      'autoloader',
+      'toolbar',
+      'show-language',
+      'copy-to-clipboard',
+      'color-scheme',
+      'match-braces',
+      'normalize-whitespace',
+    ],
+    []
+  );
+
+  const loadPrismPlugins = useCallback(
+    async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => {
+      for (const plugin of prismPlugins) {
+        try {
+          if (plugin === 'color-scheme') {
+            await import(`@utils/plugins/prism-${plugin}`);
+          } else {
+            await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`);
+
+            if (plugin === 'autoloader')
+              Prism.plugins.autoloader.languages_path = '/prism/';
+          }
+        } catch (error) {
+          console.error('CodeBlock: an error occurred with Prism.');
+          console.error(error);
+        }
+      }
+    },
+    []
+  );
+
+  useEffect(() => {
+    loadPrismPlugins([...defaultPlugins, ...plugins]).then(() => {
+      Prism.highlightAll();
+    });
+  }, [loadPrismPlugins, defaultPlugins, plugins]);
+
   const copyText = intl.formatMessage({
     defaultMessage: 'Copy',
     description: 'Prism: copy button text (no clicked)',
@@ -58,18 +95,20 @@ const CodeBlock = ({
     id: 'Ua2g2p',
   });
 
+  const defaultPluginsClasses = 'match-braces';
+  const pluginsClasses = plugins.join(' ');
+
   return (
-    <div
+    <pre
+      className={`language-${language} ${defaultPluginsClasses} ${pluginsClasses}`}
       data-prismjs-copy={copyText}
       data-prismjs-copy-success={copiedText}
       data-prismjs-copy-error={errorText}
       data-prismjs-color-scheme-dark={darkTheme}
       data-prismjs-color-scheme-light={lightTheme}
     >
-      <pre className={classNames.join(' ')}>
-        <code className={languageClass}>{children}</code>
-      </pre>
-    </div>
+      <code className={`language-${language}`}>{code}</code>
+    </pre>
   );
 };
 
diff --git a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
index 9707097..20ad267 100644
--- a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
+++ b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
@@ -1,7 +1,7 @@
 import { Toggle } from '@components/FormElements';
 import { MoonIcon, SunIcon } from '@components/Icons';
 import Spinner from '@components/Spinner/Spinner';
-import { usePrismTheme } from '@utils/providers/prism';
+import { usePrismTheme } from '@utils/providers/prism-theme';
 import { useEffect, useState } from 'react';
 import { useIntl } from 'react-intl';
 
diff --git a/src/content b/src/content
index b5aa522..52c97a4 160000
--- a/src/content
+++ b/src/content
@@ -1 +1 @@
-Subproject commit b5aa522476547db4efa2d6c8e774ca0422ef6547
+Subproject commit 52c97a48f39ef0de9a61d2cf120fae2c70790555
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index e8c00de..84c2469 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -1,7 +1,7 @@
 import { AppPropsWithLayout } from '@ts/types/app';
 import { settings } from '@utils/config';
 import { AckeeProvider } from '@utils/providers/ackee';
-import { PrismThemeProvider } from '@utils/providers/prism';
+import { PrismThemeProvider } from '@utils/providers/prism-theme';
 import { ThemeProvider } from 'next-themes';
 import { useRouter } from 'next/router';
 import { IntlProvider } from 'react-intl';
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
index 41b84b6..27a6f7b 100644
--- a/src/pages/article/[slug].tsx
+++ b/src/pages/article/[slug].tsx
@@ -14,21 +14,20 @@ import {
 import styles from '@styles/pages/Page.module.scss';
 import { NextPageWithLayout } from '@ts/types/app';
 import { ArticleMeta, ArticleProps } from '@ts/types/articles';
+import { PrismDefaultPlugins, PrismPlugins } from '@ts/types/prism';
 import { settings } from '@utils/config';
 import { getFormattedPaths } from '@utils/helpers/format';
 import { loadTranslation } from '@utils/helpers/i18n';
 import { addPrismClasses } from '@utils/helpers/prism';
-import { usePrismTheme } from '@utils/providers/prism';
 import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
 import Head from 'next/head';
 import { useRouter } from 'next/router';
-import { highlightAll } from 'prismjs';
+import Script from 'next/script';
+import Prism from 'prismjs';
 import { ParsedUrlQuery } from 'querystring';
-import { useEffect } from 'react';
+import { useCallback, useEffect, useMemo } from 'react';
 import { useIntl } from 'react-intl';
 import { Blog, BlogPosting, Graph, WebPage } from 'schema-dts';
-import '@utils/plugins/prism-color-scheme';
-import Script from 'next/script';
 
 const SingleArticle: NextPageWithLayout<ArticleProps> = ({
   comments,
@@ -37,19 +36,48 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({
   const intl = useIntl();
   const router = useRouter();
 
-  useEffect(() => {
-    addPrismClasses();
-    highlightAll();
-  });
+  const loadPrismPlugins = useCallback(
+    async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => {
+      for (const plugin of prismPlugins) {
+        try {
+          if (plugin === 'color-scheme') {
+            await import(`@utils/plugins/prism-${plugin}`);
+          } else {
+            await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`);
 
-  const { setCodeBlocks } = usePrismTheme();
+            if (plugin === 'autoloader')
+              Prism.plugins.autoloader.languages_path = '/prism/';
+          }
+        } catch (error) {
+          console.error('Article: an error occurred with Prism.');
+          console.error(error);
+        }
+      }
+    },
+    []
+  );
+
+  const plugins: (PrismDefaultPlugins | PrismPlugins)[] = useMemo(
+    () => [
+      'autoloader',
+      'toolbar',
+      'show-language',
+      'copy-to-clipboard',
+      'color-scheme',
+      'command-line',
+      'line-numbers',
+      'match-braces',
+      'normalize-whitespace',
+    ],
+    []
+  );
 
   useEffect(() => {
-    const allPre: NodeListOf<HTMLPreElement> = document.querySelectorAll(
-      'pre[data-prismjs-color-scheme-current]'
-    );
-    setCodeBlocks(allPre);
-  }, [setCodeBlocks, router.asPath]);
+    loadPrismPlugins(plugins).then(() => {
+      addPrismClasses();
+      Prism.highlightAll();
+    });
+  }, [plugins, loadPrismPlugins]);
 
   if (router.isFallback) return <Spinner />;
 
diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx
index b9a8f39..1f09fed 100644
--- a/src/pages/projet/[slug].tsx
+++ b/src/pages/projet/[slug].tsx
@@ -41,6 +41,7 @@ const Project: NextPageWithLayout<ProjectProps> = ({
   };
 
   const components: NestedMDXComponents = {
+    CodeBlock: (props) => CodeBlock(props),
     Gallery: (props) => Gallery(props),
     Image: (props) => ResponsiveImage({ caption: props.caption, ...props }),
     Link: (props) => Link(props),
diff --git a/src/styles/vendors/_prism.scss b/src/styles/vendors/_prism.scss
index 2882835..7c05c9f 100644
--- a/src/styles/vendors/_prism.scss
+++ b/src/styles/vendors/_prism.scss
@@ -43,18 +43,6 @@
   }
 
   .toolbar-item:nth-child(1) {
-    grid-column: 2;
-    grid-row: 1;
-    margin: 0 var(--spacing-2xs);
-
-    @include mix.media("screen") {
-      @include mix.dimensions("2xs") {
-        order: 2;
-      }
-    }
-  }
-
-  .toolbar-item:nth-child(2) {
     grid-column: 1;
     grid-row: 1 / 3;
     margin-right: auto;
@@ -64,24 +52,18 @@
     color: var(--color-primary-darker);
     font-size: var(--font-size-sm);
     font-weight: 600;
+  }
 
-    @include mix.media("screen") {
-      @include mix.dimensions("2xs") {
-        order: 1;
-      }
-    }
+  .toolbar-item:nth-child(2) {
+    grid-column: 2;
+    grid-row: 1;
+    margin: 0 var(--spacing-2xs);
   }
 
   .toolbar-item:nth-child(3) {
     grid-column: 2;
     grid-row: 2;
     margin: 0 var(--spacing-2xs);
-
-    @include mix.media("screen") {
-      @include mix.dimensions("2xs") {
-        order: 3;
-      }
-    }
   }
 }
 
diff --git a/src/ts/types/prism.ts b/src/ts/types/prism.ts
new file mode 100644
index 0000000..663bc08
--- /dev/null
+++ b/src/ts/types/prism.ts
@@ -0,0 +1,51 @@
+export type PrismLanguages =
+  | 'apacheconf'
+  | 'bash'
+  | 'css'
+  | 'diff'
+  | 'docker'
+  | 'editorconfig'
+  | 'ejs'
+  | 'git'
+  | 'graphql'
+  | 'html'
+  | 'ignore'
+  | 'ini'
+  | 'javascript'
+  | 'jsdoc'
+  | 'json'
+  | 'jsx'
+  | 'makefile'
+  | 'markup'
+  | 'php'
+  | 'phpdoc'
+  | 'regex'
+  | 'scss'
+  | 'shell-session'
+  | 'smarty'
+  | 'tcl'
+  | 'toml'
+  | 'tsx'
+  | 'twig'
+  | 'yaml';
+
+export type PrismDefaultPlugins =
+  | 'autoloader'
+  | 'color-scheme'
+  | 'copy-to-clipboard'
+  | 'match-braces'
+  | 'normalize-whitespace'
+  | 'show-language'
+  | 'toolbar';
+
+export type PrismPlugins =
+  | 'command-line'
+  | 'diff-highlight'
+  | 'inline-color'
+  | 'line-highlight'
+  | 'line-numbers';
+
+export type PrismProviderProps = {
+  language: PrismLanguages;
+  plugins: PrismPlugins[];
+};
diff --git a/src/utils/helpers/prism.ts b/src/utils/helpers/prism.ts
index bc84c91..a5f5787 100644
--- a/src/utils/helpers/prism.ts
+++ b/src/utils/helpers/prism.ts
@@ -17,19 +17,18 @@ export const addPrismClasses = () => {
   const preTags = document.getElementsByTagName('pre');
 
   Array.from(preTags).forEach((preTag) => {
-    if (
-      isLanguageBlock(preTag.classList) &&
-      !preTag.classList.contains('command-line') &&
-      !preTag.classList.contains('language-diff')
-    ) {
-      preTag.classList.add('line-numbers', 'match-braces');
-    }
+    if (!isLanguageBlock(preTag.classList)) return;
+
+    preTag.classList.add('match-braces');
 
-    if (
-      preTag.classList.contains('command-line') &&
-      preTag.classList.contains('filter-output')
-    ) {
+    if (preTag.classList.contains('filter-output')) {
       preTag.setAttribute('data-filter-output', '#output#');
     }
+
+    if (preTag.classList.contains('language-bash')) {
+      preTag.classList.add('command-line');
+    } else if (!preTag.classList.contains('language-diff')) {
+      preTag.classList.add('line-numbers');
+    }
   });
 };
diff --git a/src/utils/providers/prism-theme.tsx b/src/utils/providers/prism-theme.tsx
new file mode 100644
index 0000000..2ed8454
--- /dev/null
+++ b/src/utils/providers/prism-theme.tsx
@@ -0,0 +1,165 @@
+import { LocalStorage } from '@services/local-storage';
+import {
+  createContext,
+  FC,
+  useCallback,
+  useContext,
+  useEffect,
+  useState,
+} from 'react';
+
+export type PrismTheme = 'dark' | 'light' | 'system';
+export type ResolvedPrismTheme = 'dark' | 'light';
+
+export type UsePrismThemeProps = {
+  themes: PrismTheme[];
+  theme?: PrismTheme;
+  setTheme: (theme: PrismTheme) => void;
+  resolvedTheme?: ResolvedPrismTheme;
+  codeBlocks?: NodeListOf<HTMLPreElement>;
+  setCodeBlocks: (codeBlocks: NodeListOf<HTMLPreElement>) => void;
+};
+
+export type PrismThemeProviderProps = {
+  attribute?: string;
+  storageKey?: string;
+  themes?: PrismTheme[];
+};
+
+export const PrismThemeContext = createContext<UsePrismThemeProps>({
+  themes: ['dark', 'light', 'system'],
+  setTheme: (_) => {
+    // This is intentional.
+  },
+  setCodeBlocks: (_) => {
+    // This is intentional.
+  },
+});
+
+export const usePrismTheme = () => useContext(PrismThemeContext);
+
+const prefersDarkScheme = () => {
+  if (typeof window === 'undefined') return;
+
+  return (
+    window.matchMedia &&
+    window.matchMedia('(prefers-color-scheme: dark)').matches
+  );
+};
+
+const isValidTheme = (theme: string): boolean => {
+  return theme === 'dark' || theme === 'light' || theme === 'system';
+};
+
+const getTheme = (key: string): PrismTheme | undefined => {
+  if (typeof window === 'undefined') return undefined;
+  const storageValue = LocalStorage.get(key);
+
+  return storageValue && isValidTheme(storageValue)
+    ? (storageValue as PrismTheme)
+    : undefined;
+};
+
+export const PrismThemeProvider: FC<PrismThemeProviderProps> = ({
+  attribute = 'data-prismjs-color-scheme-current',
+  storageKey = 'prismjs-color-scheme',
+  themes = ['dark', 'light', 'system'],
+  children,
+}) => {
+  const getThemeFromSystem = useCallback(() => {
+    return prefersDarkScheme() ? 'dark' : 'light';
+  }, []);
+
+  const [prismTheme, setPrismTheme] = useState<PrismTheme>(
+    getTheme(storageKey) || 'system'
+  );
+
+  const updateTheme = (theme: PrismTheme) => {
+    setPrismTheme(theme);
+  };
+
+  useEffect(() => {
+    LocalStorage.set(storageKey, prismTheme);
+  }, [prismTheme, storageKey]);
+
+  const [resolvedTheme, setResolvedTheme] = useState<ResolvedPrismTheme>();
+
+  useEffect(() => {
+    if (prismTheme === 'dark' || prismTheme === 'light') {
+      setResolvedTheme(prismTheme);
+    } else {
+      setResolvedTheme(getThemeFromSystem());
+    }
+  }, [prismTheme, getThemeFromSystem]);
+
+  const updateResolvedTheme = useCallback(() => {
+    setResolvedTheme(getThemeFromSystem());
+  }, [getThemeFromSystem]);
+
+  useEffect(() => {
+    window
+      .matchMedia('(prefers-color-scheme: dark)')
+      .addEventListener('change', updateResolvedTheme);
+
+    return () =>
+      window
+        .matchMedia('(prefers-color-scheme: dark)')
+        .removeEventListener('change', updateResolvedTheme);
+  }, [updateResolvedTheme]);
+
+  const [preTags, setPreTags] = useState<NodeListOf<HTMLPreElement>>();
+
+  const updatePreTags = useCallback((tags: NodeListOf<HTMLPreElement>) => {
+    setPreTags(tags);
+  }, []);
+
+  const updatePreTagsAttribute = useCallback(() => {
+    preTags?.forEach((pre) => {
+      pre.setAttribute(attribute, prismTheme);
+    });
+  }, [attribute, preTags, prismTheme]);
+
+  useEffect(() => {
+    updatePreTagsAttribute();
+  }, [updatePreTagsAttribute, prismTheme]);
+
+  const listenAttributeChange = useCallback(
+    (pre: HTMLPreElement) => {
+      var observer = new MutationObserver(function (mutations) {
+        mutations.forEach((record) => {
+          var mutatedPre = record.target as HTMLPreElement;
+          var newTheme = mutatedPre.getAttribute(attribute) as PrismTheme;
+          setPrismTheme(newTheme);
+        });
+      });
+      observer.observe(pre, {
+        attributes: true,
+        attributeFilter: [attribute],
+      });
+    },
+    [attribute]
+  );
+
+  useEffect(() => {
+    if (!preTags) return;
+
+    preTags.forEach((pre) => {
+      listenAttributeChange(pre);
+    });
+  }, [preTags, listenAttributeChange]);
+
+  return (
+    <PrismThemeContext.Provider
+      value={{
+        themes,
+        theme: prismTheme,
+        setTheme: updateTheme,
+        codeBlocks: preTags,
+        setCodeBlocks: updatePreTags,
+        resolvedTheme,
+      }}
+    >
+      {children}
+    </PrismThemeContext.Provider>
+  );
+};
diff --git a/src/utils/providers/prism.tsx b/src/utils/providers/prism.tsx
deleted file mode 100644
index 7a4221d..0000000
--- a/src/utils/providers/prism.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-import { LocalStorage } from '@services/local-storage';
-import {
-  createContext,
-  FC,
-  useCallback,
-  useContext,
-  useEffect,
-  useState,
-} from 'react';
-
-export type PrismTheme = 'dark' | 'light' | 'system';
-export type ResolvedPrismTheme = 'dark' | 'light';
-
-export type UsePrismThemeProps = {
-  themes: PrismTheme[];
-  theme?: PrismTheme;
-  setTheme: (theme: PrismTheme) => void;
-  resolvedTheme?: ResolvedPrismTheme;
-  codeBlocks?: NodeListOf<HTMLPreElement>;
-  setCodeBlocks: (codeBlocks: NodeListOf<HTMLPreElement>) => void;
-};
-
-export type PrismThemeProviderProps = {
-  attribute?: string;
-  storageKey?: string;
-  themes?: PrismTheme[];
-};
-
-export const PrismThemeContext = createContext<UsePrismThemeProps>({
-  themes: ['dark', 'light', 'system'],
-  setTheme: (_) => {},
-  setCodeBlocks: (_) => {},
-});
-
-export const usePrismTheme = () => useContext(PrismThemeContext);
-
-const prefersDarkScheme = () => {
-  if (typeof window === 'undefined') return;
-
-  return (
-    window.matchMedia &&
-    window.matchMedia('(prefers-color-scheme: dark)').matches
-  );
-};
-
-const isValidTheme = (theme: string): boolean => {
-  return theme === 'dark' || theme === 'light' || theme === 'system';
-};
-
-const getTheme = (key: string): PrismTheme | undefined => {
-  if (typeof window === 'undefined') return undefined;
-  const storageValue = LocalStorage.get(key);
-
-  return storageValue && isValidTheme(storageValue)
-    ? (storageValue as PrismTheme)
-    : undefined;
-};
-
-export const PrismThemeProvider: FC<PrismThemeProviderProps> = ({
-  attribute = 'data-prismjs-color-scheme-current',
-  storageKey = 'prismjs-color-scheme',
-  themes = ['dark', 'light', 'system'],
-  children,
-}) => {
-  const getThemeFromSystem = useCallback(() => {
-    return prefersDarkScheme() ? 'dark' : 'light';
-  }, []);
-
-  const [prismTheme, setPrismTheme] = useState<PrismTheme>(
-    getTheme(storageKey) || 'system'
-  );
-
-  const updateTheme = (theme: PrismTheme) => {
-    setPrismTheme(theme);
-  };
-
-  useEffect(() => {
-    LocalStorage.set(storageKey, prismTheme);
-  }, [prismTheme, storageKey]);
-
-  const [resolvedTheme, setResolvedTheme] = useState<ResolvedPrismTheme>();
-
-  useEffect(() => {
-    if (prismTheme === 'dark' || prismTheme === 'light') {
-      setResolvedTheme(prismTheme);
-    } else {
-      setResolvedTheme(getThemeFromSystem());
-    }
-  }, [prismTheme, getThemeFromSystem]);
-
-  const updateResolvedTheme = useCallback(() => {
-    setResolvedTheme(getThemeFromSystem());
-  }, [getThemeFromSystem]);
-
-  useEffect(() => {
-    window
-      .matchMedia('(prefers-color-scheme: dark)')
-      .addEventListener('change', updateResolvedTheme);
-
-    return () =>
-      window
-        .matchMedia('(prefers-color-scheme: dark)')
-        .removeEventListener('change', updateResolvedTheme);
-  }, [updateResolvedTheme]);
-
-  const [preTags, setPreTags] = useState<NodeListOf<HTMLPreElement>>();
-
-  const updatePreTags = useCallback((tags: NodeListOf<HTMLPreElement>) => {
-    setPreTags(tags);
-  }, []);
-
-  const updatePreTagsAttribute = useCallback(() => {
-    preTags?.forEach((pre) => {
-      pre.setAttribute(attribute, prismTheme);
-    });
-  }, [attribute, preTags, prismTheme]);
-
-  useEffect(() => {
-    updatePreTagsAttribute();
-  }, [updatePreTagsAttribute, prismTheme]);
-
-  const listenAttributeChange = useCallback(
-    (pre: HTMLPreElement) => {
-      var observer = new MutationObserver(function (mutations) {
-        mutations.forEach((record) => {
-          var mutatedPre = record.target as HTMLPreElement;
-          var newTheme = mutatedPre.getAttribute(attribute) as PrismTheme;
-          setPrismTheme(newTheme);
-        });
-      });
-      observer.observe(pre, {
-        attributes: true,
-        attributeFilter: [attribute],
-      });
-    },
-    [attribute]
-  );
-
-  useEffect(() => {
-    if (!preTags) return;
-
-    preTags.forEach((pre) => {
-      listenAttributeChange(pre);
-    });
-  }, [preTags, listenAttributeChange]);
-
-  return (
-    <PrismThemeContext.Provider
-      value={{
-        themes,
-        theme: prismTheme,
-        setTheme: updateTheme,
-        codeBlocks: preTags,
-        setCodeBlocks: updatePreTags,
-        resolvedTheme,
-      }}
-    >
-      {children}
-    </PrismThemeContext.Provider>
-  );
-};
-- 
cgit v1.2.3

