// src/lib/hook.ts
import {
  useEffect,
  useMemo,
  useRef as useRef2,
  useState
} from "react";
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
import { guessEmbeddedLanguages } from "shiki/core";

// src/lib/utils.ts
import { useRef } from "react";
import { dequal } from "dequal";
var useStableValue = (value) => {
  const ref = useRef(value);
  if (value !== ref.current && !dequal(value, ref.current)) {
    ref.current = value;
  }
  return ref.current;
};
var throttleHighlighting = (performHighlight, timeoutControl, throttleMs) => {
  const now = Date.now();
  clearTimeout(timeoutControl.current.timeoutId);
  const delay = Math.max(0, timeoutControl.current.nextAllowedTime - now);
  timeoutControl.current.timeoutId = setTimeout(() => {
    performHighlight().catch(console.error);
    timeoutControl.current.nextAllowedTime = now + throttleMs;
  }, delay);
};

// src/lib/language.ts
import { isSpecialLang } from "shiki/core";
var FALLBACK_LANGUAGE = "plaintext";
var toArray = (value) => value == null ? [] : Array.isArray(value) ? value : [value];
var languageKey = (lang) => {
  if (lang == null) return null;
  if (typeof lang === "string") return `s:${lang}`;
  return `o:${lang.name}::${lang.scopeName}`;
};
var dedupeLanguages = (langs) => {
  const seen = /* @__PURE__ */ new Set();
  const deduped = [];
  for (const lang of langs) {
    const key = languageKey(lang);
    if (key == null || seen.has(key)) continue;
    seen.add(key);
    deduped.push(lang);
  }
  return deduped;
};
var isLoadableLanguage = (lang, bundledLanguages) => {
  if (lang == null) return false;
  if (typeof lang === "string") return lang in bundledLanguages;
  return typeof lang.name === "string" && typeof lang.scopeName === "string";
};
var resolveLoadedLanguage = (languageId, loadedLanguages) => isSpecialLang(languageId) || loadedLanguages.includes(languageId) ? languageId : FALLBACK_LANGUAGE;
var resolveLanguage = (lang, customLanguages, langAliases, preloadLanguages) => {
  const customLangs = toArray(customLanguages);
  const preloadLangs = toArray(preloadLanguages);
  const customMatchPool = [...customLangs, ...preloadLangs];
  let languageId = FALLBACK_LANGUAGE;
  let primaryLang;
  if (lang == null || typeof lang === "string" && !lang.trim()) {
    return {
      languageId,
      langsToLoad: dedupeLanguages([...preloadLangs, ...customLangs])
    };
  }
  if (typeof lang === "object") {
    languageId = lang.name;
    primaryLang = lang;
  } else {
    const lowerLang = lang.toLowerCase();
    const matches = (str) => str?.toLowerCase() === lowerLang;
    const customMatch = customMatchPool.find(
      (candidate) => typeof candidate === "object" && candidate != null && !!(matches(candidate.name) || matches(candidate.scopeName) || matches(candidate.scopeName?.split(".").pop()) || candidate.aliases?.some(matches) || candidate.fileTypes?.some(matches))
    );
    if (customMatch) {
      languageId = customMatch.name || lang;
      primaryLang = customMatch;
    } else if (langAliases?.[lang]) {
      languageId = langAliases[lang];
      primaryLang = langAliases[lang];
    } else {
      languageId = lang;
      primaryLang = lang;
    }
  }
  return {
    languageId,
    langsToLoad: dedupeLanguages([
      primaryLang,
      ...preloadLangs,
      ...customLangs
    ])
  };
};

// src/lib/theme.ts
var isTextmateTheme = (value) => typeof value === "object" && value !== null && "tokenColors" in value && Array.isArray(value.tokenColors);
function resolveTheme(themeInput) {
  const inputIsTextmateTheme = isTextmateTheme(themeInput);
  const isMultiThemeConfig = typeof themeInput === "object" && themeInput !== null && !inputIsTextmateTheme;
  const validMultiThemeObj = typeof themeInput === "object" && themeInput !== null && !inputIsTextmateTheme && Object.entries(themeInput).some(
    ([key, value]) => key.trim() !== "" && (typeof value === "string" && value.trim() !== "" || isTextmateTheme(value))
  );
  if (isMultiThemeConfig) {
    const themeId = validMultiThemeObj ? `multi-${Object.values(themeInput).map(
      (theme) => (typeof theme === "string" ? theme : theme?.name) || "custom"
    ).sort().join("-")}` : "multi-default";
    return {
      isMultiTheme: true,
      themeId,
      multiTheme: validMultiThemeObj ? themeInput : null,
      themesToLoad: validMultiThemeObj ? Object.values(themeInput) : []
    };
  }
  return {
    isMultiTheme: false,
    themeId: typeof themeInput === "string" ? themeInput : themeInput?.name || "custom",
    singleTheme: themeInput,
    themesToLoad: [themeInput]
  };
}

// src/lib/transformers.ts
function lineNumbersTransformer(startLine = 1) {
  return {
    name: "react-shiki:line-numbers",
    code(node) {
      this.addClassToHast(node, "has-line-numbers");
      this.addClassToHast(node, "rs-has-line-numbers");
      if (startLine !== 1) {
        const existingStyle = node.properties?.style || "";
        const newStyle = existingStyle ? `${existingStyle}; --line-start: ${startLine}` : `--line-start: ${startLine}`;
        node.properties = {
          ...node.properties,
          style: newStyle
        };
      }
    },
    line(node) {
      this.addClassToHast(node, "line-numbers");
      this.addClassToHast(node, "rs-line-number");
      return node;
    }
  };
}

// src/lib/options.ts
var DEFAULT_THEMES = {
  light: "github-light",
  dark: "github-dark"
};
var buildThemeOptions = (resolvedTheme, options) => {
  if (resolvedTheme.isMultiTheme) {
    return {
      themes: resolvedTheme.multiTheme ?? DEFAULT_THEMES,
      defaultColor: options.defaultColor,
      cssVariablePrefix: options.cssVariablePrefix
    };
  }
  return {
    theme: resolvedTheme.singleTheme ?? DEFAULT_THEMES.dark
  };
};
var buildTransformers = (options) => {
  const transformers = [...options.transformers || []];
  if (options.showLineNumbers) {
    transformers.push(lineNumbersTransformer(options.startingLineNumber));
  }
  return transformers;
};
var buildShikiOptions = ({
  languageId,
  resolvedTheme,
  options
}) => {
  const {
    defaultColor: _defaultColor,
    cssVariablePrefix: _cssVariablePrefix,
    showLineNumbers: _showLineNumbers,
    startingLineNumber: _startingLineNumber,
    transformers: _transformers,
    ...restOptions
  } = options;
  return {
    lang: languageId,
    ...buildThemeOptions(resolvedTheme, options),
    ...restOptions,
    transformers: buildTransformers(options)
  };
};

// src/lib/hook.ts
var getEmbeddedLanguages = (code, languageId, highlighter) => {
  const bundled = highlighter.getBundledLanguages();
  return guessEmbeddedLanguages(code, languageId).flatMap(
    (language) => bundled[language] ?? []
  );
};
var useShikiHighlighter = (code, lang, themeInput, options = {}, highlighterFactory) => {
  const [highlightedCode, setHighlightedCode] = useState(null);
  const stableLang = useStableValue(lang);
  const stableTheme = useStableValue(themeInput);
  const stableOpts = useStableValue(options);
  const { languageId, langsToLoad } = useMemo(
    () => resolveLanguage(
      stableLang,
      stableOpts.customLanguages,
      stableOpts.langAlias,
      stableOpts.preloadLanguages
    ),
    [
      stableLang,
      stableOpts.customLanguages,
      stableOpts.preloadLanguages,
      stableOpts.langAlias
    ]
  );
  const resolvedTheme = useMemo(
    () => resolveTheme(stableTheme),
    [stableTheme]
  );
  const { themesToLoad } = resolvedTheme;
  const timeoutControl = useRef2({
    nextAllowedTime: 0,
    timeoutId: void 0
  });
  const shikiOptions = useMemo(
    () => buildShikiOptions({
      languageId,
      resolvedTheme,
      options: stableOpts
    }),
    [languageId, resolvedTheme, stableOpts]
  );
  useEffect(() => {
    let isMounted = true;
    const highlightCode = async () => {
      if (!languageId) return;
      const highlighter = stableOpts.highlighter ? stableOpts.highlighter : await highlighterFactory(
        langsToLoad,
        themesToLoad,
        stableOpts.engine
      );
      if (!stableOpts.highlighter) {
        const embedded = getEmbeddedLanguages(
          code,
          languageId,
          highlighter
        );
        if (embedded.length > 0) {
          await highlighter.loadLanguage(...embedded);
        }
      }
      const langToUse = resolveLoadedLanguage(
        languageId,
        highlighter.getLoadedLanguages()
      );
      const finalOptions = { ...shikiOptions, lang: langToUse };
      if (isMounted) {
        const output = stableOpts.outputFormat === "html" ? highlighter.codeToHtml(code, finalOptions) : toJsxRuntime(highlighter.codeToHast(code, finalOptions), {
          jsx,
          jsxs,
          Fragment
        });
        setHighlightedCode(output);
      }
    };
    const { delay } = stableOpts;
    if (delay) {
      throttleHighlighting(highlightCode, timeoutControl, delay);
    } else {
      highlightCode().catch(console.error);
    }
    return () => {
      isMounted = false;
      clearTimeout(timeoutControl.current.timeoutId);
    };
  }, [
    code,
    shikiOptions,
    stableOpts.delay,
    stableOpts.highlighter,
    langsToLoad,
    themesToLoad
  ]);
  return highlightedCode;
};

// src/lib/plugins.ts
import { visit } from "unist-util-visit";
function rehypeInlineCodeProperty() {
  return (tree) => {
    visit(tree, "element", (node, _index, parent) => {
      if (node.tagName === "code" && parent.tagName !== "pre") {
        node.properties.inline = true;
      }
    });
  };
}
var isInlineCode = (node) => {
  const textContent = (node.children || []).filter((child) => child.type === "text").map((child) => child.value).join("");
  return !textContent.includes("\n");
};

// #style-inject:#style-inject
function styleInject(css, { insertAt } = {}) {
  if (!css || typeof document === "undefined") return;
  const head = document.head || document.getElementsByTagName("head")[0];
  const style = document.createElement("style");
  style.type = "text/css";
  if (insertAt === "top") {
    if (head.firstChild) {
      head.insertBefore(style, head.firstChild);
    } else {
      head.appendChild(style);
    }
  } else {
    head.appendChild(style);
  }
  if (style.styleSheet) {
    style.styleSheet.cssText = css;
  } else {
    style.appendChild(document.createTextNode(css));
  }
}

// src/styles/component.css
styleInject("@layer base {\n  .rs-root {\n    position: relative;\n  }\n  .rs-default-styles pre {\n    overflow: auto;\n    border-radius: 0.5rem;\n    padding-left: 1.5rem;\n    padding-right: 1.5rem;\n    padding-top: 1.25rem;\n    padding-bottom: 1.25rem;\n  }\n  .rs-language-label {\n    position: absolute;\n    right: 0.75rem;\n    top: 0.5rem;\n    font-family: monospace;\n    font-size: 0.75rem;\n    letter-spacing: -0.05em;\n    color: rgba(107, 114, 128, 0.85);\n  }\n}\n");

// src/styles/features.css
styleInject("@layer base {\n  .line-numbers::before,\n  .rs-line-number::before {\n    counter-increment: line-number;\n    content: counter(line-number);\n    display: inline-flex;\n    justify-content: flex-end;\n    align-items: flex-start;\n    box-sizing: content-box;\n    min-width: var(--rs-line-numbers-width, 2ch);\n    padding-left: var(--rs-line-numbers-padding-left, 2ch);\n    padding-right: var(--rs-line-numbers-padding-right, 2ch);\n    color: var(--rs-line-numbers-foreground, rgba(107, 114, 128, 0.6));\n    font-size: var(--rs-line-numbers-font-size, inherit);\n    font-weight: var(--rs-line-numbers-font-weight, inherit);\n    line-height: var(--rs-line-numbers-line-height, inherit);\n    font-family: var(--rs-line-numbers-font-family, inherit);\n    opacity: var(--rs-line-numbers-opacity, 1);\n    user-select: none;\n    pointer-events: none;\n  }\n  .has-line-numbers,\n  .rs-has-line-numbers {\n    counter-reset: line-number calc(var(--line-start, 1) - 1);\n    --rs-line-numbers-foreground: var(--line-numbers-foreground, rgba(107, 114, 128, 0.5));\n    --rs-line-numbers-width: var(--line-numbers-width, 2ch);\n    --rs-line-numbers-padding-left: var(--line-numbers-padding-left, 0ch);\n    --rs-line-numbers-padding-right: var(--line-numbers-padding-right, 2ch);\n    --rs-line-numbers-font-size: var(--line-numbers-font-size, inherit);\n    --rs-line-numbers-font-weight: var(--line-numbers-font-weight, inherit);\n    --rs-line-numbers-line-height: var(--line-numbers-line-height, inherit);\n    --rs-line-numbers-font-family: var(--line-numbers-font-family, inherit);\n    --rs-line-numbers-opacity: var(--line-numbers-opacity, 1);\n  }\n}\n");

// src/lib/component.tsx
import { clsx } from "clsx";
import { forwardRef } from "react";
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
var createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
  return forwardRef(
    ({
      language,
      theme,
      delay,
      transformers,
      defaultColor,
      cssVariablePrefix,
      addDefaultStyles = true,
      style,
      langStyle,
      className,
      langClassName,
      showLanguage = true,
      showLineNumbers = false,
      startingLineNumber = 1,
      children: code,
      as: Element = "pre",
      customLanguages,
      preloadLanguages,
      ...shikiOptions
    }, ref) => {
      const options = {
        delay,
        transformers,
        customLanguages,
        preloadLanguages,
        showLineNumbers,
        defaultColor,
        cssVariablePrefix,
        startingLineNumber,
        ...shikiOptions
      };
      const displayLanguageId = typeof language === "object" ? language.name || null : language?.trim() || null;
      const highlightedCode = useShikiHighlighterImpl(
        code,
        language,
        theme,
        options
      );
      const isHtmlOutput = typeof highlightedCode === "string";
      return /* @__PURE__ */ jsxs2(
        Element,
        {
          ref,
          "data-testid": "shiki-container",
          className: clsx(
            "rs-root",
            "not-prose",
            addDefaultStyles && "rs-default-styles",
            className
          ),
          style,
          id: "shiki-container",
          children: [
            showLanguage && displayLanguageId ? /* @__PURE__ */ jsx2(
              "span",
              {
                className: clsx(
                  "rs-language-label",
                  langClassName
                ),
                style: langStyle,
                id: "language-label",
                children: displayLanguageId
              }
            ) : null,
            isHtmlOutput ? /* @__PURE__ */ jsx2("div", { dangerouslySetInnerHTML: { __html: highlightedCode } }) : highlightedCode
          ]
        }
      );
    }
  );
};

export {
  isLoadableLanguage,
  useShikiHighlighter,
  rehypeInlineCodeProperty,
  isInlineCode,
  createShikiHighlighterComponent
};
//# sourceMappingURL=chunk-S5E5K22S.js.map