'use strict';

var react = require('react');
var react$1 = require('@emotion/react');
var ReactMarkdown = require('react-markdown');
var remarkGfm = require('remark-gfm');
var core = require('@timeui/core');
var jsxRuntime = require('@emotion/react/jsx-runtime');
var reactDom = require('react-dom');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);

// src/MarkdownViewer/MarkdownViewer.tsx
var CodeBlock = react.forwardRef(function CodeBlock2({ code, language = "tsx", title, isCopyable = true, disableHighlight = false, onCopy, ...rest }, ref) {
  const theme = react$1.useTheme();
  const i18n = core.useI18n();
  const [copied, setCopied] = react.useState(false);
  const [highlightedHtml, setHighlightedHtml] = react.useState(null);
  react.useEffect(() => {
    if (disableHighlight) {
      setHighlightedHtml(null);
      return;
    }
    let cancelled = false;
    (async () => {
      try {
        const shiki = await import('shiki');
        const highlighter = await shiki.getSingletonHighlighter({
          themes: ["github-light", "github-dark"],
          langs: [language]
        });
        const loaded = highlighter.getLoadedLanguages();
        if (!loaded.includes(language)) {
          await highlighter.loadLanguage(language);
        }
        const html = highlighter.codeToHtml(code, {
          lang: language,
          themes: { light: "github-light", dark: "github-dark" },
          defaultColor: theme.mode === "dark" ? "dark" : "light"
        });
        if (!cancelled) setHighlightedHtml(html);
      } catch (err) {
        if (process.env.NODE_ENV !== "production") {
          console.warn(
            "[TimeUI] CodeBlock: syntax highlighting unavailable \u2014 falling back to plain text.",
            err
          );
        }
        if (!cancelled) setHighlightedHtml(null);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [code, language, theme.mode, disableHighlight]);
  const handleCopy = react.useCallback(async () => {
    try {
      await navigator.clipboard.writeText(code);
      setCopied(true);
      onCopy?.(code);
      window.setTimeout(() => setCopied(false), 1500);
    } catch {
    }
  }, [code, onCopy]);
  const bg = theme.colors.bg.sunken;
  const fg = theme.colors.text.primary;
  const border = theme.colors.border.subtle;
  const fastDuration = theme.motion.duration.fast;
  const monoFont = theme.typography.fontFamily.mono;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      ref,
      ...rest,
      css: react$1.css`
        position: relative;
        margin: 16px 0;
        border-radius: 10px;
        border: 1px solid ${border};
        overflow: hidden;
        background: ${bg};
        font-family: ${monoFont};

        .shiki {
          margin: 0 !important;
          padding: 16px ${isCopyable ? 56 : 16}px 16px 16px;
          font-size: 13px !important;
          line-height: 1.7 !important;
          font-family: inherit !important;
          background: transparent !important;
          overflow: auto;
        }
        .shiki code {
          font-family: inherit;
        }
      `,
      children: [
        title && /* @__PURE__ */ jsxRuntime.jsxs(
          "div",
          {
            css: react$1.css`
            padding: 8px 14px;
            border-bottom: 1px solid ${border};
            font-size: 11px;
            letter-spacing: 0.04em;
            text-transform: uppercase;
            color: ${theme.colors.text.muted};
            display: flex;
            justify-content: space-between;
            align-items: center;
          `,
            children: [
              /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
              /* @__PURE__ */ jsxRuntime.jsx("span", { children: language })
            ]
          }
        ),
        highlightedHtml ? /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: highlightedHtml } }) : /* @__PURE__ */ jsxRuntime.jsx(
          "pre",
          {
            css: react$1.css`
            margin: 0;
            padding: 16px ${isCopyable ? 56 : 16}px 16px 16px;
            font-size: 13px;
            line-height: 1.7;
            color: ${fg};
            overflow: auto;
            font-family: inherit;
          `,
            children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code })
          }
        ),
        isCopyable && /* @__PURE__ */ jsxRuntime.jsx(
          "button",
          {
            type: "button",
            onClick: handleCopy,
            "aria-label": i18n.codeBlock.copyLabel,
            "data-copied": copied || void 0,
            css: react$1.css`
            position: absolute;
            top: ${title ? 44 : 10}px;
            right: 10px;
            padding: 4px 10px;
            font-family: inherit;
            font-size: 10px;
            letter-spacing: 0.04em;
            text-transform: uppercase;
            color: ${theme.colors.text.muted};
            background: transparent;
            border: 1px solid ${border};
            border-radius: 6px;
            cursor: pointer;
            transition:
              background ${fastDuration},
              color ${fastDuration},
              border-color ${fastDuration};
            @media (prefers-reduced-motion: reduce) {
              transition: none;
            }
            &:hover,
            &:focus-visible {
              background: ${theme.colors.bg.muted};
              color: ${theme.colors.text.primary};
              outline: none;
            }
            &:focus-visible {
              border-color: ${theme.colors.focus};
            }
          `,
            children: copied ? i18n.codeBlock.copied : i18n.codeBlock.copy
          }
        )
      ]
    }
  );
});
CodeBlock.displayName = "CodeBlock";
function createMarkdownViewerTypography(theme) {
  const isDark = theme.mode === "dark";
  const tableStripe = isDark ? theme.colors.bg.raised : theme.colors.bg.sunken;
  const tableHover = theme.colors.bg.muted;
  return react$1.css`
    color: ${theme.colors.text.primary};
    font-family: ${theme.typography.fontFamily.sans};
    font-size: ${theme.typography.fontSize.md};
    line-height: ${theme.typography.lineHeight.relaxed};
    word-break: break-word;

    /* :first-of-type 而非 :first-child —— Emotion SSR 会在内容前注入 <style>，
       :first-child 会先匹配到 <style> 触发 dev 警告。与 ChatMarkdown 保持一致。 */
    & > :first-of-type {
      margin-top: 0;
    }
    & > :last-child {
      margin-bottom: 0;
    }

    p {
      margin: 0 0 1em;
    }

    strong {
      font-weight: ${theme.typography.fontWeight.bold};
    }
    em {
      font-style: italic;
    }
    del {
      text-decoration: line-through;
      color: ${theme.colors.text.muted};
    }

    a {
      color: ${theme.colors.text.link};
      text-decoration: underline;
      text-underline-offset: 2px;
      text-decoration-thickness: 1px;
      transition: color 150ms ease;
      @media (prefers-reduced-motion: reduce) {
        transition: none;
      }
      &:hover {
        color: ${theme.colors.primary[600]};
        text-decoration-thickness: 2px;
      }
      &:focus-visible {
        outline: 2px solid ${theme.colors.focus};
        outline-offset: 2px;
        border-radius: 2px;
      }
    }

    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
      margin: 1.6em 0 0.6em;
      font-weight: ${theme.typography.fontWeight.bold};
      line-height: 1.3;
      color: ${theme.colors.text.primary};
      scroll-margin-top: 1em;
    }
    :is(h1, h2, h3, h4, h5, h6):first-of-type {
      margin-top: 0;
    }
    h1 {
      font-size: 2em;
      padding-bottom: 0.3em;
      border-bottom: 1px solid ${theme.colors.border.subtle};
    }
    h2 {
      font-size: 1.5em;
      padding-bottom: 0.25em;
      border-bottom: 1px solid ${theme.colors.border.subtle};
    }
    h3 {
      font-size: 1.25em;
    }
    h4 {
      font-size: 1.1em;
    }
    h5 {
      font-size: 1em;
    }
    h6 {
      font-size: 0.9em;
      color: ${theme.colors.text.secondary};
    }

    ul,
    ol {
      margin: 0 0 1em;
      padding-left: 1.6em;
    }
    li {
      margin: 0.25em 0;
    }
    li > p {
      margin: 0;
    }
    li > p + p {
      margin-top: 0.4em;
    }
    /* GFM task list 视觉更紧凑，复选框对齐文字基线。 */
    li.task-list-item,
    li:has(> input[type='checkbox']) {
      list-style: none;
      margin-left: -1.2em;
    }
    li > input[type='checkbox'] {
      margin-right: 0.5em;
      vertical-align: middle;
    }

    blockquote {
      margin: 0 0 1em;
      padding: 0.6em 1em;
      border-left: 4px solid ${theme.colors.border.default};
      color: ${theme.colors.text.secondary};
      background: ${theme.colors.bg.sunken};
      border-radius: 0 ${theme.radius.md} ${theme.radius.md} 0;
    }
    blockquote > :last-child {
      margin-bottom: 0;
    }

    hr {
      border: 0;
      border-top: 1px solid ${theme.colors.border.subtle};
      margin: 2em 0;
    }

    /* 行内 code；fenced code 由自定义 pre/code renderer 接管为 CodeBlock。 */
    :not(pre) > code {
      font-family: ${theme.typography.fontFamily.mono};
      font-size: 0.9em;
      background: ${theme.colors.bg.sunken};
      color: ${theme.colors.text.primary};
      padding: 0.15em 0.4em;
      border-radius: ${theme.radius.sm};
      border: 1px solid ${theme.colors.border.subtle};
      word-break: break-all;
    }

    /* fallback：caller 用自定义 components 覆盖了 pre/code 时的回落路径。 */
    pre {
      margin: 0 0 1em;
      padding: 1em;
      background: ${theme.colors.bg.sunken};
      border: 1px solid ${theme.colors.border.subtle};
      border-radius: ${theme.radius.md};
      overflow-x: auto;
      font-family: ${theme.typography.fontFamily.mono};
      font-size: 0.9em;
      line-height: 1.6;
    }
    pre code {
      background: transparent;
      padding: 0;
      border: 0;
      border-radius: 0;
      font-size: inherit;
      white-space: pre;
      word-break: normal;
    }

    table {
      margin: 0 0 1em;
      width: 100%;
      border-collapse: collapse;
      border: 1px solid ${theme.colors.border.subtle};
      border-radius: ${theme.radius.md};
      overflow: hidden;
      font-size: 0.95em;
    }
    th,
    td {
      padding: 0.55em 0.85em;
      border-bottom: 1px solid ${theme.colors.border.subtle};
      text-align: left;
      vertical-align: top;
    }
    th {
      font-weight: ${theme.typography.fontWeight.semibold};
      background: ${theme.colors.bg.muted};
      color: ${theme.colors.text.primary};
    }
    tbody tr:nth-of-type(even) td {
      background: ${tableStripe};
    }
    tbody tr:hover td {
      background: ${tableHover};
    }
    tr:last-child td {
      border-bottom: 0;
    }

    img {
      max-width: 100%;
      height: auto;
      border-radius: ${theme.radius.md};
      margin: 0.5em 0;
    }

    /* 内嵌的 CodeBlock 上下间距与正文段落对齐。 */
    [data-slot='markdown-codeblock'] {
      margin: 0 0 1em;
    }
  `;
}
function withAlpha(color, alpha) {
  const a = Math.max(0, Math.min(1, alpha));
  const c = color.trim();
  const rgbMatch = c.match(
    /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+[\d.%]+)?\s*\)$/i
  );
  if (rgbMatch) {
    const [, r, g, b] = rgbMatch;
    return `rgba(${r}, ${g}, ${b}, ${a})`;
  }
  const hexMatch = c.match(/^#([0-9a-f]{3,8})$/i);
  if (hexMatch && hexMatch[1]) {
    let h = hexMatch[1];
    if (h.length === 3) {
      h = h.split("").map((ch) => ch + ch).join("");
    }
    if (h.length === 6 || h.length === 8) {
      const r = parseInt(h.slice(0, 2), 16);
      const g = parseInt(h.slice(2, 4), 16);
      const b = parseInt(h.slice(4, 6), 16);
      return `rgba(${r}, ${g}, ${b}, ${a})`;
    }
  }
  return c;
}
function resolveVariantColor(variant, color) {
  const newVariants = [
    "solid",
    "bordered",
    "light",
    "flat",
    "faded",
    "shadow",
    "ghost"
  ];
  if (variant && newVariants.includes(variant)) {
    return { variant, color: color ?? "default" };
  }
  switch (variant) {
    case "primary":
      return { variant: "solid", color: color ?? "primary" };
    case "secondary":
      return { variant: "solid", color: color ?? "secondary" };
    case "outline":
      return { variant: "bordered", color: color ?? "primary" };
    case "danger":
      return { variant: "solid", color: color ?? "danger" };
    case "ghost":
      return color ? { variant: "ghost", color } : { variant: "light", color: "default" };
    default:
      return { variant: "solid", color: color ?? "default" };
  }
}
var Spinner = ({ size = 14 }) => /* @__PURE__ */ jsxRuntime.jsx(
  "span",
  {
    "aria-hidden": true,
    css: react$1.css`
      width: ${size}px;
      height: ${size}px;
      border: 2px solid currentColor;
      border-right-color: transparent;
      border-radius: 50%;
      display: inline-block;
      flex-shrink: 0;
      animation: timeui-spin 0.7s linear infinite;
      @media (prefers-reduced-motion: reduce) {
        animation: none;
      }
      @keyframes timeui-spin {
        to {
          transform: rotate(360deg);
        }
      }
    `
  }
);
var spinnerSize = {
  xs: 12,
  sm: 14,
  md: 16,
  lg: 18,
  xl: 20
};
var Button = react.forwardRef(function Button2({
  as,
  variant,
  color,
  size = "md",
  radius: radiusProp,
  isLoading = false,
  isDisabled = false,
  isFullWidth = false,
  isIconOnly = false,
  startIcon,
  endIcon,
  children,
  type,
  title: titleProp,
  "aria-label": ariaLabel,
  ...rest
}, ref) {
  const tooltipTitle = titleProp ?? (isIconOnly ? ariaLabel : void 0);
  const theme = react$1.useTheme();
  const Comp = as || "button";
  const isButton = Comp === "button";
  const { variant: v, color: c } = resolveVariantColor(variant, color);
  const scale = theme.colors[c];
  const neutral = theme.colors.default;
  const focusColor = theme.colors.focus;
  const borderThin = theme.borders.width.thin;
  const sizeTokens = theme.components.button[size];
  const sizeRadiusMap = {
    xs: "sm",
    sm: "sm",
    md: "md",
    lg: "lg",
    xl: "lg"
  };
  const radiusKey = radiusProp ?? sizeRadiusMap[size];
  const borderRadius = theme.componentRadius[radiusKey];
  let bg = "transparent";
  let fg = scale.DEFAULT;
  let border = "0 solid transparent";
  let boxShadow = "none";
  let hoverBg = null;
  let hoverFg = null;
  switch (v) {
    case "solid":
      bg = scale.DEFAULT;
      fg = scale.foreground;
      hoverBg = withAlpha(scale.DEFAULT, 0.9);
      break;
    case "bordered":
      bg = "transparent";
      fg = scale[600];
      border = `${borderThin} solid ${scale.DEFAULT}`;
      hoverBg = withAlpha(scale.DEFAULT, 0.12);
      break;
    case "light":
      bg = "transparent";
      fg = scale[600];
      hoverBg = withAlpha(scale.DEFAULT, 0.2);
      break;
    case "flat":
      bg = withAlpha(scale.DEFAULT, 0.2);
      fg = scale[600];
      hoverBg = withAlpha(scale.DEFAULT, 0.3);
      break;
    case "faded":
      bg = neutral[100];
      fg = scale[600];
      border = `${borderThin} solid ${neutral[200]}`;
      break;
    case "shadow":
      bg = scale.DEFAULT;
      fg = scale.foreground;
      boxShadow = `${withAlpha(scale.DEFAULT, 0.4)} 0 10px 15px -3px, ${withAlpha(
        scale.DEFAULT,
        0.4
      )} 0 4px 6px -4px`;
      hoverBg = withAlpha(scale.DEFAULT, 0.9);
      break;
    case "ghost":
      bg = "transparent";
      fg = scale[600];
      border = `${borderThin} solid ${scale.DEFAULT}`;
      hoverBg = scale.DEFAULT;
      hoverFg = scale.foreground;
      break;
  }
  const duration = theme.motion.duration.normal ?? "250ms";
  return /* @__PURE__ */ jsxRuntime.jsxs(
    Comp,
    {
      ref,
      type: isButton ? type ?? "button" : type,
      disabled: isButton ? isDisabled || isLoading : void 0,
      "aria-label": ariaLabel,
      "aria-disabled": isDisabled || isLoading || void 0,
      "aria-busy": isLoading || void 0,
      title: tooltipTitle,
      "data-variant": v,
      "data-color": c,
      "data-size": size,
      "data-loading": isLoading || void 0,
      "data-icon-only": isIconOnly || void 0,
      css: react$1.css`
        position: relative;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: ${isIconOnly ? "0" : sizeTokens.gap};
        box-sizing: border-box;
        height: ${sizeTokens.height};
        min-width: ${isIconOnly ? sizeTokens.height : sizeTokens.minWidth};
        ${isIconOnly ? `width: ${sizeTokens.height};` : ""}
        padding: ${isIconOnly ? "0" : `0 ${sizeTokens.paddingX}`};
        font-family: inherit;
        font-size: ${sizeTokens.fontSize};
        line-height: ${sizeTokens.lineHeight};
        font-weight: 400;
        letter-spacing: normal;
        white-space: nowrap;
        overflow: hidden;
        cursor: pointer;
        user-select: none;
        width: ${isFullWidth ? "100%" : "auto"};
        background-color: ${bg};
        color: ${fg};
        border: ${border};
        border-radius: ${borderRadius};
        box-shadow: ${boxShadow};
        outline: 1.5px solid transparent;
        -webkit-font-smoothing: antialiased;
        transition:
          background-color ${duration},
          color ${duration},
          border-color ${duration},
          transform ${duration},
          opacity ${duration},
          box-shadow ${duration};

        &:hover:not(:disabled):not([aria-disabled='true']) {
          ${hoverBg !== null ? `background-color: ${hoverBg};` : ""}
          ${hoverFg !== null ? `color: ${hoverFg};` : ""}
        }

        &:active:not(:disabled):not([aria-disabled='true']),
        &[data-pressed='true'] {
          transform: scale(0.97);
        }

        &:focus-visible {
          outline: none;
          box-shadow: ${boxShadow === "none" ? "" : `${boxShadow},`} inset 0 0 0 2px ${focusColor};
          z-index: 1;
        }

        &:disabled,
        &[aria-disabled='true'],
        &[data-loading='true'] {
          opacity: 0.5;
          pointer-events: none;
        }

        @media (prefers-reduced-motion: reduce) {
          transition: none;
          &:active:not(:disabled):not([aria-disabled='true']),
          &[data-pressed='true'] {
            transform: none;
          }
        }
      `,
      ...rest,
      children: [
        isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: spinnerSize[size] }) : startIcon,
        children,
        !isLoading && endIcon
      ]
    }
  );
});
Button.displayName = "Button";

// src/utils/env.ts
var isDev = typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production";

// src/utils/refs.ts
function mergeRefs(...refs) {
  return (node) => {
    for (const ref of refs) {
      if (!ref) continue;
      if (typeof ref === "function") {
        ref(node);
      } else {
        ref.current = node;
      }
    }
  };
}
function useControllableState(opts) {
  const { value, defaultValue, onChange, name } = opts;
  const [internal, setInternal] = react.useState(defaultValue);
  const isControlled = value !== void 0;
  const current = isControlled ? value : internal;
  const onChangeRef = react.useRef(onChange);
  const isControlledRef = react.useRef(isControlled);
  react.useEffect(() => {
    onChangeRef.current = onChange;
  }, [onChange]);
  react.useEffect(() => {
    isControlledRef.current = isControlled;
  }, [isControlled]);
  const warnedBothRef = react.useRef(false);
  react.useEffect(() => {
    if (!isDev) return;
    if (warnedBothRef.current) return;
    if (value !== void 0 && defaultValue !== void 0) {
      warnedBothRef.current = true;
      console.warn(
        `[TimeUI] ${name ?? "useControllableState"}: received both \`value\` and \`defaultValue\`. Components must be either controlled or uncontrolled \u2014 \`defaultValue\` will be ignored.`
      );
    }
  }, []);
  const firstControlledRef = react.useRef(isControlled);
  const warnedFlipRef = react.useRef(false);
  react.useEffect(() => {
    if (!isDev) return;
    if (warnedFlipRef.current) return;
    if (firstControlledRef.current !== isControlled) {
      warnedFlipRef.current = true;
      console.warn(
        `[TimeUI] ${name ?? "useControllableState"}: a component is changing from ${firstControlledRef.current ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}. Decide between using a controlled or uncontrolled component for the lifetime of the component.`
      );
    }
  }, [isControlled, name]);
  const setState = react.useCallback((next) => {
    if (isControlledRef.current) {
      onChangeRef.current?.(next);
      return;
    }
    setInternal(next);
    onChangeRef.current?.(next);
  }, []);
  return [current, setState];
}
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
var parseOffsetToken = (raw, fallback) => {
  if (raw === void 0) return fallback;
  if (typeof raw === "number") return raw;
  const n = parseFloat(raw);
  return Number.isFinite(n) ? n : fallback;
};
var splitPlacement = (placement) => {
  const [side, alignSuffix] = placement.split("-");
  return { side, align: alignSuffix ?? "center" };
};
var OPPOSITE_SIDE = {
  top: "bottom",
  bottom: "top",
  left: "right",
  right: "left"
};
var sideHasRoom = (side, anchorRect, popoverSize, offset, viewport, padding) => {
  if (side === "top") return anchorRect.top - offset - padding >= popoverSize.height;
  if (side === "bottom")
    return viewport.height - (anchorRect.top + anchorRect.height) - offset - padding >= popoverSize.height;
  if (side === "left") return anchorRect.left - offset - padding >= popoverSize.width;
  return viewport.width - (anchorRect.left + anchorRect.width) - offset - padding >= popoverSize.width;
};
var computePopoverPosition = (anchorRect, popoverSize, placement, offset, viewport, padding = 8) => {
  let { side, align } = splitPlacement(placement);
  if (viewport) {
    const fits = sideHasRoom(side, anchorRect, popoverSize, offset, viewport, padding);
    const opposite = OPPOSITE_SIDE[side];
    const oppositeFits = sideHasRoom(opposite, anchorRect, popoverSize, offset, viewport, padding);
    if (!fits && oppositeFits) {
      side = opposite;
    }
  }
  let top = 0;
  let left = 0;
  if (side === "top") {
    top = anchorRect.top - popoverSize.height - offset;
  } else if (side === "bottom") {
    top = anchorRect.top + anchorRect.height + offset;
  } else if (side === "left") {
    left = anchorRect.left - popoverSize.width - offset;
  } else {
    left = anchorRect.left + anchorRect.width + offset;
  }
  if (side === "top" || side === "bottom") {
    if (align === "start") {
      left = anchorRect.left;
    } else if (align === "end") {
      left = anchorRect.left + anchorRect.width - popoverSize.width;
    } else {
      left = anchorRect.left + anchorRect.width / 2 - popoverSize.width / 2;
    }
  } else {
    if (align === "start") {
      top = anchorRect.top;
    } else if (align === "end") {
      top = anchorRect.top + anchorRect.height - popoverSize.height;
    } else {
      top = anchorRect.top + anchorRect.height / 2 - popoverSize.height / 2;
    }
  }
  if (viewport) {
    if (side === "top" || side === "bottom") {
      const minLeft = padding;
      const maxLeft = viewport.width - popoverSize.width - padding;
      if (maxLeft >= minLeft) {
        left = Math.min(Math.max(left, minLeft), maxLeft);
      }
    } else {
      const minTop = padding;
      const maxTop = viewport.height - popoverSize.height - padding;
      if (maxTop >= minTop) {
        top = Math.min(Math.max(top, minTop), maxTop);
      }
    }
  }
  return { top, left, side, align };
};
var enterTransformStart = (side, distance) => {
  if (side === "top") return `translateY(${distance})`;
  if (side === "bottom") return `translateY(-${distance})`;
  if (side === "left") return `translateX(${distance})`;
  return `translateX(-${distance})`;
};
var Popover = react.forwardRef(
  function Popover2(props, forwardedRef) {
    const {
      anchor,
      children,
      isOpen,
      defaultOpen = false,
      onOpenChange,
      trigger = "click",
      placement = "bottom",
      offset,
      hasArrow = true,
      closeOnBlur = true,
      closeOnEsc = true,
      header,
      footer,
      openDelay = 0,
      closeDelay = 100,
      className,
      style,
      id,
      "aria-label": ariaLabel
    } = props;
    const theme = react$1.useTheme();
    const popoverTokens = theme.components.popover;
    const autoId = react.useId();
    const safeAutoId = autoId.replace(/:/g, "");
    const baseId = id ?? `timeui-popover-${safeAutoId}`;
    const headerId = header ? `${baseId}-header` : void 0;
    const [open, setOpen] = useControllableState({
      value: isOpen,
      defaultValue: isOpen !== void 0 ? void 0 : defaultOpen,
      onChange: onOpenChange,
      name: "Popover"
    });
    const isManual = trigger === "manual";
    const anchorRef = react.useRef(null);
    const panelRef = react.useRef(null);
    const hoverTimerRef = react.useRef(null);
    const clearHoverTimer = react.useCallback(() => {
      if (hoverTimerRef.current) {
        clearTimeout(hoverTimerRef.current);
        hoverTimerRef.current = null;
      }
    }, []);
    react.useEffect(
      () => () => {
        clearHoverTimer();
      },
      [clearHoverTimer]
    );
    const [position, setPosition] = react.useState(null);
    const [isMounted, setIsMounted] = react.useState(false);
    react.useEffect(() => {
      setIsMounted(true);
    }, []);
    const offsetPx = react.useMemo(
      () => parseOffsetToken(offset, parseOffsetToken(popoverTokens.offset, 8)),
      [offset, popoverTokens.offset]
    );
    const { side: placementSide, align: placementAlign } = react.useMemo(
      () => splitPlacement(placement),
      [placement]
    );
    const updatePosition = react.useCallback(() => {
      const anchorEl = anchorRef.current;
      const panelEl = panelRef.current;
      if (!anchorEl || !panelEl) return;
      const aRect = anchorEl.getBoundingClientRect();
      const pRect = panelEl.getBoundingClientRect();
      const next = computePopoverPosition(
        { top: aRect.top, left: aRect.left, width: aRect.width, height: aRect.height },
        { width: pRect.width, height: pRect.height },
        placement,
        offsetPx,
        { width: window.innerWidth, height: window.innerHeight }
      );
      setPosition((prev) => {
        if (prev && prev.top === next.top && prev.left === next.left && prev.side === next.side && prev.align === next.align) {
          return prev;
        }
        return next;
      });
    }, [placement, offsetPx]);
    useIsomorphicLayoutEffect(() => {
      if (!open || !isMounted) {
        setPosition(null);
        return;
      }
      updatePosition();
      const onScrollResize = () => updatePosition();
      window.addEventListener("scroll", onScrollResize, { passive: true, capture: true });
      window.addEventListener("resize", onScrollResize, { passive: true });
      return () => {
        window.removeEventListener("scroll", onScrollResize, true);
        window.removeEventListener("resize", onScrollResize);
      };
    }, [open, isMounted, updatePosition]);
    react.useEffect(() => {
      if (!open || !closeOnEsc || isManual) return;
      const onKey = (e) => {
        if (e.key === "Escape") {
          e.stopPropagation();
          setOpen(false);
        }
      };
      window.addEventListener("keydown", onKey);
      return () => window.removeEventListener("keydown", onKey);
    }, [open, closeOnEsc, isManual, setOpen]);
    react.useEffect(() => {
      if (!open || !closeOnBlur || isManual) return;
      const onMouseDown = (e) => {
        const target = e.target;
        if (!target) return;
        if (anchorRef.current?.contains(target)) return;
        if (panelRef.current?.contains(target)) return;
        setOpen(false);
      };
      document.addEventListener("mousedown", onMouseDown);
      return () => document.removeEventListener("mousedown", onMouseDown);
    }, [open, closeOnBlur, isManual, setOpen]);
    const handleAnchorClick = react.useCallback(
      (e) => {
        if (isManual || trigger !== "click") return;
        e.preventDefault();
        setOpen(!open);
      },
      [isManual, trigger, open, setOpen]
    );
    const openWithDelay = react.useCallback(
      (delay) => {
        clearHoverTimer();
        if (delay <= 0) {
          setOpen(true);
          return;
        }
        hoverTimerRef.current = setTimeout(() => setOpen(true), delay);
      },
      [clearHoverTimer, setOpen]
    );
    const closeWithDelay = react.useCallback(
      (delay) => {
        clearHoverTimer();
        if (delay <= 0) {
          setOpen(false);
          return;
        }
        hoverTimerRef.current = setTimeout(() => setOpen(false), delay);
      },
      [clearHoverTimer, setOpen]
    );
    const handleAnchorMouseEnter = react.useCallback(() => {
      if (isManual || trigger !== "hover") return;
      openWithDelay(openDelay);
    }, [isManual, trigger, openDelay, openWithDelay]);
    const handleAnchorMouseLeave = react.useCallback(() => {
      if (isManual || trigger !== "hover") return;
      closeWithDelay(closeDelay);
    }, [isManual, trigger, closeDelay, closeWithDelay]);
    const handleAnchorFocus = react.useCallback(() => {
      if (isManual || trigger !== "focus") return;
      setOpen(true);
    }, [isManual, trigger, setOpen]);
    const handleAnchorBlur = react.useCallback(() => {
      if (isManual || trigger !== "focus") return;
      setOpen(false);
    }, [isManual, trigger, setOpen]);
    const handlePanelMouseEnter = react.useCallback(() => {
      if (isManual || trigger !== "hover") return;
      clearHoverTimer();
    }, [isManual, trigger, clearHoverTimer]);
    const handlePanelMouseLeave = react.useCallback(() => {
      if (isManual || trigger !== "hover") return;
      closeWithDelay(closeDelay);
    }, [isManual, trigger, closeDelay, closeWithDelay]);
    const anchorEventProps = {
      onClick: handleAnchorClick,
      onMouseEnter: handleAnchorMouseEnter,
      onMouseLeave: handleAnchorMouseLeave,
      onFocus: handleAnchorFocus,
      onBlur: handleAnchorBlur,
      "aria-expanded": trigger === "click" ? open : void 0,
      "aria-haspopup": trigger === "click" ? "dialog" : void 0,
      "aria-controls": open ? baseId : void 0
    };
    const renderAnchor = () => {
      if (typeof anchor === "function") {
        return anchor({ isOpen: open, ref: anchorRef });
      }
      if (!react.isValidElement(anchor)) return null;
      const element = anchor;
      const existingProps = element.props ?? {};
      const majorReactVersion = Number.parseInt(react.version.split(".")[0] ?? "18", 10);
      const existingRef = majorReactVersion >= 19 ? element.props.ref : element.ref;
      const composed = {
        onClick: (e) => {
          existingProps.onClick?.(e);
          if (!e.defaultPrevented) anchorEventProps.onClick(e);
        },
        onMouseEnter: () => {
          existingProps.onMouseEnter?.();
          anchorEventProps.onMouseEnter();
        },
        onMouseLeave: () => {
          existingProps.onMouseLeave?.();
          anchorEventProps.onMouseLeave();
        },
        onFocus: (e) => {
          existingProps.onFocus?.(e);
          anchorEventProps.onFocus();
        },
        onBlur: (e) => {
          existingProps.onBlur?.(e);
          anchorEventProps.onBlur();
        },
        "aria-expanded": anchorEventProps["aria-expanded"],
        "aria-haspopup": anchorEventProps["aria-haspopup"],
        "aria-controls": anchorEventProps["aria-controls"],
        // React 19 puts ref on props; React 18 warns if props.ref is read.
        ref: mergeRefs(anchorRef, existingRef)
      };
      return react.cloneElement(element, composed);
    };
    const handlePanelKeyDown = react.useCallback(
      (e) => {
        if (e.key === "Escape" && closeOnEsc && !isManual) {
          e.stopPropagation();
          setOpen(false);
        }
      },
      [closeOnEsc, isManual, setOpen]
    );
    const surfaceBg = theme.colors.bg.surface ?? theme.colors.bg.canvas;
    const hairline = theme.colors.border.subtle;
    const textPrimary = theme.colors.text.primary;
    const textSecondary = theme.colors.text.secondary;
    const motionEasing = theme.motion.easing;
    const easing = motionEasing.emphasized ?? motionEasing.standard ?? motionEasing.easeInOut;
    const enterTransform = enterTransformStart(
      position?.side ?? placementSide,
      popoverTokens.enterTranslate
    );
    const panelCss = react$1.css`
      position: fixed;
      z-index: ${theme.zIndex.popover};
      min-width: ${popoverTokens.minWidth};
      max-width: ${popoverTokens.maxWidth};
      background: ${surfaceBg};
      color: ${textPrimary};
      border-radius: ${popoverTokens.radius};
      box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.12),
        0 0 0 1px ${hairline};
      font-family: inherit;
      font-size: ${popoverTokens.bodyFontSize};
      line-height: ${popoverTokens.bodyLineHeight};
      box-sizing: border-box;
      outline: none;
      animation: timeui-popover-in ${popoverTokens.enterDuration} ${easing};
      @keyframes timeui-popover-in {
        from {
          opacity: 0;
          transform: ${enterTransform};
        }
        to {
          opacity: 1;
          transform: translate(0, 0);
        }
      }
      @media (prefers-reduced-motion: reduce) {
        animation: none;
        transition: none;
        transform: none !important;
      }
    `;
    const headerCss = react$1.css`
      padding: ${popoverTokens.headerPaddingY} ${popoverTokens.paddingX};
      font-size: ${popoverTokens.headerFontSize};
      font-weight: ${popoverTokens.headerFontWeight};
      color: ${textPrimary};
      border-bottom: 1px solid ${hairline};
    `;
    const bodyCss = react$1.css`
      padding: ${popoverTokens.paddingY} ${popoverTokens.paddingX};
      color: ${textPrimary};
    `;
    const footerCss = react$1.css`
      padding: ${popoverTokens.footerPaddingY} ${popoverTokens.paddingX};
      border-top: 1px solid ${hairline};
      color: ${textSecondary};
    `;
    const arrowSize = popoverTokens.arrowSize;
    const arrowInset = popoverTokens.arrowInset;
    const arrowSidePos = react.useMemo(() => {
      const side = position?.side ?? placementSide;
      const align = position?.align ?? placementAlign;
      const baseStyle = {
        position: "absolute",
        width: arrowSize,
        height: arrowSize,
        background: surfaceBg,
        transform: "rotate(45deg)",
        boxShadow: `1px 1px 0 0 ${hairline}`,
        pointerEvents: "none"
      };
      const centerOffset = `calc(${arrowSize} / -2)`;
      if (side === "top") {
        baseStyle.bottom = centerOffset;
        baseStyle.boxShadow = `1px 1px 0 0 ${hairline}`;
      } else if (side === "bottom") {
        baseStyle.top = centerOffset;
        baseStyle.boxShadow = `-1px -1px 0 0 ${hairline}`;
      } else if (side === "left") {
        baseStyle.right = centerOffset;
        baseStyle.boxShadow = `1px -1px 0 0 ${hairline}`;
      } else {
        baseStyle.left = centerOffset;
        baseStyle.boxShadow = `-1px 1px 0 0 ${hairline}`;
      }
      if (side === "top" || side === "bottom") {
        if (align === "start") {
          baseStyle.left = arrowInset;
        } else if (align === "end") {
          baseStyle.right = arrowInset;
        } else {
          baseStyle.left = `calc(50% - ${arrowSize} / 2)`;
        }
      } else {
        if (align === "start") {
          baseStyle.top = arrowInset;
        } else if (align === "end") {
          baseStyle.bottom = arrowInset;
        } else {
          baseStyle.top = `calc(50% - ${arrowSize} / 2)`;
        }
      }
      return baseStyle;
    }, [position, placementSide, placementAlign, arrowSize, arrowInset, surfaceBg, hairline]);
    const positionStyle = position ? { top: position.top, left: position.left } : { top: -9999, left: -9999, visibility: "hidden" };
    const mergedStyle = { ...positionStyle, ...style };
    const role = trigger === "hover" ? "tooltip" : "dialog";
    const ariaModal = role === "dialog" ? false : void 0;
    const renderPanel = () => /* @__PURE__ */ jsxRuntime.jsxs(
      "div",
      {
        ref: mergeRefs(panelRef, forwardedRef),
        id: baseId,
        role,
        "aria-modal": ariaModal,
        "aria-label": ariaLabel,
        "aria-labelledby": !ariaLabel && headerId ? headerId : void 0,
        "data-placement": placement,
        "data-side": position?.side ?? placementSide,
        "data-align": position?.align ?? placementAlign,
        tabIndex: -1,
        className,
        style: mergedStyle,
        css: panelCss,
        onKeyDown: handlePanelKeyDown,
        onMouseEnter: handlePanelMouseEnter,
        onMouseLeave: handlePanelMouseLeave,
        children: [
          header ? /* @__PURE__ */ jsxRuntime.jsx("div", { id: headerId, css: headerCss, children: header }) : null,
          /* @__PURE__ */ jsxRuntime.jsx("div", { css: bodyCss, children }),
          footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { css: footerCss, children: footer }) : null,
          hasArrow ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, "data-popover-arrow": "", style: arrowSidePos }) : null
        ]
      }
    );
    return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
      renderAnchor(),
      open && isMounted && typeof document !== "undefined" ? reactDom.createPortal(renderPanel(), document.body) : null
    ] });
  }
);
Popover.displayName = "TimeUI.Popover";
var lastClosedAt = 0;
var parseToken = (raw, fallback) => {
  if (raw === void 0) return fallback;
  if (typeof raw === "number") return raw;
  const n = parseFloat(raw);
  return Number.isFinite(n) ? n : fallback;
};
var splitPlacement2 = (placement) => {
  const [side, alignSuffix] = placement.split("-");
  return { side, align: alignSuffix ?? "center" };
};
var enterTransformStart2 = (side, distance) => {
  if (side === "top") return `translateY(${distance})`;
  if (side === "bottom") return `translateY(-${distance})`;
  if (side === "left") return `translateX(${distance})`;
  return `translateX(-${distance})`;
};
var Tooltip = react.forwardRef(
  function Tooltip2(props, forwardedRef) {
    const {
      content,
      children,
      placement = "top",
      offset,
      hasArrow = true,
      isDisabled = false,
      openDelay,
      closeDelay,
      isOpen,
      defaultOpen = false,
      onOpenChange,
      className,
      style,
      id
    } = props;
    const theme = react$1.useTheme();
    const tooltipTokens = theme.components.tooltip;
    const isDark = theme.mode === "dark";
    const [open, setOpen] = useControllableState({
      value: isOpen,
      defaultValue: isOpen !== void 0 ? void 0 : defaultOpen,
      onChange: onOpenChange,
      name: "Tooltip"
    });
    const isControlled = isOpen !== void 0;
    const autoId = react.useId();
    const safeAutoId = autoId.replace(/:/g, "");
    const baseId = id ?? `timeui-tooltip-${safeAutoId}`;
    const anchorRef = react.useRef(null);
    const panelRef = react.useRef(null);
    const enterTimerRef = react.useRef(null);
    const exitTimerRef = react.useRef(null);
    const clearEnterTimer = react.useCallback(() => {
      if (enterTimerRef.current) {
        clearTimeout(enterTimerRef.current);
        enterTimerRef.current = null;
      }
    }, []);
    const clearExitTimer = react.useCallback(() => {
      if (exitTimerRef.current) {
        clearTimeout(exitTimerRef.current);
        exitTimerRef.current = null;
      }
    }, []);
    react.useEffect(
      () => () => {
        clearEnterTimer();
        clearExitTimer();
      },
      [clearEnterTimer, clearExitTimer]
    );
    const offsetPx = react.useMemo(
      () => parseToken(offset, parseToken(tooltipTokens.offset, 6)),
      [offset, tooltipTokens.offset]
    );
    const openDelayMs = react.useMemo(
      () => parseToken(openDelay, parseToken(tooltipTokens.enterDelay, 120)),
      [openDelay, tooltipTokens.enterDelay]
    );
    const closeDelayMs = react.useMemo(
      () => parseToken(closeDelay, parseToken(tooltipTokens.exitDelay, 80)),
      [closeDelay, tooltipTokens.exitDelay]
    );
    const warmThresholdMs = react.useMemo(
      () => parseToken(void 0, parseToken(tooltipTokens.warmThreshold, 500)),
      [tooltipTokens.warmThreshold]
    );
    const { side: placementSide, align: placementAlign } = react.useMemo(
      () => splitPlacement2(placement),
      [placement]
    );
    const [position, setPosition] = react.useState(null);
    const [isMounted, setIsMounted] = react.useState(false);
    react.useEffect(() => {
      setIsMounted(true);
    }, []);
    const updatePosition = react.useCallback(() => {
      const anchorEl = anchorRef.current;
      const panelEl = panelRef.current;
      if (!anchorEl || !panelEl) return;
      const aRect = anchorEl.getBoundingClientRect();
      const pRect = panelEl.getBoundingClientRect();
      const next = computePopoverPosition(
        { top: aRect.top, left: aRect.left, width: aRect.width, height: aRect.height },
        { width: pRect.width, height: pRect.height },
        placement,
        offsetPx,
        { width: window.innerWidth, height: window.innerHeight }
      );
      setPosition((prev) => {
        if (prev && prev.top === next.top && prev.left === next.left && prev.side === next.side && prev.align === next.align) {
          return prev;
        }
        return next;
      });
    }, [placement, offsetPx]);
    useIsomorphicLayoutEffect(() => {
      if (!open || !isMounted || isDisabled) {
        setPosition(null);
        return;
      }
      updatePosition();
      const onScrollResize = () => updatePosition();
      window.addEventListener("scroll", onScrollResize, { passive: true, capture: true });
      window.addEventListener("resize", onScrollResize, { passive: true });
      return () => {
        window.removeEventListener("scroll", onScrollResize, true);
        window.removeEventListener("resize", onScrollResize);
      };
    }, [open, isMounted, isDisabled, updatePosition]);
    const computeEffectiveEnterDelay = react.useCallback(() => {
      const now = Date.now();
      if (lastClosedAt > 0 && now - lastClosedAt < warmThresholdMs) {
        return 0;
      }
      return openDelayMs;
    }, [openDelayMs, warmThresholdMs]);
    const scheduleOpen = react.useCallback(() => {
      if (isControlled || isDisabled) return;
      clearExitTimer();
      clearEnterTimer();
      const delay = computeEffectiveEnterDelay();
      if (delay <= 0) {
        setOpen(true);
        return;
      }
      enterTimerRef.current = setTimeout(() => {
        enterTimerRef.current = null;
        setOpen(true);
      }, delay);
    }, [
      isControlled,
      isDisabled,
      clearExitTimer,
      clearEnterTimer,
      computeEffectiveEnterDelay,
      setOpen
    ]);
    const scheduleClose = react.useCallback(() => {
      if (isControlled || isDisabled) return;
      clearEnterTimer();
      clearExitTimer();
      const doClose = () => {
        lastClosedAt = Date.now();
        setOpen(false);
      };
      if (closeDelayMs <= 0) {
        doClose();
        return;
      }
      exitTimerRef.current = setTimeout(() => {
        exitTimerRef.current = null;
        doClose();
      }, closeDelayMs);
    }, [isControlled, isDisabled, clearEnterTimer, clearExitTimer, closeDelayMs, setOpen]);
    const renderAnchor = () => {
      if (!react.isValidElement(children)) return null;
      const element = children;
      const existingProps = element.props ?? {};
      const composedDescribedBy = [existingProps["aria-describedby"], baseId].filter(Boolean).join(" ");
      const composed = {
        onMouseEnter: (e) => {
          existingProps.onMouseEnter?.(e);
          scheduleOpen();
        },
        onMouseLeave: (e) => {
          existingProps.onMouseLeave?.(e);
          scheduleClose();
        },
        onFocus: (e) => {
          existingProps.onFocus?.(e);
          scheduleOpen();
        },
        onBlur: (e) => {
          existingProps.onBlur?.(e);
          scheduleClose();
        },
        "aria-describedby": composedDescribedBy,
        // React 19 把 ref 放进 props；React 18 仍挂在 element.ref 上。
        // 优先读 props.ref 以消除 R19 的 deprecation 警告，fallback 保持 R18 兼容。
        ref: mergeRefs(
          anchorRef,
          element.props.ref ?? element.ref
        )
      };
      return react.cloneElement(element, composed);
    };
    const bg = isDark ? tooltipTokens.bgDark : tooltipTokens.bgLight;
    const fg = isDark ? tooltipTokens.fgDark : tooltipTokens.fgLight;
    const motionEasing = theme.motion.easing;
    const easing = motionEasing.standard ?? motionEasing.easeInOut;
    const enterTransform = enterTransformStart2(
      position?.side ?? placementSide,
      tooltipTokens.enterTranslate
    );
    const panelCss = react$1.css`
      position: fixed;
      z-index: ${theme.zIndex.tooltip};
      max-width: ${tooltipTokens.maxWidth};
      padding: ${tooltipTokens.paddingY} ${tooltipTokens.paddingX};
      background: ${bg};
      color: ${fg};
      border-radius: ${tooltipTokens.radius};
      box-shadow: ${tooltipTokens.shadow};
      font-family: inherit;
      font-size: ${tooltipTokens.fontSize};
      line-height: ${tooltipTokens.lineHeight};
      font-weight: ${tooltipTokens.fontWeight};
      box-sizing: border-box;
      /* tooltip 不应该捕获鼠标 —— 鼠标移到 tooltip 上不会使其保持开启，
       移开 anchor 后立刻进入 exit 流程，符合 tooltip 的 UX 预期。 */
      pointer-events: none;
      /* 触摸屏上 tooltip 不应被显示（无 hover）；用 hover 媒体特性兜底。 */
      @media (hover: none) {
        display: none;
      }
      animation: timeui-tooltip-in ${tooltipTokens.enterDuration} ${easing};
      @keyframes timeui-tooltip-in {
        from {
          opacity: 0;
          transform: ${enterTransform};
        }
        to {
          opacity: 1;
          transform: translate(0, 0);
        }
      }
      @media (prefers-reduced-motion: reduce) {
        animation: none;
        transition: none;
        transform: none !important;
      }
    `;
    const arrowSize = tooltipTokens.arrowSize;
    const arrowStyle = react.useMemo(() => {
      const side = position?.side ?? placementSide;
      const align = position?.align ?? placementAlign;
      const baseStyle = {
        position: "absolute",
        width: arrowSize,
        height: arrowSize,
        background: bg,
        transform: "rotate(45deg)",
        pointerEvents: "none"
      };
      const centerOffset = `calc(${arrowSize} / -2)`;
      if (side === "top") {
        baseStyle.bottom = centerOffset;
      } else if (side === "bottom") {
        baseStyle.top = centerOffset;
      } else if (side === "left") {
        baseStyle.right = centerOffset;
      } else {
        baseStyle.left = centerOffset;
      }
      if (side === "top" || side === "bottom") {
        if (align === "start") {
          baseStyle.left = "8px";
        } else if (align === "end") {
          baseStyle.right = "8px";
        } else {
          baseStyle.left = `calc(50% - ${arrowSize} / 2)`;
        }
      } else {
        if (align === "start") {
          baseStyle.top = "8px";
        } else if (align === "end") {
          baseStyle.bottom = "8px";
        } else {
          baseStyle.top = `calc(50% - ${arrowSize} / 2)`;
        }
      }
      return baseStyle;
    }, [position, placementSide, placementAlign, arrowSize, bg]);
    const positionStyle = position ? { top: position.top, left: position.left } : { top: -9999, left: -9999, visibility: "hidden" };
    const mergedStyle = { ...positionStyle, ...style };
    const renderPanel = () => /* @__PURE__ */ jsxRuntime.jsxs(
      "div",
      {
        ref: mergeRefs(panelRef, forwardedRef),
        id: baseId,
        role: "tooltip",
        "data-placement": placement,
        "data-side": position?.side ?? placementSide,
        "data-align": position?.align ?? placementAlign,
        className,
        style: mergedStyle,
        css: panelCss,
        children: [
          content,
          hasArrow ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, "data-tooltip-arrow": "", style: arrowStyle }) : null
        ]
      }
    );
    const shouldRenderPortal = !isDisabled && open && isMounted && typeof document !== "undefined";
    return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
      renderAnchor(),
      shouldRenderPortal ? reactDom.createPortal(renderPanel(), document.body) : null
    ] });
  }
);
Tooltip.displayName = "TimeUI.Tooltip";
var DEFAULT_TOOLBAR_LABELS = {
  toolbarLabel: "Markdown viewer toolbar",
  copy: "Copy markdown",
  copied: "Copied!",
  download: "Download as .md",
  refresh: "Refresh"
};
function Icon({ size = 16, children }) {
  return /* @__PURE__ */ jsxRuntime.jsx(
    "svg",
    {
      width: size,
      height: size,
      viewBox: "0 0 24 24",
      fill: "none",
      stroke: "currentColor",
      strokeWidth: 1.8,
      strokeLinecap: "round",
      strokeLinejoin: "round",
      "aria-hidden": true,
      focusable: false,
      children
    }
  );
}
var CopyIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Icon, { size, children: [
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
] });
var CheckIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Icon, { size, children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) });
var DownloadIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Icon, { size, children: [
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "7 10 12 15 17 10" }),
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
] });
var RefreshIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Icon, { size, children: [
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "23 4 23 10 17 10" }),
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.49 15A9 9 0 1 1 19 6.36L23 10" })
] });
function MarkdownViewerToolbar(props) {
  const {
    getMarkdown,
    config,
    labels,
    canRefresh,
    onRefresh,
    downloadFileName = "document"
  } = props;
  const theme = react$1.useTheme();
  const [copied, setCopied] = react.useState(false);
  const showCopy = config.copy !== false;
  const showDownload = config.download !== false;
  const showRefresh = config.refresh !== false && canRefresh;
  const handleCopy = react.useCallback(async () => {
    if (typeof navigator === "undefined" || !navigator.clipboard) return;
    try {
      await navigator.clipboard.writeText(getMarkdown());
      setCopied(true);
      window.setTimeout(() => setCopied(false), 2e3);
    } catch {
    }
  }, [getMarkdown]);
  const handleDownload = react.useCallback(() => {
    if (typeof window === "undefined" || typeof document === "undefined") return;
    if (typeof Blob === "undefined" || typeof URL.createObjectURL !== "function") return;
    const blob = new Blob([getMarkdown()], { type: "text/markdown;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${downloadFileName}.md`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    window.setTimeout(() => URL.revokeObjectURL(url), 0);
  }, [getMarkdown, downloadFileName]);
  const containerCss = react$1.css`
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 10px;
    border-bottom: 1px solid ${theme.colors.border.subtle};
    background: ${theme.colors.bg.muted};
    color: ${theme.colors.text.primary};
  `;
  const spacerCss = react$1.css`
    flex: 1;
  `;
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "toolbar", "aria-label": labels.toolbarLabel, css: containerCss, children: [
    /* @__PURE__ */ jsxRuntime.jsx("span", { css: spacerCss }),
    showRefresh && /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: labels.refresh, placement: "top", children: /* @__PURE__ */ jsxRuntime.jsx(
      Button,
      {
        type: "button",
        variant: "light",
        color: "default",
        size: "sm",
        isIconOnly: true,
        "aria-label": labels.refresh,
        onClick: () => onRefresh?.(),
        children: /* @__PURE__ */ jsxRuntime.jsx(RefreshIcon, { size: 15 })
      }
    ) }),
    showCopy && /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: copied ? labels.copied : labels.copy, placement: "top", children: /* @__PURE__ */ jsxRuntime.jsx(
      Button,
      {
        type: "button",
        variant: "light",
        color: "default",
        size: "sm",
        isIconOnly: true,
        "aria-label": copied ? labels.copied : labels.copy,
        onClick: handleCopy,
        children: copied ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { size: 15 }) : /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, { size: 15 })
      }
    ) }),
    showDownload && /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: labels.download, placement: "top", children: /* @__PURE__ */ jsxRuntime.jsx(
      Button,
      {
        type: "button",
        variant: "light",
        color: "default",
        size: "sm",
        isIconOnly: true,
        "aria-label": labels.download,
        onClick: handleDownload,
        children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { size: 15 })
      }
    ) })
  ] });
}
MarkdownViewerToolbar.displayName = "MarkdownViewerToolbar";
function baseSlug(text) {
  return text.toLowerCase().trim().replace(/[^\p{L}\p{N}\s-]+/gu, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
}
function createSlugger() {
  const counts = /* @__PURE__ */ new Map();
  return (text) => {
    const base = baseSlug(text) || "section";
    const used = counts.get(base) ?? 0;
    counts.set(base, used + 1);
    return used === 0 ? base : `${base}-${used}`;
  };
}
function extractHeadings(markdown, maxDepth) {
  const lines = markdown.split("\n");
  const slugger = createSlugger();
  const items = [];
  let inFence = false;
  let inFrontmatter = false;
  const cleanInline = (raw) => raw.replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/_([^_]+)_/g, "$1").replace(/~~([^~]+)~~/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").trim();
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i] ?? "";
    if (i === 0 && /^---\s*$/.test(line)) {
      inFrontmatter = true;
      continue;
    }
    if (inFrontmatter) {
      if (/^---\s*$/.test(line)) inFrontmatter = false;
      continue;
    }
    if (/^\s{0,3}(```|~~~)/.test(line)) {
      inFence = !inFence;
      continue;
    }
    if (inFence) continue;
    const atx = /^\s{0,3}(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
    if (atx) {
      const level = atx[1].length;
      if (level > maxDepth) continue;
      const text = cleanInline(atx[2]);
      items.push({ id: slugger(text), level, text, line: i + 1 });
      continue;
    }
    const next = lines[i + 1] ?? "";
    const isSetextH1 = /^\s{0,3}=+\s*$/.test(next);
    const isSetextH2 = /^\s{0,3}-+\s*$/.test(next);
    if ((isSetextH1 || isSetextH2) && line.trim() !== "") {
      const level = isSetextH1 ? 1 : 2;
      if (level <= maxDepth) {
        const text = cleanInline(line.trim());
        items.push({ id: slugger(text), level, text, line: i + 1 });
      }
      i += 1;
      continue;
    }
  }
  return items;
}
function MarkdownViewerToc(props) {
  const {
    items,
    scrollContainerRef,
    contentRef,
    ariaLabel = "Table of contents",
    className
  } = props;
  const theme = react$1.useTheme();
  const [activeId, setActiveId] = react.useState(items[0]?.id ?? null);
  react.useEffect(() => {
    setActiveId(items[0]?.id ?? null);
  }, [items]);
  react.useEffect(() => {
    if (typeof window === "undefined" || typeof IntersectionObserver === "undefined") return;
    const container = contentRef.current;
    if (!container) return;
    if (items.length === 0) return;
    const headings = items.map((it) => container.querySelector(`#${CSS.escape(it.id)}`)).filter((el) => el !== null);
    if (headings.length === 0) return;
    const visible = /* @__PURE__ */ new Set();
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          const id = entry.target.id;
          if (entry.isIntersecting) visible.add(id);
          else visible.delete(id);
        }
        const firstVisible = headings.find((h) => visible.has(h.id));
        if (firstVisible) {
          setActiveId(firstVisible.id);
        }
      },
      {
        root: scrollContainerRef.current,
        // 把 viewport 顶部 10%、底部 60% 排除在外 —— 让"中段可见"的标题胜出。
        rootMargin: "-10% 0px -60% 0px",
        threshold: [0, 1]
      }
    );
    for (const h of headings) observer.observe(h);
    return () => observer.disconnect();
  }, [items, contentRef, scrollContainerRef]);
  if (items.length === 0) return null;
  const handleClick = (event, id) => {
    event.preventDefault();
    const container = contentRef.current;
    const target = container?.querySelector(`#${CSS.escape(id)}`);
    if (!target) return;
    setActiveId(id);
    const reduced = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    target.scrollIntoView({ behavior: reduced ? "auto" : "smooth", block: "start" });
  };
  const minLevel = items.reduce((acc, it) => Math.min(acc, it.level), 6);
  const isDark = theme.mode === "dark";
  const scrollbarThumbIdle = isDark ? "rgba(255, 255, 255, 0.18)" : "rgba(0, 0, 0, 0.18)";
  const scrollbarThumbHover = isDark ? "rgba(255, 255, 255, 0.32)" : "rgba(0, 0, 0, 0.32)";
  const navCss = react$1.css`
    flex: 0 0 220px;
    min-width: 0;
    align-self: flex-start;
    position: sticky;
    top: 0;
    max-height: 100%;
    overflow-y: auto;
    padding: 4px 0 4px 16px;
    border-inline-start: 1px solid ${theme.colors.border.subtle};
    font-family: ${theme.typography.fontFamily.sans};
    font-size: ${theme.typography.fontSize.sm};
    color: ${theme.colors.text.secondary};

    /* hover-only thin scrollbar，与正文滚动区一致。 */
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    transition: scrollbar-color 200ms ease;
    &:hover,
    &:focus-within {
      scrollbar-color: ${scrollbarThumbIdle} transparent;
    }
    &::-webkit-scrollbar {
      width: 8px;
    }
    &::-webkit-scrollbar-track {
      background: transparent;
    }
    &::-webkit-scrollbar-thumb {
      background: transparent;
      border-radius: 999px;
      border: 2px solid transparent;
      background-clip: content-box;
      transition: background-color 200ms ease;
    }
    &:hover::-webkit-scrollbar-thumb,
    &:focus-within::-webkit-scrollbar-thumb {
      background-color: ${scrollbarThumbIdle};
    }
    &::-webkit-scrollbar-thumb:hover {
      background-color: ${scrollbarThumbHover};
    }
    @media (prefers-reduced-motion: reduce) {
      transition: none;
      &::-webkit-scrollbar-thumb {
        transition: none;
      }
    }

    /* host-style isolation —— neutralise outer ul/li/a treatments. */
    ul,
    ol,
    li {
      list-style: none !important;
      margin: 0 !important;
      padding: 0 !important;
    }
    li {
      position: static !important;
    }
    li::before,
    li::after {
      content: none !important;
      display: none !important;
    }
    a {
      background: none !important;
      background-image: none !important;
      padding-bottom: 0 !important;
      text-decoration: none !important;
    }
  `;
  const headingCss = react$1.css`
    font-size: 11px;
    font-weight: ${theme.typography.fontWeight.semibold};
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: ${theme.colors.text.muted};
    margin: 0 0 8px;
    padding: 0 8px;
    line-height: 1.4;
  `;
  const itemAnchorCss = (isActive, depth) => {
    const indentStep = Math.max(0, depth - minLevel);
    return react$1.css`
      position: relative;
      display: block;
      padding: 5px 10px 5px ${10 + indentStep * 12}px;
      color: ${isActive ? theme.colors.text.primary : theme.colors.text.secondary};
      font-weight: ${isActive ? theme.typography.fontWeight.semibold : theme.typography.fontWeight.regular};
      line-height: 1.45;
      border-radius: ${theme.radius.sm};
      transition:
        color 120ms ease,
        background 120ms ease;
      @media (prefers-reduced-motion: reduce) {
        transition: none;
      }

      /* Active accent bar — sits flush against nav's inline-start border.
         Use logical property so RTL flips with the layout. -17px = 16px nav
         padding + 1px border width. */
      &::before {
        content: '';
        position: absolute;
        inset-inline-start: -17px;
        top: 6px;
        bottom: 6px;
        width: 2px;
        border-radius: 1px;
        background: ${isActive ? theme.colors.primary[500] : "transparent"};
        transition: background 120ms ease;
      }

      &:hover {
        color: ${theme.colors.text.primary};
        background: ${theme.colors.bg.muted};
      }
      &:focus-visible {
        outline: 2px solid ${theme.colors.focus};
        outline-offset: -2px;
      }
    `;
  };
  return /* @__PURE__ */ jsxRuntime.jsxs("nav", { "aria-label": ariaLabel, className, css: navCss, children: [
    /* @__PURE__ */ jsxRuntime.jsx("div", { css: headingCss, "aria-hidden": true, children: ariaLabel }),
    /* @__PURE__ */ jsxRuntime.jsx("ul", { children: items.map((item) => {
      const isActive = item.id === activeId;
      return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
        "a",
        {
          href: `#${item.id}`,
          onClick: (e) => handleClick(e, item.id),
          "aria-current": isActive ? "location" : void 0,
          css: itemAnchorCss(isActive, item.level),
          children: item.text
        }
      ) }, item.id);
    }) })
  ] });
}
MarkdownViewerToc.displayName = "MarkdownViewerToc";
function isExternalHref(href) {
  return /^(https?:|mailto:|tel:|ftp:)/i.test(href);
}
function isAnchorHref(href) {
  return href.startsWith("#");
}
function childrenToText(children) {
  if (children == null || children === false) return "";
  if (typeof children === "string") return children;
  if (typeof children === "number") return String(children);
  if (Array.isArray(children)) return children.map(childrenToText).join("");
  if (typeof children === "object" && "props" in children) {
    const el = children;
    return childrenToText(el.props?.children);
  }
  return "";
}
function extractLanguage(className) {
  if (!className) return void 0;
  const m = /language-([\w+#-]+)/.exec(className);
  return m?.[1];
}
function toCssLength(value) {
  if (value === void 0) return void 0;
  return typeof value === "number" ? `${value}px` : value;
}
var MarkdownViewer = react.forwardRef(
  function MarkdownViewer2(props, forwardedRef) {
    const {
      source,
      sourceType = "content",
      fetchOptions,
      showToolbar = true,
      toolbar,
      toolbarLabels: toolbarLabelOverrides,
      showToc = false,
      tocPosition = "right",
      tocMaxDepth = 3,
      gfm = true,
      linkTarget = "_blank",
      onLinkClick,
      components: componentOverrides,
      onLoad,
      onError,
      loadingFallback,
      errorFallback,
      maxWidth = "72ch",
      className,
      style,
      id,
      "aria-label": ariaLabel,
      ...rest
    } = props;
    const theme = react$1.useTheme();
    const [markdown, setMarkdown] = react.useState(sourceType === "content" ? source : "");
    const [isLoading, setIsLoading] = react.useState(sourceType === "url");
    const [error, setError] = react.useState(null);
    const [refreshNonce, setRefreshNonce] = react.useState(0);
    const onLoadRef = react.useRef(onLoad);
    const onErrorRef = react.useRef(onError);
    react.useEffect(() => {
      onLoadRef.current = onLoad;
    }, [onLoad]);
    react.useEffect(() => {
      onErrorRef.current = onError;
    }, [onError]);
    const contentRef = react.useRef(null);
    const scrollContainerRef = react.useRef(null);
    react.useImperativeHandle(forwardedRef, () => scrollContainerRef.current, []);
    react.useEffect(() => {
      if (sourceType !== "url") {
        setMarkdown(source);
        setIsLoading(false);
        setError(null);
        onLoadRef.current?.(source);
        return;
      }
      const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
      let cancelled = false;
      setIsLoading(true);
      setError(null);
      (async () => {
        try {
          const resp = await fetch(source, { ...fetchOptions, signal: controller?.signal });
          if (!resp.ok) {
            throw new Error(`Failed to fetch markdown (${resp.status} ${resp.statusText})`);
          }
          const text = await resp.text();
          if (cancelled) return;
          setMarkdown(text);
          setIsLoading(false);
          onLoadRef.current?.(text);
        } catch (err) {
          if (cancelled) return;
          if (err instanceof DOMException && err.name === "AbortError") return;
          if (typeof err === "object" && err && "name" in err && err.name === "AbortError") return;
          const e = err instanceof Error ? err : new Error(String(err));
          setError(e);
          setIsLoading(false);
          onErrorRef.current?.(e);
        }
      })();
      return () => {
        cancelled = true;
        controller?.abort();
      };
    }, [source, sourceType, refreshNonce]);
    const handleRefresh = react.useCallback(() => {
      setRefreshNonce((n) => n + 1);
    }, []);
    const allHeadings = react.useMemo(() => extractHeadings(markdown, 6), [markdown]);
    const headingIdByLine = react.useMemo(() => {
      const m = /* @__PURE__ */ new Map();
      for (const h of allHeadings) m.set(h.line, h.id);
      return m;
    }, [allHeadings]);
    const tocItems = react.useMemo(
      () => showToc ? allHeadings.filter((h) => h.level >= 1 && h.level <= tocMaxDepth) : [],
      [allHeadings, tocMaxDepth, showToc]
    );
    const handleAnchorClick = react.useCallback(
      (event) => {
        const a = event.currentTarget;
        const href = a.getAttribute("href") ?? "";
        onLinkClick?.(href, event);
        if (event.defaultPrevented) return;
        if (!isAnchorHref(href)) return;
        event.preventDefault();
        const targetId = href.slice(1);
        const target = contentRef.current?.querySelector(`#${CSS.escape(targetId)}`);
        if (!target) return;
        const reduced = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
        target.scrollIntoView({ behavior: reduced ? "auto" : "smooth", block: "start" });
      },
      [onLinkClick]
    );
    const components = react.useMemo(() => {
      const Heading = (level) => {
        const Tag = `h${level}`;
        const HeadingComp = ({
          children,
          node,
          ...rest2
        }) => {
          const line = node?.position?.start?.line ?? 0;
          const id2 = headingIdByLine.get(line);
          const fallbackId = id2 ?? (baseSlug(childrenToText(children)) || void 0);
          return /* @__PURE__ */ jsxRuntime.jsx(Tag, { id: id2 ?? fallbackId, ...rest2, children });
        };
        HeadingComp.displayName = `MarkdownHeading${level}`;
        return HeadingComp;
      };
      const Anchor = ({ href, children, ...rest2 }) => {
        const safeHref = href ?? "";
        const external = isExternalHref(safeHref);
        const anchor = isAnchorHref(safeHref);
        const target = anchor ? "_self" : external ? linkTarget : "_self";
        const rel = external && target === "_blank" ? "noreferrer noopener" : void 0;
        return /* @__PURE__ */ jsxRuntime.jsx(
          "a",
          {
            href: safeHref,
            target: target === "_self" ? void 0 : target,
            rel,
            onClick: handleAnchorClick,
            ...rest2,
            children
          }
        );
      };
      const Pre = ({ children, ...rest2 }) => {
        const only = Array.isArray(children) ? children[0] : children;
        if (only && typeof only === "object" && "props" in only) {
          const codeEl = only;
          const language = extractLanguage(codeEl.props?.className) ?? "text";
          const codeText = childrenToText(codeEl.props?.children).replace(/\n$/, "");
          return /* @__PURE__ */ jsxRuntime.jsx(
            CodeBlock,
            {
              code: codeText,
              language,
              "data-slot": "markdown-codeblock",
              disableHighlight: language === "text"
            }
          );
        }
        return /* @__PURE__ */ jsxRuntime.jsx("pre", { ...rest2, children });
      };
      const base = {
        a: Anchor,
        pre: Pre,
        h1: Heading(1),
        h2: Heading(2),
        h3: Heading(3),
        h4: Heading(4),
        h5: Heading(5),
        h6: Heading(6)
      };
      return { ...base, ...componentOverrides ?? {} };
    }, [componentOverrides, linkTarget, handleAnchorClick, headingIdByLine]);
    const labels = react.useMemo(
      () => ({ ...DEFAULT_TOOLBAR_LABELS, ...toolbarLabelOverrides ?? {} }),
      [toolbarLabelOverrides]
    );
    const typographyCss = react.useMemo(() => createMarkdownViewerTypography(theme), [theme]);
    const wrapperCss = react$1.css`
      display: flex;
      flex-direction: column;
      box-sizing: border-box;
      width: 100%;
      min-width: 0;
      border: 1px solid ${theme.colors.border.subtle};
      border-radius: ${theme.componentRadius.md};
      background: ${theme.colors.bg.surface};
      color: ${theme.colors.text.primary};
      overflow: hidden;
    `;
    const bodyCss = react$1.css`
      display: flex;
      flex-direction: ${tocPosition === "left" ? "row-reverse" : "row"};
      min-height: 0;
      flex: 1 1 auto;
    `;
    const isDark = theme.mode === "dark";
    const scrollbarThumbIdle = isDark ? "rgba(255, 255, 255, 0.18)" : "rgba(0, 0, 0, 0.18)";
    const scrollbarThumbHover = isDark ? "rgba(255, 255, 255, 0.32)" : "rgba(0, 0, 0, 0.32)";
    const scrollCss = react$1.css`
      flex: 1 1 auto;
      min-width: 0;
      overflow: auto;
      padding: 1.25em 1.5em;

      /* Firefox：细条 + 半透明色，无 hover state，但视觉一致。 */
      scrollbar-width: thin;
      scrollbar-color: transparent transparent;
      transition: scrollbar-color 200ms ease;

      &:hover,
      &:focus-within {
        scrollbar-color: ${scrollbarThumbIdle} transparent;
      }

      /* WebKit：默认完全隐藏轨道；hover 时浮现 thumb，悬停在 thumb 上加深。 */
      &::-webkit-scrollbar {
        width: 8px;
        height: 8px;
      }
      &::-webkit-scrollbar-track {
        background: transparent;
      }
      &::-webkit-scrollbar-thumb {
        background: transparent;
        border-radius: 999px;
        border: 2px solid transparent;
        background-clip: content-box;
        transition: background-color 200ms ease;
      }
      &:hover::-webkit-scrollbar-thumb,
      &:focus-within::-webkit-scrollbar-thumb {
        background-color: ${scrollbarThumbIdle};
      }
      &::-webkit-scrollbar-thumb:hover {
        background-color: ${scrollbarThumbHover};
      }
      &::-webkit-scrollbar-corner {
        background: transparent;
      }

      @media (prefers-reduced-motion: reduce) {
        transition: none;
        &::-webkit-scrollbar-thumb {
          transition: none;
        }
      }
    `;
    const innerMaxWidthCss = react$1.css`
      max-width: ${toCssLength(maxWidth) ?? "72ch"};
      margin: 0 auto;
    `;
    const renderBody = () => {
      if (isLoading) {
        if (loadingFallback !== void 0) {
          return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "markdown-loading", children: loadingFallback });
        }
        return /* @__PURE__ */ jsxRuntime.jsxs(
          "div",
          {
            "data-slot": "markdown-loading",
            css: react$1.css`
              display: flex;
              align-items: center;
              gap: 8px;
              color: ${theme.colors.text.muted};
              padding: 0.5em 0;
              font-size: ${theme.typography.fontSize.sm};
            `,
            children: [
              /* @__PURE__ */ jsxRuntime.jsx(
                "span",
                {
                  "aria-hidden": true,
                  css: react$1.css`
                width: 14px;
                height: 14px;
                border-radius: 50%;
                border: 2px solid ${theme.colors.border.default};
                border-top-color: ${theme.colors.primary[500]};
                animation: timeui-md-spin 0.7s linear infinite;
                @media (prefers-reduced-motion: reduce) {
                  animation: none;
                }
                @keyframes timeui-md-spin {
                  to {
                    transform: rotate(360deg);
                  }
                }
              `
                }
              ),
              /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Loading\u2026" })
            ]
          }
        );
      }
      if (error) {
        if (typeof errorFallback === "function") {
          return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "markdown-error", children: errorFallback(error) });
        }
        if (errorFallback !== void 0) {
          return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "markdown-error", children: errorFallback });
        }
        return /* @__PURE__ */ jsxRuntime.jsx(
          "div",
          {
            "data-slot": "markdown-error",
            role: "alert",
            css: react$1.css`
              padding: 1em;
              border: 1px solid ${theme.colors.danger[500]};
              border-radius: ${theme.radius.md};
              background: ${theme.colors.status.dangerBg};
              color: ${theme.colors.status.danger};
            `,
            children: error.message
          }
        );
      }
      return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: contentRef, "data-slot": "markdown-content", css: typographyCss, children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown__default.default, { remarkPlugins: gfm ? [remarkGfm__default.default] : [], components, children: markdown }) });
    };
    return /* @__PURE__ */ jsxRuntime.jsxs(
      "div",
      {
        id,
        role: "article",
        "aria-label": ariaLabel,
        "aria-busy": isLoading || void 0,
        className,
        style,
        css: wrapperCss,
        "data-loading": isLoading || void 0,
        "data-error": error ? "" : void 0,
        "data-toc-position": showToc ? tocPosition : void 0,
        ...rest,
        children: [
          showToolbar && /* @__PURE__ */ jsxRuntime.jsx(
            MarkdownViewerToolbar,
            {
              getMarkdown: () => markdown,
              config: toolbar ?? {},
              labels,
              canRefresh: sourceType === "url",
              onRefresh: handleRefresh
            }
          ),
          /* @__PURE__ */ jsxRuntime.jsxs("div", { css: bodyCss, children: [
            /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollContainerRef, css: scrollCss, children: /* @__PURE__ */ jsxRuntime.jsx("div", { css: innerMaxWidthCss, children: renderBody() }) }),
            showToc && tocItems.length > 0 && !isLoading && !error && /* @__PURE__ */ jsxRuntime.jsx(
              MarkdownViewerToc,
              {
                items: tocItems,
                scrollContainerRef,
                contentRef
              }
            )
          ] })
        ]
      }
    );
  }
);
MarkdownViewer.displayName = "MarkdownViewer";

exports.MarkdownViewer = MarkdownViewer;
//# sourceMappingURL=markdown-viewer.cjs.map
//# sourceMappingURL=markdown-viewer.cjs.map