@lobehub/ui 5.9.2 → 5.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/es/A/index.mjs.map +1 -1
  2. package/es/Accordion/Accordion.mjs.map +1 -1
  3. package/es/Alert/Alert.mjs.map +1 -1
  4. package/es/Checkbox/CheckboxGroup.mjs.map +1 -1
  5. package/es/ConfigProvider/index.mjs.map +1 -1
  6. package/es/DraggablePanel/DraggablePanel.mjs +19 -3
  7. package/es/DraggablePanel/DraggablePanel.mjs.map +1 -1
  8. package/es/DraggableSideNav/DraggableSideNav.mjs.map +1 -1
  9. package/es/EditorSlashMenu/useNormalizedItems.mjs.map +1 -1
  10. package/es/EmojiPicker/AvatarUploader.mjs.map +1 -1
  11. package/es/EmojiPicker/EmojiPicker.mjs.map +1 -1
  12. package/es/Form/components/FormFlatGroup.mjs.map +1 -1
  13. package/es/Grid/Grid.mjs.map +1 -1
  14. package/es/Highlighter/LangSelect.mjs.map +1 -1
  15. package/es/Highlighter/SyntaxHighlighter/StreamRenderer.mjs.map +1 -1
  16. package/es/Highlighter/const.mjs.map +1 -1
  17. package/es/Hotkey/Hotkey.mjs.map +1 -1
  18. package/es/HotkeyInput/HotkeyInput.mjs.map +1 -1
  19. package/es/Icon/style.mjs.map +1 -1
  20. package/es/Image/components/Toolbar.mjs.map +1 -1
  21. package/es/Img/index.mjs.map +1 -1
  22. package/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs.map +1 -1
  23. package/es/Markdown/SyntaxMarkdown/useSmoothStreamContent.mjs.map +1 -1
  24. package/es/Markdown/components/CodeBlock.mjs.map +1 -1
  25. package/es/Markdown/markdown.style.mjs.map +1 -1
  26. package/es/Markdown/plugins/remarkBr.mjs.map +1 -1
  27. package/es/Markdown/plugins/remarkColor.mjs.map +1 -1
  28. package/es/Markdown/plugins/remarkGfmPlus.mjs.map +1 -1
  29. package/es/MaterialFileTypeIcon/MaterialFileTypeIcon.mjs.map +1 -1
  30. package/es/MaterialFileTypeIcon/utils.mjs.map +1 -1
  31. package/es/Mermaid/SyntaxMermaid/StaticMermaid.mjs.map +1 -1
  32. package/es/Mermaid/SyntaxMermaid/StreamMermaid.mjs.map +1 -1
  33. package/es/Modal/imperative.mjs.map +1 -1
  34. package/es/ScrollShadow/ScrollShadow.mjs.map +1 -1
  35. package/es/SortableList/SortableList.mjs.map +1 -1
  36. package/es/SortableList/components/SortableItem.mjs.map +1 -1
  37. package/es/Text/Text.mjs.map +1 -1
  38. package/es/ThemeProvider/ThemeProvider.mjs.map +1 -1
  39. package/es/Toc/style.mjs.map +1 -1
  40. package/es/Tooltip/TooltipGroup.mjs.map +1 -1
  41. package/es/Tooltip/TooltipInGroup.mjs.map +1 -1
  42. package/es/Tooltip/TooltipStandalone.mjs.map +1 -1
  43. package/es/Tooltip/useMergedTooltipProps.mjs.map +1 -1
  44. package/es/awesome/Spline/ParentSize.mjs.map +1 -1
  45. package/es/awesome/TypewriterEffect/TypewriterEffect.mjs.map +1 -1
  46. package/es/base-ui/ContextMenu/renderItems.mjs.map +1 -1
  47. package/es/base-ui/ContextMenu/store.mjs.map +1 -1
  48. package/es/base-ui/DropdownMenu/renderItems.mjs.map +1 -1
  49. package/es/base-ui/FloatingSheet/FloatingSheet.mjs.map +1 -1
  50. package/es/base-ui/FloatingSheet/useSheetDrag.mjs.map +1 -1
  51. package/es/base-ui/FloatingSheet/useSnapPoints.mjs.map +1 -1
  52. package/es/base-ui/Modal/atoms.mjs.map +1 -1
  53. package/es/base-ui/Modal/imperative.mjs.map +1 -1
  54. package/es/base-ui/Modal/style.mjs +1 -0
  55. package/es/base-ui/Modal/style.mjs.map +1 -1
  56. package/es/base-ui/Modal/zIndexManager.mjs.map +1 -1
  57. package/es/base-ui/Popover/useMergedPopoverProps.mjs.map +1 -1
  58. package/es/base-ui/Select/Select.mjs.map +1 -1
  59. package/es/base-ui/Toast/imperative.mjs.map +1 -1
  60. package/es/brand/BrandLoading/index.mjs.map +1 -1
  61. package/es/brand/Logo3d/index.mjs.map +1 -1
  62. package/es/chat/ChatItem/components/Actions.mjs.map +1 -1
  63. package/es/chat/EditableMessageList/EditableMessageList.mjs.map +1 -1
  64. package/es/hooks/useHighlight.mjs.map +1 -1
  65. package/es/hooks/useMarkdown/latex.mjs.map +1 -1
  66. package/es/hooks/useMarkdown/useMarkdownComponents.mjs.map +1 -1
  67. package/es/hooks/useMarkdown/useMarkdownContent.mjs.map +1 -1
  68. package/es/hooks/useMermaid.mjs.map +1 -1
  69. package/es/hooks/useNativeButton.mjs.map +1 -1
  70. package/es/hooks/useStreamHighlight.mjs.map +1 -1
  71. package/es/hooks/useStreamMermaid.mjs.map +1 -1
  72. package/es/i18n/useTranslation.mjs.map +1 -1
  73. package/es/mdx/mdxComponents/CodeBlock.mjs.map +1 -1
  74. package/es/styles/customTheme.mjs.map +1 -1
  75. package/es/styles/theme/customStylishStatic.mjs +15 -0
  76. package/es/styles/theme/customStylishStatic.mjs.map +1 -1
  77. package/es/styles/theme/token/base.mjs.map +1 -1
  78. package/es/utils/blobToPng.mjs.map +1 -1
  79. package/es/utils/placement.mjs.map +1 -1
  80. package/es/utils/smoothCorners.mjs.map +1 -1
  81. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useNativeButton.mjs","names":[],"sources":["../../src/hooks/useNativeButton.ts"],"sourcesContent":["import { isValidElement, type ReactElement, type ReactNode, useMemo } from 'react';\n\nexport interface UseNativeButtonOptions {\n /**\n * The children element that will be used as the trigger\n */\n children: ReactNode;\n /**\n * User-provided nativeButton prop\n */\n nativeButton?: boolean;\n /**\n * Additional nativeButton from trigger props (for DropdownMenu)\n */\n triggerNativeButton?: boolean;\n}\n\nexport interface UseNativeButtonResult {\n /**\n * Whether the trigger element is a native button\n */\n isNativeButtonTriggerElement: boolean;\n /**\n * The resolved nativeButton value to pass to Base UI components\n */\n resolvedNativeButton: boolean | undefined;\n}\n\n/**\n * Map of component displayNames to their nativeButton values.\n * Components that render native <button> elements should be true,\n * components that render non-button elements should be false.\n */\nconst NATIVE_BUTTON_MAP: Record<string, boolean> = {\n A: false,\n ActionIcon: false,\n ActionIconGroup: false,\n Alert: false,\n Avatar: false,\n AvatarGroup: false,\n Block: false,\n BottomGradientButton: true,\n Burger: false,\n Button: true,\n Center: false,\n Checkbox: false,\n CheckboxGroup: false,\n Collapse: false,\n ColorSwatches: false,\n CopyButton: false,\n DownloadButton: false,\n EditableText: false,\n Empty: false,\n FileTypeIcon: false,\n Flexbox: false,\n FluentEmoji: false,\n GradientButton: true,\n Highlighter: false,\n Hotkey: false,\n Icon: false,\n Image: false,\n Img: false,\n Input: false,\n InputNumber: false,\n InputPassword: false,\n List: false,\n ListItem: false,\n Select: false,\n Switch: false,\n Markdown: false,\n MaterialFileTypeIcon: false,\n Segmented: false,\n Skeleton: false,\n SkeletonAvatar: false,\n SkeletonBlock: false,\n SkeletonButton: false,\n SkeletonParagraph: false,\n SkeletonTags: false,\n SkeletonTitle: false,\n Snippet: false,\n Tag: false,\n Text: false,\n TextArea: false,\n ThemeSwitch: false,\n Video: false,\n};\n\n/**\n * Get the displayName of a React component from an element.\n * Handles function components, forwardRef, memo, etc.\n */\nfunction getComponentDisplayName(element: ReactElement): string | undefined {\n const type = element.type;\n\n if (typeof type === 'string') return undefined;\n\n if (typeof type === 'function') {\n return (type as any).displayName || type.name;\n }\n\n if (typeof type === 'object' && type !== null) {\n // Handle forwardRef, memo, etc.\n const displayName =\n (type as any).displayName ||\n (type as any).render?.displayName ||\n (type as any).render?.name ||\n (type as any).type?.displayName ||\n (type as any).type?.name;\n return displayName;\n }\n\n return undefined;\n}\n\n/**\n * Hook to resolve nativeButton prop for Base UI trigger components.\n *\n * When using `render`, Base UI expects the rendered element to be a native <button> by default.\n * If we can infer it's not, we opt out to avoid warnings (users can still override via `nativeButton`).\n */\nexport function useNativeButton({\n children,\n nativeButton,\n triggerNativeButton,\n}: UseNativeButtonOptions): UseNativeButtonResult {\n const isNativeButtonTriggerElement = useMemo(() => {\n if (!isValidElement(children)) return false;\n return typeof children.type === 'string' && children.type === 'button';\n }, [children]);\n\n const resolvedNativeButton = useMemo(() => {\n // User-provided nativeButton takes highest priority\n if (nativeButton !== undefined) return nativeButton;\n // Trigger props nativeButton (for DropdownMenu) takes second priority\n if (triggerNativeButton !== undefined) return triggerNativeButton;\n // If it's a native button element, return true\n if (isNativeButtonTriggerElement) return true;\n // If children is not a valid element, let Base UI decide\n if (!isValidElement(children)) return undefined;\n // If it's a string type but not a button (e.g., 'div'), return false\n if (typeof children.type === 'string') return false;\n\n // Check if it's a known component from the library\n const displayName = getComponentDisplayName(children);\n if (displayName && displayName in NATIVE_BUTTON_MAP) {\n return NATIVE_BUTTON_MAP[displayName];\n }\n\n // For unknown React components, default to false to avoid warnings\n // since most custom components don't render native buttons\n return false;\n }, [children, isNativeButtonTriggerElement, nativeButton, triggerNativeButton]);\n\n return {\n isNativeButtonTriggerElement,\n resolvedNativeButton,\n };\n}\n"],"mappings":";;;;;;;AAiCA,MAAM,oBAA6C;CACjD,GAAG;CACH,YAAY;CACZ,iBAAiB;CACjB,OAAO;CACP,QAAQ;CACR,aAAa;CACb,OAAO;CACP,sBAAsB;CACtB,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,eAAe;CACf,UAAU;CACV,eAAe;CACf,YAAY;CACZ,gBAAgB;CAChB,cAAc;CACd,OAAO;CACP,cAAc;CACd,SAAS;CACT,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,QAAQ;CACR,MAAM;CACN,OAAO;CACP,KAAK;CACL,OAAO;CACP,aAAa;CACb,eAAe;CACf,MAAM;CACN,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,sBAAsB;CACtB,WAAW;CACX,UAAU;CACV,gBAAgB;CAChB,eAAe;CACf,gBAAgB;CAChB,mBAAmB;CACnB,cAAc;CACd,eAAe;CACf,SAAS;CACT,KAAK;CACL,MAAM;CACN,UAAU;CACV,aAAa;CACb,OAAO;CACR;;;;;AAMD,SAAS,wBAAwB,SAA2C;CAC1E,MAAM,OAAO,QAAQ;AAErB,KAAI,OAAO,SAAS,SAAU,QAAO,KAAA;AAErC,KAAI,OAAO,SAAS,WAClB,QAAQ,KAAa,eAAe,KAAK;AAG3C,KAAI,OAAO,SAAS,YAAY,SAAS,KAQvC,QALG,KAAa,eACb,KAAa,QAAQ,eACrB,KAAa,QAAQ,QACrB,KAAa,MAAM,eACnB,KAAa,MAAM;;;;;;;;AAa1B,SAAgB,gBAAgB,EAC9B,UACA,cACA,uBACgD;CAChD,MAAM,+BAA+B,cAAc;AACjD,MAAI,CAAC,eAAe,SAAS,CAAE,QAAO;AACtC,SAAO,OAAO,SAAS,SAAS,YAAY,SAAS,SAAS;IAC7D,CAAC,SAAS,CAAC;AAyBd,QAAO;EACL;EACA,sBAzB2B,cAAc;AAEzC,OAAI,iBAAiB,KAAA,EAAW,QAAO;AAEvC,OAAI,wBAAwB,KAAA,EAAW,QAAO;AAE9C,OAAI,6BAA8B,QAAO;AAEzC,OAAI,CAAC,eAAe,SAAS,CAAE,QAAO,KAAA;AAEtC,OAAI,OAAO,SAAS,SAAS,SAAU,QAAO;GAG9C,MAAM,cAAc,wBAAwB,SAAS;AACrD,OAAI,eAAe,eAAe,kBAChC,QAAO,kBAAkB;AAK3B,UAAO;KACN;GAAC;GAAU;GAA8B;GAAc;GAAoB,CAAC;EAK9E"}
1
+ {"version":3,"file":"useNativeButton.mjs","names":[],"sources":["../../src/hooks/useNativeButton.ts"],"sourcesContent":["import { isValidElement, type ReactElement, type ReactNode, useMemo } from 'react';\n\nexport interface UseNativeButtonOptions {\n /**\n * The children element that will be used as the trigger\n */\n children: ReactNode;\n /**\n * User-provided nativeButton prop\n */\n nativeButton?: boolean;\n /**\n * Additional nativeButton from trigger props (for DropdownMenu)\n */\n triggerNativeButton?: boolean;\n}\n\nexport interface UseNativeButtonResult {\n /**\n * Whether the trigger element is a native button\n */\n isNativeButtonTriggerElement: boolean;\n /**\n * The resolved nativeButton value to pass to Base UI components\n */\n resolvedNativeButton: boolean | undefined;\n}\n\n/**\n * Map of component displayNames to their nativeButton values.\n * Components that render native <button> elements should be true,\n * components that render non-button elements should be false.\n */\nconst NATIVE_BUTTON_MAP: Record<string, boolean> = {\n A: false,\n ActionIcon: false,\n ActionIconGroup: false,\n Alert: false,\n Avatar: false,\n AvatarGroup: false,\n Block: false,\n BottomGradientButton: true,\n Burger: false,\n Button: true,\n Center: false,\n Checkbox: false,\n CheckboxGroup: false,\n Collapse: false,\n ColorSwatches: false,\n CopyButton: false,\n DownloadButton: false,\n EditableText: false,\n Empty: false,\n FileTypeIcon: false,\n Flexbox: false,\n FluentEmoji: false,\n GradientButton: true,\n Highlighter: false,\n Hotkey: false,\n Icon: false,\n Image: false,\n Img: false,\n Input: false,\n InputNumber: false,\n InputPassword: false,\n List: false,\n ListItem: false,\n Select: false,\n Switch: false,\n Markdown: false,\n MaterialFileTypeIcon: false,\n Segmented: false,\n Skeleton: false,\n SkeletonAvatar: false,\n SkeletonBlock: false,\n SkeletonButton: false,\n SkeletonParagraph: false,\n SkeletonTags: false,\n SkeletonTitle: false,\n Snippet: false,\n Tag: false,\n Text: false,\n TextArea: false,\n ThemeSwitch: false,\n Video: false,\n};\n\n/**\n * Get the displayName of a React component from an element.\n * Handles function components, forwardRef, memo, etc.\n */\nfunction getComponentDisplayName(element: ReactElement): string | undefined {\n const type = element.type;\n\n if (typeof type === 'string') return undefined;\n\n if (typeof type === 'function') {\n return (type as any).displayName || type.name;\n }\n\n if (typeof type === 'object' && type !== null) {\n // Handle forwardRef, memo, etc.\n const displayName =\n (type as any).displayName ||\n (type as any).render?.displayName ||\n (type as any).render?.name ||\n (type as any).type?.displayName ||\n (type as any).type?.name;\n return displayName;\n }\n\n return undefined;\n}\n\n/**\n * Hook to resolve nativeButton prop for Base UI trigger components.\n *\n * When using `render`, Base UI expects the rendered element to be a native <button> by default.\n * If we can infer it's not, we opt out to avoid warnings (users can still override via `nativeButton`).\n */\nexport function useNativeButton({\n children,\n nativeButton,\n triggerNativeButton,\n}: UseNativeButtonOptions): UseNativeButtonResult {\n const isNativeButtonTriggerElement = useMemo(() => {\n if (!isValidElement(children)) return false;\n return typeof children.type === 'string' && children.type === 'button';\n }, [children]);\n\n const resolvedNativeButton = useMemo(() => {\n // User-provided nativeButton takes highest priority\n if (nativeButton !== undefined) return nativeButton;\n // Trigger props nativeButton (for DropdownMenu) takes second priority\n if (triggerNativeButton !== undefined) return triggerNativeButton;\n // If it's a native button element, return true\n if (isNativeButtonTriggerElement) return true;\n // If children is not a valid element, let Base UI decide\n if (!isValidElement(children)) return undefined;\n // If it's a string type but not a button (e.g., 'div'), return false\n if (typeof children.type === 'string') return false;\n\n // Check if it's a known component from the library\n const displayName = getComponentDisplayName(children);\n if (displayName && displayName in NATIVE_BUTTON_MAP) {\n return NATIVE_BUTTON_MAP[displayName];\n }\n\n // For unknown React components, default to false to avoid warnings\n // since most custom components don't render native buttons\n return false;\n }, [children, isNativeButtonTriggerElement, nativeButton, triggerNativeButton]);\n\n return {\n isNativeButtonTriggerElement,\n resolvedNativeButton,\n };\n}\n"],"mappings":";;;;;;;AAiCA,MAAM,oBAA6C;CACjD,GAAG;CACH,YAAY;CACZ,iBAAiB;CACjB,OAAO;CACP,QAAQ;CACR,aAAa;CACb,OAAO;CACP,sBAAsB;CACtB,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,eAAe;CACf,UAAU;CACV,eAAe;CACf,YAAY;CACZ,gBAAgB;CAChB,cAAc;CACd,OAAO;CACP,cAAc;CACd,SAAS;CACT,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,QAAQ;CACR,MAAM;CACN,OAAO;CACP,KAAK;CACL,OAAO;CACP,aAAa;CACb,eAAe;CACf,MAAM;CACN,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,sBAAsB;CACtB,WAAW;CACX,UAAU;CACV,gBAAgB;CAChB,eAAe;CACf,gBAAgB;CAChB,mBAAmB;CACnB,cAAc;CACd,eAAe;CACf,SAAS;CACT,KAAK;CACL,MAAM;CACN,UAAU;CACV,aAAa;CACb,OAAO;CACR;;;;;AAMD,SAAS,wBAAwB,SAA2C;CAC1E,MAAM,OAAO,QAAQ;AAErB,KAAI,OAAO,SAAS,SAAU,QAAO,KAAA;AAErC,KAAI,OAAO,SAAS,WAClB,QAAQ,KAAa,eAAe,KAAK;AAG3C,KAAI,OAAO,SAAS,YAAY,SAAS,KAQvC,QALG,KAAa,eACb,KAAa,QAAQ,eACrB,KAAa,QAAQ,QACrB,KAAa,MAAM,eACnB,KAAa,MAAM;;;;;;;;AAa1B,SAAgB,gBAAgB,EAC9B,UACA,cACA,uBACgD;CAChD,MAAM,+BAA+B,cAAc;AACjD,MAAI,CAAC,eAAe,SAAS,CAAE,QAAO;AACtC,SAAO,OAAO,SAAS,SAAS,YAAY,SAAS,SAAS;IAC7D,CAAC,SAAS,CAAC;AAyBd,QAAO;EACL;EACA,sBAzB2B,cAAc;AAEzC,OAAI,iBAAiB,KAAA,EAAW,QAAO;AAEvC,OAAI,wBAAwB,KAAA,EAAW,QAAO;AAE9C,OAAI,6BAA8B,QAAO;AAEzC,OAAI,CAAC,eAAe,SAAS,CAAE,QAAO,KAAA;AAEtC,OAAI,OAAO,SAAS,SAAS,SAAU,QAAO;GAG9C,MAAM,cAAc,wBAAwB,SAAS;AACrD,OAAI,eAAe,eAAe,kBAChC,QAAO,kBAAkB;AAK3B,UAAO;KACN;GAAC;GAAU;GAA8B;GAAc;GAAoB,CAIxD;EACrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useStreamHighlight.mjs","names":["lobeTheme"],"sources":["../../src/hooks/useStreamHighlight.ts"],"sourcesContent":["'use client';\n\nimport { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { type BuiltinTheme, type ThemedToken } from 'shiki';\nimport { ShikiStreamTokenizer } from 'shiki-stream';\n\nimport { getCodeLanguageByInput } from '@/Highlighter/const';\nimport lobeTheme from '@/Highlighter/theme/lobe-theme';\n\nimport { shikiModulePromise, type StreamingHighlightResult } from './useHighlight';\n\ntype StreamingOptions = {\n customThemes?: Record<string, any>;\n enabled?: boolean;\n language: string;\n theme: string;\n};\n\n// Optimized version: reduce array allocations and object spreading\nconst tokensToLineTokens = (tokens: ThemedToken[]): ThemedToken[][] => {\n if (!tokens.length) return [[]];\n\n const lines: ThemedToken[][] = [];\n let currentLine: ThemedToken[] = [];\n\n for (const token of tokens) {\n const content = token.content ?? '';\n\n if (content === '\\n') {\n lines.push(currentLine);\n currentLine = [];\n continue;\n }\n\n const newlineIndex = content.indexOf('\\n');\n if (newlineIndex === -1) {\n // No newline, add token directly\n currentLine.push(token);\n } else {\n // Split on newlines\n const segments = content.split('\\n');\n for (const [j, segment] of segments.entries()) {\n if (segment) {\n // Only create new object if we need to modify content\n currentLine.push(j === 0 && segment === content ? token : { ...token, content: segment });\n }\n if (j < segments.length - 1) {\n lines.push(currentLine);\n currentLine = [];\n }\n }\n }\n }\n\n // Don't forget the last line\n if (currentLine.length > 0 || lines.length === 0) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [[]];\n};\n\nconst createPreStyle = (bg?: string, fg?: string): CSSProperties | undefined => {\n if (!bg && !fg) return undefined;\n return {\n backgroundColor: bg,\n color: fg,\n };\n};\n\nconst useStreamingHighlighter = (\n text: string,\n options: StreamingOptions,\n): StreamingHighlightResult | undefined => {\n const { customThemes, enabled, language, theme } = options;\n const [result, setResult] = useState<StreamingHighlightResult>();\n const tokenizerRef = useRef<ShikiStreamTokenizer | null>(null);\n const previousTextRef = useRef('');\n const safeText = text ?? '';\n const latestTextRef = useRef(safeText);\n const preStyleRef = useRef<CSSProperties | undefined>(undefined);\n const linesRef = useRef<ThemedToken[][]>([[]]);\n\n useEffect(() => {\n latestTextRef.current = safeText;\n }, [safeText]);\n\n // Use ref to store callback to avoid recreating it\n const setStreamingResultRef = useRef((rawLines: ThemedToken[][]) => {\n const previousLines = linesRef.current;\n const newLinesLength = rawLines.length;\n const prevLinesLength = previousLines.length;\n\n // Fast path: if lengths differ or it's a complete reset, use new lines directly\n if (newLinesLength !== prevLinesLength || newLinesLength === 0) {\n linesRef.current = rawLines;\n setResult({\n lines: rawLines,\n preStyle: preStyleRef.current,\n });\n return;\n }\n\n // Optimized comparison: only check changed lines\n let hasChanges = false;\n const mergedLines: ThemedToken[][] = [];\n\n for (let i = 0; i < newLinesLength; i++) {\n const newLine = rawLines[i];\n const prevLine = previousLines[i];\n\n // Quick reference equality check first\n if (prevLine === newLine) {\n mergedLines[i] = prevLine;\n continue;\n }\n\n // Length check\n if (!prevLine || prevLine.length !== newLine.length) {\n mergedLines[i] = newLine;\n hasChanges = true;\n continue;\n }\n\n // Deep comparison only for lines that might have changed\n let lineChanged = false;\n for (const [j, newToken] of newLine.entries()) {\n if (prevLine[j] !== newToken) {\n lineChanged = true;\n break;\n }\n }\n\n if (lineChanged) {\n mergedLines[i] = newLine;\n hasChanges = true;\n } else {\n mergedLines[i] = prevLine;\n }\n }\n\n // Only update state if there are actual changes\n if (hasChanges) {\n linesRef.current = mergedLines;\n setResult({\n lines: mergedLines,\n preStyle: preStyleRef.current,\n });\n }\n });\n\n const updateTokens = useCallback(async (nextText: string, forceReset = false) => {\n const tokenizer = tokenizerRef.current;\n if (!tokenizer) return;\n\n if (forceReset) {\n tokenizer.clear();\n previousTextRef.current = '';\n }\n\n const previousText = previousTextRef.current;\n let chunk = nextText;\n const canAppend = !forceReset && nextText.startsWith(previousText);\n\n if (canAppend) {\n chunk = nextText.slice(previousText.length);\n } else if (!forceReset) {\n tokenizer.clear();\n }\n\n previousTextRef.current = nextText;\n\n if (!chunk) {\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const totalLength = stableTokens.length + unstableTokens.length;\n\n if (totalLength === 0) {\n setStreamingResultRef.current([[]]);\n return;\n }\n\n // Only create merged array if we have both stable and unstable tokens\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n return;\n }\n\n try {\n await tokenizer.enqueue(chunk);\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n } catch (error) {\n console.error('Streaming highlighting failed:', error);\n }\n }, []);\n\n // Cache highlighter key to avoid unnecessary recreations\n const highlighterKeyRef = useRef<string>('');\n\n useEffect(() => {\n if (!enabled) {\n tokenizerRef.current?.clear();\n tokenizerRef.current = null;\n previousTextRef.current = '';\n preStyleRef.current = undefined;\n linesRef.current = [[]];\n setResult(undefined);\n highlighterKeyRef.current = '';\n return;\n }\n\n // Skip if language/theme combination hasn't changed\n const currentKey = `${language}-${theme}`;\n if (highlighterKeyRef.current === currentKey && tokenizerRef.current) {\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n const mod = await shikiModulePromise;\n if (!mod || cancelled) return;\n\n try {\n // Load custom theme if using slack-dark or slack-ochin\n let themesToLoad: any[] = [theme];\n if (customThemes && theme === 'lobe-theme') {\n const customTheme = customThemes[theme];\n if (customTheme) {\n themesToLoad = [customTheme as any];\n }\n }\n\n // Only load the specific language and theme needed\n // getSingletonHighlighter will cache the instance internally\n const highlighter = await mod.getSingletonHighlighter({\n langs: language ? [language] : ['plaintext'],\n themes: themesToLoad,\n });\n\n if (!highlighter || cancelled) return;\n\n // Only create new tokenizer if key changed\n if (highlighterKeyRef.current !== currentKey) {\n // Clear old tokenizer\n tokenizerRef.current?.clear();\n\n const tokenizer = new ShikiStreamTokenizer({\n highlighter,\n lang: language,\n theme,\n });\n\n tokenizerRef.current = tokenizer;\n highlighterKeyRef.current = currentKey;\n previousTextRef.current = '';\n linesRef.current = [[]];\n\n const themeInfo = highlighter.getTheme(theme);\n preStyleRef.current = createPreStyle(themeInfo?.bg, themeInfo?.fg);\n }\n\n const currentText = latestTextRef.current;\n if (currentText) {\n await updateTokens(currentText, true);\n } else {\n setStreamingResultRef.current([[]]);\n }\n } catch (error) {\n console.error('Streaming highlighter initialization failed:', error);\n // Reset on error\n highlighterKeyRef.current = '';\n }\n })();\n\n return () => {\n cancelled = true;\n // Cleanup only if this effect was cancelled before completion\n // The next effect will handle cleanup if key changed\n };\n }, [enabled, language, theme, updateTokens, customThemes]);\n\n // Separate effect for text updates to avoid unnecessary tokenizer recreation\n useEffect(() => {\n if (!enabled) return;\n if (!tokenizerRef.current) return;\n // Use ref to get latest text to avoid stale closures\n const currentText = latestTextRef.current;\n updateTokens(currentText);\n }, [enabled, safeText, updateTokens]);\n\n return result;\n};\n\nexport const useStreamHighlight = (\n text: string,\n {\n language,\n theme: builtinTheme,\n streaming,\n }: { enableTransformer?: boolean; language: string; streaming?: boolean; theme?: BuiltinTheme },\n) => {\n // Safely handle language and text with boundary checks\n const safeText = text ?? '';\n const lang = (language ?? 'plaintext').toLowerCase();\n\n // Match supported languages\n const matchedLanguage = useMemo(() => getCodeLanguageByInput(lang), [lang]);\n\n const effectiveTheme = builtinTheme || 'lobe-theme';\n\n return useStreamingHighlighter(safeText, {\n customThemes: {\n 'lobe-theme': lobeTheme,\n },\n enabled: streaming,\n language: matchedLanguage,\n theme: effectiveTheme,\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useStreamHighlight.mjs","names":["lobeTheme"],"sources":["../../src/hooks/useStreamHighlight.ts"],"sourcesContent":["'use client';\n\nimport { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { type BuiltinTheme, type ThemedToken } from 'shiki';\nimport { ShikiStreamTokenizer } from 'shiki-stream';\n\nimport { getCodeLanguageByInput } from '@/Highlighter/const';\nimport lobeTheme from '@/Highlighter/theme/lobe-theme';\n\nimport { shikiModulePromise, type StreamingHighlightResult } from './useHighlight';\n\ntype StreamingOptions = {\n customThemes?: Record<string, any>;\n enabled?: boolean;\n language: string;\n theme: string;\n};\n\n// Optimized version: reduce array allocations and object spreading\nconst tokensToLineTokens = (tokens: ThemedToken[]): ThemedToken[][] => {\n if (!tokens.length) return [[]];\n\n const lines: ThemedToken[][] = [];\n let currentLine: ThemedToken[] = [];\n\n for (const token of tokens) {\n const content = token.content ?? '';\n\n if (content === '\\n') {\n lines.push(currentLine);\n currentLine = [];\n continue;\n }\n\n const newlineIndex = content.indexOf('\\n');\n if (newlineIndex === -1) {\n // No newline, add token directly\n currentLine.push(token);\n } else {\n // Split on newlines\n const segments = content.split('\\n');\n for (const [j, segment] of segments.entries()) {\n if (segment) {\n // Only create new object if we need to modify content\n currentLine.push(j === 0 && segment === content ? token : { ...token, content: segment });\n }\n if (j < segments.length - 1) {\n lines.push(currentLine);\n currentLine = [];\n }\n }\n }\n }\n\n // Don't forget the last line\n if (currentLine.length > 0 || lines.length === 0) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [[]];\n};\n\nconst createPreStyle = (bg?: string, fg?: string): CSSProperties | undefined => {\n if (!bg && !fg) return undefined;\n return {\n backgroundColor: bg,\n color: fg,\n };\n};\n\nconst useStreamingHighlighter = (\n text: string,\n options: StreamingOptions,\n): StreamingHighlightResult | undefined => {\n const { customThemes, enabled, language, theme } = options;\n const [result, setResult] = useState<StreamingHighlightResult>();\n const tokenizerRef = useRef<ShikiStreamTokenizer | null>(null);\n const previousTextRef = useRef('');\n const safeText = text ?? '';\n const latestTextRef = useRef(safeText);\n const preStyleRef = useRef<CSSProperties | undefined>(undefined);\n const linesRef = useRef<ThemedToken[][]>([[]]);\n\n useEffect(() => {\n latestTextRef.current = safeText;\n }, [safeText]);\n\n // Use ref to store callback to avoid recreating it\n const setStreamingResultRef = useRef((rawLines: ThemedToken[][]) => {\n const previousLines = linesRef.current;\n const newLinesLength = rawLines.length;\n const prevLinesLength = previousLines.length;\n\n // Fast path: if lengths differ or it's a complete reset, use new lines directly\n if (newLinesLength !== prevLinesLength || newLinesLength === 0) {\n linesRef.current = rawLines;\n setResult({\n lines: rawLines,\n preStyle: preStyleRef.current,\n });\n return;\n }\n\n // Optimized comparison: only check changed lines\n let hasChanges = false;\n const mergedLines: ThemedToken[][] = [];\n\n for (let i = 0; i < newLinesLength; i++) {\n const newLine = rawLines[i];\n const prevLine = previousLines[i];\n\n // Quick reference equality check first\n if (prevLine === newLine) {\n mergedLines[i] = prevLine;\n continue;\n }\n\n // Length check\n if (!prevLine || prevLine.length !== newLine.length) {\n mergedLines[i] = newLine;\n hasChanges = true;\n continue;\n }\n\n // Deep comparison only for lines that might have changed\n let lineChanged = false;\n for (const [j, newToken] of newLine.entries()) {\n if (prevLine[j] !== newToken) {\n lineChanged = true;\n break;\n }\n }\n\n if (lineChanged) {\n mergedLines[i] = newLine;\n hasChanges = true;\n } else {\n mergedLines[i] = prevLine;\n }\n }\n\n // Only update state if there are actual changes\n if (hasChanges) {\n linesRef.current = mergedLines;\n setResult({\n lines: mergedLines,\n preStyle: preStyleRef.current,\n });\n }\n });\n\n const updateTokens = useCallback(async (nextText: string, forceReset = false) => {\n const tokenizer = tokenizerRef.current;\n if (!tokenizer) return;\n\n if (forceReset) {\n tokenizer.clear();\n previousTextRef.current = '';\n }\n\n const previousText = previousTextRef.current;\n let chunk = nextText;\n const canAppend = !forceReset && nextText.startsWith(previousText);\n\n if (canAppend) {\n chunk = nextText.slice(previousText.length);\n } else if (!forceReset) {\n tokenizer.clear();\n }\n\n previousTextRef.current = nextText;\n\n if (!chunk) {\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const totalLength = stableTokens.length + unstableTokens.length;\n\n if (totalLength === 0) {\n setStreamingResultRef.current([[]]);\n return;\n }\n\n // Only create merged array if we have both stable and unstable tokens\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n return;\n }\n\n try {\n await tokenizer.enqueue(chunk);\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n } catch (error) {\n console.error('Streaming highlighting failed:', error);\n }\n }, []);\n\n // Cache highlighter key to avoid unnecessary recreations\n const highlighterKeyRef = useRef<string>('');\n\n useEffect(() => {\n if (!enabled) {\n tokenizerRef.current?.clear();\n tokenizerRef.current = null;\n previousTextRef.current = '';\n preStyleRef.current = undefined;\n linesRef.current = [[]];\n setResult(undefined);\n highlighterKeyRef.current = '';\n return;\n }\n\n // Skip if language/theme combination hasn't changed\n const currentKey = `${language}-${theme}`;\n if (highlighterKeyRef.current === currentKey && tokenizerRef.current) {\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n const mod = await shikiModulePromise;\n if (!mod || cancelled) return;\n\n try {\n // Load custom theme if using slack-dark or slack-ochin\n let themesToLoad: any[] = [theme];\n if (customThemes && theme === 'lobe-theme') {\n const customTheme = customThemes[theme];\n if (customTheme) {\n themesToLoad = [customTheme as any];\n }\n }\n\n // Only load the specific language and theme needed\n // getSingletonHighlighter will cache the instance internally\n const highlighter = await mod.getSingletonHighlighter({\n langs: language ? [language] : ['plaintext'],\n themes: themesToLoad,\n });\n\n if (!highlighter || cancelled) return;\n\n // Only create new tokenizer if key changed\n if (highlighterKeyRef.current !== currentKey) {\n // Clear old tokenizer\n tokenizerRef.current?.clear();\n\n const tokenizer = new ShikiStreamTokenizer({\n highlighter,\n lang: language,\n theme,\n });\n\n tokenizerRef.current = tokenizer;\n highlighterKeyRef.current = currentKey;\n previousTextRef.current = '';\n linesRef.current = [[]];\n\n const themeInfo = highlighter.getTheme(theme);\n preStyleRef.current = createPreStyle(themeInfo?.bg, themeInfo?.fg);\n }\n\n const currentText = latestTextRef.current;\n if (currentText) {\n await updateTokens(currentText, true);\n } else {\n setStreamingResultRef.current([[]]);\n }\n } catch (error) {\n console.error('Streaming highlighter initialization failed:', error);\n // Reset on error\n highlighterKeyRef.current = '';\n }\n })();\n\n return () => {\n cancelled = true;\n // Cleanup only if this effect was cancelled before completion\n // The next effect will handle cleanup if key changed\n };\n }, [enabled, language, theme, updateTokens, customThemes]);\n\n // Separate effect for text updates to avoid unnecessary tokenizer recreation\n useEffect(() => {\n if (!enabled) return;\n if (!tokenizerRef.current) return;\n // Use ref to get latest text to avoid stale closures\n const currentText = latestTextRef.current;\n updateTokens(currentText);\n }, [enabled, safeText, updateTokens]);\n\n return result;\n};\n\nexport const useStreamHighlight = (\n text: string,\n {\n language,\n theme: builtinTheme,\n streaming,\n }: { enableTransformer?: boolean; language: string; streaming?: boolean; theme?: BuiltinTheme },\n) => {\n // Safely handle language and text with boundary checks\n const safeText = text ?? '';\n const lang = (language ?? 'plaintext').toLowerCase();\n\n // Match supported languages\n const matchedLanguage = useMemo(() => getCodeLanguageByInput(lang), [lang]);\n\n const effectiveTheme = builtinTheme || 'lobe-theme';\n\n return useStreamingHighlighter(safeText, {\n customThemes: {\n 'lobe-theme': lobeTheme,\n },\n enabled: streaming,\n language: matchedLanguage,\n theme: effectiveTheme,\n });\n};\n"],"mappings":";;;;;;;AAmBA,MAAM,sBAAsB,WAA2C;AACrE,KAAI,CAAC,OAAO,OAAQ,QAAO,CAAC,EAAE,CAAC;CAE/B,MAAM,QAAyB,EAAE;CACjC,IAAI,cAA6B,EAAE;AAEnC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,UAAU,MAAM,WAAW;AAEjC,MAAI,YAAY,MAAM;AACpB,SAAM,KAAK,YAAY;AACvB,iBAAc,EAAE;AAChB;;AAIF,MADqB,QAAQ,QAAQ,KACrB,KAAK,GAEnB,aAAY,KAAK,MAAM;OAClB;GAEL,MAAM,WAAW,QAAQ,MAAM,KAAK;AACpC,QAAK,MAAM,CAAC,GAAG,YAAY,SAAS,SAAS,EAAE;AAC7C,QAAI,QAEF,aAAY,KAAK,MAAM,KAAK,YAAY,UAAU,QAAQ;KAAE,GAAG;KAAO,SAAS;KAAS,CAAC;AAE3F,QAAI,IAAI,SAAS,SAAS,GAAG;AAC3B,WAAM,KAAK,YAAY;AACvB,mBAAc,EAAE;;;;;AAOxB,KAAI,YAAY,SAAS,KAAK,MAAM,WAAW,EAC7C,OAAM,KAAK,YAAY;AAGzB,QAAO,MAAM,SAAS,IAAI,QAAQ,CAAC,EAAE,CAAC;;AAGxC,MAAM,kBAAkB,IAAa,OAA2C;AAC9E,KAAI,CAAC,MAAM,CAAC,GAAI,QAAO,KAAA;AACvB,QAAO;EACL,iBAAiB;EACjB,OAAO;EACR;;AAGH,MAAM,2BACJ,MACA,YACyC;CACzC,MAAM,EAAE,cAAc,SAAS,UAAU,UAAU;CACnD,MAAM,CAAC,QAAQ,aAAa,UAAoC;CAChE,MAAM,eAAe,OAAoC,KAAK;CAC9D,MAAM,kBAAkB,OAAO,GAAG;CAClC,MAAM,WAAW,QAAQ;CACzB,MAAM,gBAAgB,OAAO,SAAS;CACtC,MAAM,cAAc,OAAkC,KAAA,EAAU;CAChE,MAAM,WAAW,OAAwB,CAAC,EAAE,CAAC,CAAC;AAE9C,iBAAgB;AACd,gBAAc,UAAU;IACvB,CAAC,SAAS,CAAC;CAGd,MAAM,wBAAwB,QAAQ,aAA8B;EAClE,MAAM,gBAAgB,SAAS;EAC/B,MAAM,iBAAiB,SAAS;AAIhC,MAAI,mBAHoB,cAAc,UAGI,mBAAmB,GAAG;AAC9D,YAAS,UAAU;AACnB,aAAU;IACR,OAAO;IACP,UAAU,YAAY;IACvB,CAAC;AACF;;EAIF,IAAI,aAAa;EACjB,MAAM,cAA+B,EAAE;AAEvC,OAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,KAAK;GACvC,MAAM,UAAU,SAAS;GACzB,MAAM,WAAW,cAAc;AAG/B,OAAI,aAAa,SAAS;AACxB,gBAAY,KAAK;AACjB;;AAIF,OAAI,CAAC,YAAY,SAAS,WAAW,QAAQ,QAAQ;AACnD,gBAAY,KAAK;AACjB,iBAAa;AACb;;GAIF,IAAI,cAAc;AAClB,QAAK,MAAM,CAAC,GAAG,aAAa,QAAQ,SAAS,CAC3C,KAAI,SAAS,OAAO,UAAU;AAC5B,kBAAc;AACd;;AAIJ,OAAI,aAAa;AACf,gBAAY,KAAK;AACjB,iBAAa;SAEb,aAAY,KAAK;;AAKrB,MAAI,YAAY;AACd,YAAS,UAAU;AACnB,aAAU;IACR,OAAO;IACP,UAAU,YAAY;IACvB,CAAC;;GAEJ;CAEF,MAAM,eAAe,YAAY,OAAO,UAAkB,aAAa,UAAU;EAC/E,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UAAW;AAEhB,MAAI,YAAY;AACd,aAAU,OAAO;AACjB,mBAAgB,UAAU;;EAG5B,MAAM,eAAe,gBAAgB;EACrC,IAAI,QAAQ;AAGZ,MAFkB,CAAC,cAAc,SAAS,WAAW,aAAa,CAGhE,SAAQ,SAAS,MAAM,aAAa,OAAO;WAClC,CAAC,WACV,WAAU,OAAO;AAGnB,kBAAgB,UAAU;AAE1B,MAAI,CAAC,OAAO;GAEV,MAAM,eAAe,UAAU;GAC/B,MAAM,iBAAiB,UAAU;AAGjC,OAFoB,aAAa,SAAS,eAAe,WAErC,GAAG;AACrB,0BAAsB,QAAQ,CAAC,EAAE,CAAC,CAAC;AACnC;;GAIF,MAAM,eACJ,aAAa,WAAW,IACpB,iBACA,eAAe,WAAW,IACxB,eACA,CAAC,GAAG,cAAc,GAAG,eAAe;AAE5C,yBAAsB,QAAQ,mBAAmB,aAAa,CAAC;AAC/D;;AAGF,MAAI;AACF,SAAM,UAAU,QAAQ,MAAM;GAE9B,MAAM,eAAe,UAAU;GAC/B,MAAM,iBAAiB,UAAU;GACjC,MAAM,eACJ,aAAa,WAAW,IACpB,iBACA,eAAe,WAAW,IACxB,eACA,CAAC,GAAG,cAAc,GAAG,eAAe;AAC5C,yBAAsB,QAAQ,mBAAmB,aAAa,CAAC;WACxD,OAAO;AACd,WAAQ,MAAM,kCAAkC,MAAM;;IAEvD,EAAE,CAAC;CAGN,MAAM,oBAAoB,OAAe,GAAG;AAE5C,iBAAgB;AACd,MAAI,CAAC,SAAS;AACZ,gBAAa,SAAS,OAAO;AAC7B,gBAAa,UAAU;AACvB,mBAAgB,UAAU;AAC1B,eAAY,UAAU,KAAA;AACtB,YAAS,UAAU,CAAC,EAAE,CAAC;AACvB,aAAU,KAAA,EAAU;AACpB,qBAAkB,UAAU;AAC5B;;EAIF,MAAM,aAAa,GAAG,SAAS,GAAG;AAClC,MAAI,kBAAkB,YAAY,cAAc,aAAa,QAC3D;EAGF,IAAI,YAAY;AAEhB,GAAC,YAAY;GACX,MAAM,MAAM,MAAM;AAClB,OAAI,CAAC,OAAO,UAAW;AAEvB,OAAI;IAEF,IAAI,eAAsB,CAAC,MAAM;AACjC,QAAI,gBAAgB,UAAU,cAAc;KAC1C,MAAM,cAAc,aAAa;AACjC,SAAI,YACF,gBAAe,CAAC,YAAmB;;IAMvC,MAAM,cAAc,MAAM,IAAI,wBAAwB;KACpD,OAAO,WAAW,CAAC,SAAS,GAAG,CAAC,YAAY;KAC5C,QAAQ;KACT,CAAC;AAEF,QAAI,CAAC,eAAe,UAAW;AAG/B,QAAI,kBAAkB,YAAY,YAAY;AAE5C,kBAAa,SAAS,OAAO;AAQ7B,kBAAa,UAAU,IAND,qBAAqB;MACzC;MACA,MAAM;MACN;MACD,CAE+B;AAChC,uBAAkB,UAAU;AAC5B,qBAAgB,UAAU;AAC1B,cAAS,UAAU,CAAC,EAAE,CAAC;KAEvB,MAAM,YAAY,YAAY,SAAS,MAAM;AAC7C,iBAAY,UAAU,eAAe,WAAW,IAAI,WAAW,GAAG;;IAGpE,MAAM,cAAc,cAAc;AAClC,QAAI,YACF,OAAM,aAAa,aAAa,KAAK;QAErC,uBAAsB,QAAQ,CAAC,EAAE,CAAC,CAAC;YAE9B,OAAO;AACd,YAAQ,MAAM,gDAAgD,MAAM;AAEpE,sBAAkB,UAAU;;MAE5B;AAEJ,eAAa;AACX,eAAY;;IAIb;EAAC;EAAS;EAAU;EAAO;EAAc;EAAa,CAAC;AAG1D,iBAAgB;AACd,MAAI,CAAC,QAAS;AACd,MAAI,CAAC,aAAa,QAAS;EAE3B,MAAM,cAAc,cAAc;AAClC,eAAa,YAAY;IACxB;EAAC;EAAS;EAAU;EAAa,CAAC;AAErC,QAAO;;AAGT,MAAa,sBACX,MACA,EACE,UACA,OAAO,cACP,gBAEC;CAEH,MAAM,WAAW,QAAQ;CACzB,MAAM,QAAQ,YAAY,aAAa,aAAa;CAGpD,MAAM,kBAAkB,cAAc,uBAAuB,KAAK,EAAE,CAAC,KAAK,CAAC;AAI3E,QAAO,wBAAwB,UAAU;EACvC,cAAc,EACZ,cAAcA,oBACf;EACD,SAAS;EACT,UAAU;EACV,OARqB,gBAAgB;EAStC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useStreamMermaid.mjs","names":[],"sources":["../../src/hooks/useStreamMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\nimport { createMermaidConfig, loadMermaid } from './useMermaid';\n\n/**\n * 流式 Mermaid 渲染 - 支持内容逐步更新\n */\nexport const useStreamMermaid = (\n content: string,\n {\n enabled = true,\n id,\n theme: customTheme,\n }: {\n enabled?: boolean;\n id: string;\n theme?: MermaidConfig['theme'];\n },\n): string => {\n const theme = useTheme();\n const [data, setData] = useState<string>('');\n const previousContentRef = useRef<string>('');\n const latestContentRef = useRef(content);\n const renderTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n // 提取主题相关配置到 useMemo 中\n const mermaidConfig = useMemo(\n () => createMermaidConfig(theme, customTheme),\n [\n theme.fontFamilyCode,\n theme.isDarkMode,\n theme.colorTextDescription,\n theme.fontFamily,\n theme.colorTextSecondary,\n theme.colorBgContainer,\n theme.colorInfoBg,\n theme.colorInfoText,\n theme.geekblue,\n theme.colorWarning,\n theme.colorSuccess,\n theme.colorError,\n theme.colorBorder,\n theme.colorInfoBorder,\n theme.colorSuccessBorder,\n theme.colorSuccessBg,\n theme.colorSuccessText,\n theme.colorText,\n customTheme,\n ],\n );\n\n // Update latest content ref\n useEffect(() => {\n latestContentRef.current = content;\n }, [content]);\n\n // Debounced rendering for streaming content\n useEffect(() => {\n if (!enabled) {\n setData('');\n previousContentRef.current = '';\n const timeoutId = renderTimeoutRef.current;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n return;\n }\n\n const currentContent = latestContentRef.current;\n\n // Skip if content hasn't changed\n if (currentContent === previousContentRef.current && data) {\n return;\n }\n\n // Clear previous timeout\n const timeoutId = renderTimeoutRef.current;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Debounce rendering for streaming content (wait 300ms after last change)\n renderTimeoutRef.current = setTimeout(async () => {\n const contentToRender = latestContentRef.current;\n\n // Skip if content changed during debounce\n if (contentToRender !== currentContent) {\n return;\n }\n\n try {\n const mermaidInstance = await loadMermaid();\n if (!mermaidInstance) return;\n\n // 验证语法\n const isValid = await mermaidInstance.parse(contentToRender);\n\n if (isValid) {\n // 初始化并渲染\n mermaidInstance.initialize(mermaidConfig);\n const { svg } = await mermaidInstance.render(id, contentToRender);\n\n // Only update if content hasn't changed during rendering\n if (latestContentRef.current === contentToRender) {\n setData(svg);\n previousContentRef.current = contentToRender;\n }\n }\n } catch (error_) {\n // Silently handle errors during streaming\n // Only log if this is the final content\n if (contentToRender === latestContentRef.current) {\n console.error('Mermaid 解析错误:', error_);\n }\n }\n }, 300);\n\n return () => {\n const timeoutId = renderTimeoutRef.current;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n }, [enabled, content, id, mermaidConfig, data]);\n\n return data;\n};\n"],"mappings":";;;;;;;;AAWA,MAAa,oBACX,SACA,EACE,UAAU,MACV,IACA,OAAO,kBAME;CACX,MAAM,QAAQ,UAAU;CACxB,MAAM,CAAC,MAAM,WAAW,SAAiB,GAAG;CAC5C,MAAM,qBAAqB,OAAe,GAAG;CAC7C,MAAM,mBAAmB,OAAO,QAAQ;CACxC,MAAM,mBAAmB,OAAkD,KAAA,EAAU;CAGrF,MAAM,gBAAgB,cACd,oBAAoB,OAAO,YAAY,EAC7C;EACE,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN;EACD,CACF;AAGD,iBAAgB;AACd,mBAAiB,UAAU;IAC1B,CAAC,QAAQ,CAAC;AAGb,iBAAgB;AACd,MAAI,CAAC,SAAS;AACZ,WAAQ,GAAG;AACX,sBAAmB,UAAU;GAC7B,MAAM,YAAY,iBAAiB;AACnC,OAAI,UACF,cAAa,UAAU;AAEzB;;EAGF,MAAM,iBAAiB,iBAAiB;AAGxC,MAAI,mBAAmB,mBAAmB,WAAW,KACnD;EAIF,MAAM,YAAY,iBAAiB;AACnC,MAAI,UACF,cAAa,UAAU;AAIzB,mBAAiB,UAAU,WAAW,YAAY;GAChD,MAAM,kBAAkB,iBAAiB;AAGzC,OAAI,oBAAoB,eACtB;AAGF,OAAI;IACF,MAAM,kBAAkB,MAAM,aAAa;AAC3C,QAAI,CAAC,gBAAiB;AAKtB,QAFgB,MAAM,gBAAgB,MAAM,gBAAgB,EAE/C;AAEX,qBAAgB,WAAW,cAAc;KACzC,MAAM,EAAE,QAAQ,MAAM,gBAAgB,OAAO,IAAI,gBAAgB;AAGjE,SAAI,iBAAiB,YAAY,iBAAiB;AAChD,cAAQ,IAAI;AACZ,yBAAmB,UAAU;;;YAG1B,QAAQ;AAGf,QAAI,oBAAoB,iBAAiB,QACvC,SAAQ,MAAM,iBAAiB,OAAO;;KAGzC,IAAI;AAEP,eAAa;GACX,MAAM,YAAY,iBAAiB;AACnC,OAAI,UACF,cAAa,UAAU;;IAG1B;EAAC;EAAS;EAAS;EAAI;EAAe;EAAK,CAAC;AAE/C,QAAO"}
1
+ {"version":3,"file":"useStreamMermaid.mjs","names":[],"sources":["../../src/hooks/useStreamMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\nimport { createMermaidConfig, loadMermaid } from './useMermaid';\n\n/**\n * 流式 Mermaid 渲染 - 支持内容逐步更新\n */\nexport const useStreamMermaid = (\n content: string,\n {\n enabled = true,\n id,\n theme: customTheme,\n }: {\n enabled?: boolean;\n id: string;\n theme?: MermaidConfig['theme'];\n },\n): string => {\n const theme = useTheme();\n const [data, setData] = useState<string>('');\n const previousContentRef = useRef<string>('');\n const latestContentRef = useRef(content);\n const renderTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n // 提取主题相关配置到 useMemo 中\n const mermaidConfig = useMemo(\n () => createMermaidConfig(theme, customTheme),\n [\n theme.fontFamilyCode,\n theme.isDarkMode,\n theme.colorTextDescription,\n theme.fontFamily,\n theme.colorTextSecondary,\n theme.colorBgContainer,\n theme.colorInfoBg,\n theme.colorInfoText,\n theme.geekblue,\n theme.colorWarning,\n theme.colorSuccess,\n theme.colorError,\n theme.colorBorder,\n theme.colorInfoBorder,\n theme.colorSuccessBorder,\n theme.colorSuccessBg,\n theme.colorSuccessText,\n theme.colorText,\n customTheme,\n ],\n );\n\n // Update latest content ref\n useEffect(() => {\n latestContentRef.current = content;\n }, [content]);\n\n // Debounced rendering for streaming content\n useEffect(() => {\n if (!enabled) {\n setData('');\n previousContentRef.current = '';\n const timeoutId = renderTimeoutRef.current;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n return;\n }\n\n const currentContent = latestContentRef.current;\n\n // Skip if content hasn't changed\n if (currentContent === previousContentRef.current && data) {\n return;\n }\n\n // Clear previous timeout\n const timeoutId = renderTimeoutRef.current;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Debounce rendering for streaming content (wait 300ms after last change)\n renderTimeoutRef.current = setTimeout(async () => {\n const contentToRender = latestContentRef.current;\n\n // Skip if content changed during debounce\n if (contentToRender !== currentContent) {\n return;\n }\n\n try {\n const mermaidInstance = await loadMermaid();\n if (!mermaidInstance) return;\n\n // 验证语法\n const isValid = await mermaidInstance.parse(contentToRender);\n\n if (isValid) {\n // 初始化并渲染\n mermaidInstance.initialize(mermaidConfig);\n const { svg } = await mermaidInstance.render(id, contentToRender);\n\n // Only update if content hasn't changed during rendering\n if (latestContentRef.current === contentToRender) {\n setData(svg);\n previousContentRef.current = contentToRender;\n }\n }\n } catch (error_) {\n // Silently handle errors during streaming\n // Only log if this is the final content\n if (contentToRender === latestContentRef.current) {\n console.error('Mermaid 解析错误:', error_);\n }\n }\n }, 300);\n\n return () => {\n const timeoutId = renderTimeoutRef.current;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n }, [enabled, content, id, mermaidConfig, data]);\n\n return data;\n};\n"],"mappings":";;;;;;;;AAWA,MAAa,oBACX,SACA,EACE,UAAU,MACV,IACA,OAAO,kBAME;CACX,MAAM,QAAQ,UAAU;CACxB,MAAM,CAAC,MAAM,WAAW,SAAiB,GAAG;CAC5C,MAAM,qBAAqB,OAAe,GAAG;CAC7C,MAAM,mBAAmB,OAAO,QAAQ;CACxC,MAAM,mBAAmB,OAAkD,KAAA,EAAU;CAGrF,MAAM,gBAAgB,cACd,oBAAoB,OAAO,YAAY,EAC7C;EACE,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN;EACD,CACF;AAGD,iBAAgB;AACd,mBAAiB,UAAU;IAC1B,CAAC,QAAQ,CAAC;AAGb,iBAAgB;AACd,MAAI,CAAC,SAAS;AACZ,WAAQ,GAAG;AACX,sBAAmB,UAAU;GAC7B,MAAM,YAAY,iBAAiB;AACnC,OAAI,UACF,cAAa,UAAU;AAEzB;;EAGF,MAAM,iBAAiB,iBAAiB;AAGxC,MAAI,mBAAmB,mBAAmB,WAAW,KACnD;EAIF,MAAM,YAAY,iBAAiB;AACnC,MAAI,UACF,cAAa,UAAU;AAIzB,mBAAiB,UAAU,WAAW,YAAY;GAChD,MAAM,kBAAkB,iBAAiB;AAGzC,OAAI,oBAAoB,eACtB;AAGF,OAAI;IACF,MAAM,kBAAkB,MAAM,aAAa;AAC3C,QAAI,CAAC,gBAAiB;AAKtB,QAAI,MAFkB,gBAAgB,MAAM,gBAAgB,EAE/C;AAEX,qBAAgB,WAAW,cAAc;KACzC,MAAM,EAAE,QAAQ,MAAM,gBAAgB,OAAO,IAAI,gBAAgB;AAGjE,SAAI,iBAAiB,YAAY,iBAAiB;AAChD,cAAQ,IAAI;AACZ,yBAAmB,UAAU;;;YAG1B,QAAQ;AAGf,QAAI,oBAAoB,iBAAiB,QACvC,SAAQ,MAAM,iBAAiB,OAAO;;KAGzC,IAAI;AAEP,eAAa;GACX,MAAM,YAAY,iBAAiB;AACnC,OAAI,UACF,cAAa,UAAU;;IAG1B;EAAC;EAAS;EAAS;EAAI;EAAe;EAAK,CAAC;AAE/C,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTranslation.mjs","names":[],"sources":["../../src/i18n/useTranslation.ts"],"sourcesContent":["import { useMemo } from 'react';\n\nimport { useI18n } from './context';\nimport type { TranslationKey, TranslationResources } from './types';\n\nexport const useTranslation = (fallbackResources?: TranslationResources) => {\n const { t, locale } = useI18n();\n\n const translate = useMemo((): ((key: TranslationKey) => string) => {\n if (!fallbackResources) return t;\n\n return (key: TranslationKey): string => {\n const value = t(key);\n const fallback = fallbackResources[key];\n return value === key && fallback ? fallback : value;\n };\n }, [t, fallbackResources]);\n\n return { locale, t: translate };\n};\n"],"mappings":";;;AAKA,MAAa,kBAAkB,sBAA6C;CAC1E,MAAM,EAAE,GAAG,WAAW,SAAS;AAY/B,QAAO;EAAE;EAAQ,GAVC,cAAiD;AACjE,OAAI,CAAC,kBAAmB,QAAO;AAE/B,WAAQ,QAAgC;IACtC,MAAM,QAAQ,EAAE,IAAI;IACpB,MAAM,WAAW,kBAAkB;AACnC,WAAO,UAAU,OAAO,WAAW,WAAW;;KAE/C,CAAC,GAAG,kBAAkB,CAAC;EAEK"}
1
+ {"version":3,"file":"useTranslation.mjs","names":[],"sources":["../../src/i18n/useTranslation.ts"],"sourcesContent":["import { useMemo } from 'react';\n\nimport { useI18n } from './context';\nimport type { TranslationKey, TranslationResources } from './types';\n\nexport const useTranslation = (fallbackResources?: TranslationResources) => {\n const { t, locale } = useI18n();\n\n const translate = useMemo((): ((key: TranslationKey) => string) => {\n if (!fallbackResources) return t;\n\n return (key: TranslationKey): string => {\n const value = t(key);\n const fallback = fallbackResources[key];\n return value === key && fallback ? fallback : value;\n };\n }, [t, fallbackResources]);\n\n return { locale, t: translate };\n};\n"],"mappings":";;;AAKA,MAAa,kBAAkB,sBAA6C;CAC1E,MAAM,EAAE,GAAG,WAAW,SAAS;AAY/B,QAAO;EAAE;EAAQ,GAVC,cAAiD;AACjE,OAAI,CAAC,kBAAmB,QAAO;AAE/B,WAAQ,QAAgC;IACtC,MAAM,QAAQ,EAAE,IAAI;IACpB,MAAM,WAAW,kBAAkB;AACnC,WAAO,UAAU,OAAO,WAAW,WAAW;;KAE/C,CAAC,GAAG,kBAAkB,CAEI;EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock.mjs","names":[],"sources":["../../../src/mdx/mdxComponents/CodeBlock.tsx"],"sourcesContent":["'use client';\n\nimport type { FC } from 'react';\n\nimport { FALLBACK_LANG } from '@/Highlighter/const';\nimport type { MermaidProps } from '@/Mermaid';\n\nimport { Pre, PreMermaid, PreSingleLine } from '../mdxComponents/Pre';\n\nconst countLines = (str: string): number => {\n const regex = /\\n/g;\n const matches = str.match(regex);\n return matches ? matches.length : 1;\n};\n\nconst useCode = (raw: any) => {\n if (!raw) return;\n\n const { children = '', className } = raw?.props || { children: '' };\n\n if (!children) return;\n\n const content = Array.isArray(children) ? (children[0] as string) : children;\n\n const lang = className?.replace('language-', '') || FALLBACK_LANG;\n\n const isSingleLine = countLines(content) <= 1 && content.length <= 32;\n\n return {\n content,\n isSingleLine,\n lang,\n };\n};\n\ninterface CodeBlockProps {\n children: any;\n enableMermaid?: boolean;\n fullFeatured?: boolean;\n mermaid?: MermaidProps;\n}\n\nconst CodeBlock: FC<CodeBlockProps> = ({ children, fullFeatured, enableMermaid, mermaid }) => {\n const code = useCode(children);\n\n if (!code) return;\n\n if (enableMermaid && code.lang === 'mermaid')\n return (\n <PreMermaid fullFeatured={fullFeatured} {...mermaid}>\n {code.content}\n </PreMermaid>\n );\n\n if (code.isSingleLine) return <PreSingleLine language={code.lang}>{code.content}</PreSingleLine>;\n\n return (\n <Pre allowChangeLanguage={false} fullFeatured={fullFeatured} language={code.lang}>\n {code.content}\n </Pre>\n );\n};\n\nCodeBlock.displayName = 'MdxCodeBlock';\n\nexport default CodeBlock;\n"],"mappings":";;;;;AASA,MAAM,cAAc,QAAwB;CAE1C,MAAM,UAAU,IAAI,MADN,MACkB;AAChC,QAAO,UAAU,QAAQ,SAAS;;AAGpC,MAAM,WAAW,QAAa;AAC5B,KAAI,CAAC,IAAK;CAEV,MAAM,EAAE,WAAW,IAAI,cAAc,KAAK,SAAS,EAAE,UAAU,IAAI;AAEnE,KAAI,CAAC,SAAU;CAEf,MAAM,UAAU,MAAM,QAAQ,SAAS,GAAI,SAAS,KAAgB;CAEpE,MAAM,OAAO,WAAW,QAAQ,aAAa,GAAG,IAAA;AAIhD,QAAO;EACL;EACA,cAJmB,WAAW,QAAQ,IAAI,KAAK,QAAQ,UAAU;EAKjE;EACD;;AAUH,MAAM,aAAiC,EAAE,UAAU,cAAc,eAAe,cAAc;CAC5F,MAAM,OAAO,QAAQ,SAAS;AAE9B,KAAI,CAAC,KAAM;AAEX,KAAI,iBAAiB,KAAK,SAAS,UACjC,QACE,oBAAC,YAAD;EAA0B;EAAc,GAAI;YACzC,KAAK;EACK,CAAA;AAGjB,KAAI,KAAK,aAAc,QAAO,oBAAC,eAAD;EAAe,UAAU,KAAK;YAAO,KAAK;EAAwB,CAAA;AAEhG,QACE,oBAAC,KAAD;EAAK,qBAAqB;EAAqB;EAAc,UAAU,KAAK;YACzE,KAAK;EACF,CAAA;;AAIV,UAAU,cAAc"}
1
+ {"version":3,"file":"CodeBlock.mjs","names":[],"sources":["../../../src/mdx/mdxComponents/CodeBlock.tsx"],"sourcesContent":["'use client';\n\nimport type { FC } from 'react';\n\nimport { FALLBACK_LANG } from '@/Highlighter/const';\nimport type { MermaidProps } from '@/Mermaid';\n\nimport { Pre, PreMermaid, PreSingleLine } from '../mdxComponents/Pre';\n\nconst countLines = (str: string): number => {\n const regex = /\\n/g;\n const matches = str.match(regex);\n return matches ? matches.length : 1;\n};\n\nconst useCode = (raw: any) => {\n if (!raw) return;\n\n const { children = '', className } = raw?.props || { children: '' };\n\n if (!children) return;\n\n const content = Array.isArray(children) ? (children[0] as string) : children;\n\n const lang = className?.replace('language-', '') || FALLBACK_LANG;\n\n const isSingleLine = countLines(content) <= 1 && content.length <= 32;\n\n return {\n content,\n isSingleLine,\n lang,\n };\n};\n\ninterface CodeBlockProps {\n children: any;\n enableMermaid?: boolean;\n fullFeatured?: boolean;\n mermaid?: MermaidProps;\n}\n\nconst CodeBlock: FC<CodeBlockProps> = ({ children, fullFeatured, enableMermaid, mermaid }) => {\n const code = useCode(children);\n\n if (!code) return;\n\n if (enableMermaid && code.lang === 'mermaid')\n return (\n <PreMermaid fullFeatured={fullFeatured} {...mermaid}>\n {code.content}\n </PreMermaid>\n );\n\n if (code.isSingleLine) return <PreSingleLine language={code.lang}>{code.content}</PreSingleLine>;\n\n return (\n <Pre allowChangeLanguage={false} fullFeatured={fullFeatured} language={code.lang}>\n {code.content}\n </Pre>\n );\n};\n\nCodeBlock.displayName = 'MdxCodeBlock';\n\nexport default CodeBlock;\n"],"mappings":";;;;;AASA,MAAM,cAAc,QAAwB;CAE1C,MAAM,UAAU,IAAI,MAAM,MAAM;AAChC,QAAO,UAAU,QAAQ,SAAS;;AAGpC,MAAM,WAAW,QAAa;AAC5B,KAAI,CAAC,IAAK;CAEV,MAAM,EAAE,WAAW,IAAI,cAAc,KAAK,SAAS,EAAE,UAAU,IAAI;AAEnE,KAAI,CAAC,SAAU;CAEf,MAAM,UAAU,MAAM,QAAQ,SAAS,GAAI,SAAS,KAAgB;CAEpE,MAAM,OAAO,WAAW,QAAQ,aAAa,GAAG,IAAA;AAIhD,QAAO;EACL;EACA,cAJmB,WAAW,QAAQ,IAAI,KAAK,QAAQ,UAAU;EAKjE;EACD;;AAUH,MAAM,aAAiC,EAAE,UAAU,cAAc,eAAe,cAAc;CAC5F,MAAM,OAAO,QAAQ,SAAS;AAE9B,KAAI,CAAC,KAAM;AAEX,KAAI,iBAAiB,KAAK,SAAS,UACjC,QACE,oBAAC,YAAD;EAA0B;EAAc,GAAI;YACzC,KAAK;EACK,CAAA;AAGjB,KAAI,KAAK,aAAc,QAAO,oBAAC,eAAD;EAAe,UAAU,KAAK;YAAO,KAAK;EAAwB,CAAA;AAEhG,QACE,oBAAC,KAAD;EAAK,qBAAqB;EAAqB;EAAc,UAAU,KAAK;YACzE,KAAK;EACF,CAAA;;AAIV,UAAU,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"customTheme.mjs","names":[],"sources":["../../src/styles/customTheme.ts"],"sourcesContent":["import {\n blue,\n cyan,\n geekblue,\n gold,\n green,\n lime,\n magenta,\n orange,\n purple,\n red,\n volcano,\n yellow,\n} from '@/color/colors';\nimport { mauve, olive, sage, sand, slate } from '@/color/neutrals';\n\nexport const primaryColors = {\n blue: blue.dark[9],\n cyan: cyan.dark[9],\n geekblue: geekblue.dark[9],\n gold: gold.dark[9],\n green: green.dark[9],\n lime: lime.dark[9],\n magenta: magenta.dark[9],\n orange: orange.dark[9],\n purple: purple.dark[9],\n red: red.dark[9],\n volcano: volcano.dark[9],\n yellow: yellow.dark[9],\n};\n\nexport type PrimaryColorsObj = typeof primaryColors;\nexport type PrimaryColors = keyof PrimaryColorsObj;\nexport const primaryColorsSwatches = [\n primaryColors.red,\n primaryColors.orange,\n primaryColors.gold,\n primaryColors.yellow,\n primaryColors.lime,\n primaryColors.green,\n primaryColors.cyan,\n primaryColors.blue,\n primaryColors.geekblue,\n primaryColors.purple,\n primaryColors.magenta,\n primaryColors.volcano,\n];\nexport const neutralColors = {\n mauve: mauve.dark[9],\n olive: olive.dark[9],\n sage: sage.dark[9],\n sand: sand.dark[9],\n slate: slate.dark[9],\n};\nexport const neutralColorsSwatches = [\n neutralColors.mauve,\n neutralColors.slate,\n neutralColors.sage,\n neutralColors.olive,\n neutralColors.sand,\n];\n\nexport type NeutralColorsObj = typeof neutralColors;\nexport type NeutralColors = keyof NeutralColorsObj;\n\nexport const findCustomThemeName = (type: 'primary' | 'neutral', value: string) => {\n const res = type === 'primary' ? primaryColors : neutralColors;\n const result = Object.entries(res).find((item) => item[1] === value);\n return result?.[0];\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,MAAa,gBAAgB;CAC3B,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAChB,UAAU,SAAS,KAAK;CACxB,MAAM,KAAK,KAAK;CAChB,OAAO,MAAM,KAAK;CAClB,MAAM,KAAK,KAAK;CAChB,SAAS,QAAQ,KAAK;CACtB,QAAQ,OAAO,KAAK;CACpB,QAAQ,OAAO,KAAK;CACpB,KAAK,IAAI,KAAK;CACd,SAAS,QAAQ,KAAK;CACtB,QAAQ,OAAO,KAAK;CACrB;AAID,MAAa,wBAAwB;CACnC,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACf;AACD,MAAa,gBAAgB;CAC3B,OAAO,MAAM,KAAK;CAClB,OAAO,MAAM,KAAK;CAClB,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAChB,OAAO,MAAM,KAAK;CACnB;AACD,MAAa,wBAAwB;CACnC,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACf;AAKD,MAAa,uBAAuB,MAA6B,UAAkB;AAGjF,QADe,OAAO,QADV,SAAS,YAAY,gBAAgB,cACf,CAAC,MAAM,SAAS,KAAK,OAAO,MAAM,GACpD"}
1
+ {"version":3,"file":"customTheme.mjs","names":[],"sources":["../../src/styles/customTheme.ts"],"sourcesContent":["import {\n blue,\n cyan,\n geekblue,\n gold,\n green,\n lime,\n magenta,\n orange,\n purple,\n red,\n volcano,\n yellow,\n} from '@/color/colors';\nimport { mauve, olive, sage, sand, slate } from '@/color/neutrals';\n\nexport const primaryColors = {\n blue: blue.dark[9],\n cyan: cyan.dark[9],\n geekblue: geekblue.dark[9],\n gold: gold.dark[9],\n green: green.dark[9],\n lime: lime.dark[9],\n magenta: magenta.dark[9],\n orange: orange.dark[9],\n purple: purple.dark[9],\n red: red.dark[9],\n volcano: volcano.dark[9],\n yellow: yellow.dark[9],\n};\n\nexport type PrimaryColorsObj = typeof primaryColors;\nexport type PrimaryColors = keyof PrimaryColorsObj;\nexport const primaryColorsSwatches = [\n primaryColors.red,\n primaryColors.orange,\n primaryColors.gold,\n primaryColors.yellow,\n primaryColors.lime,\n primaryColors.green,\n primaryColors.cyan,\n primaryColors.blue,\n primaryColors.geekblue,\n primaryColors.purple,\n primaryColors.magenta,\n primaryColors.volcano,\n];\nexport const neutralColors = {\n mauve: mauve.dark[9],\n olive: olive.dark[9],\n sage: sage.dark[9],\n sand: sand.dark[9],\n slate: slate.dark[9],\n};\nexport const neutralColorsSwatches = [\n neutralColors.mauve,\n neutralColors.slate,\n neutralColors.sage,\n neutralColors.olive,\n neutralColors.sand,\n];\n\nexport type NeutralColorsObj = typeof neutralColors;\nexport type NeutralColors = keyof NeutralColorsObj;\n\nexport const findCustomThemeName = (type: 'primary' | 'neutral', value: string) => {\n const res = type === 'primary' ? primaryColors : neutralColors;\n const result = Object.entries(res).find((item) => item[1] === value);\n return result?.[0];\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,MAAa,gBAAgB;CAC3B,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAChB,UAAU,SAAS,KAAK;CACxB,MAAM,KAAK,KAAK;CAChB,OAAO,MAAM,KAAK;CAClB,MAAM,KAAK,KAAK;CAChB,SAAS,QAAQ,KAAK;CACtB,QAAQ,OAAO,KAAK;CACpB,QAAQ,OAAO,KAAK;CACpB,KAAK,IAAI,KAAK;CACd,SAAS,QAAQ,KAAK;CACtB,QAAQ,OAAO,KAAK;CACrB;AAID,MAAa,wBAAwB;CACnC,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACf;AACD,MAAa,gBAAgB;CAC3B,OAAO,MAAM,KAAK;CAClB,OAAO,MAAM,KAAK;CAClB,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAChB,OAAO,MAAM,KAAK;CACnB;AACD,MAAa,wBAAwB;CACnC,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACf;AAKD,MAAa,uBAAuB,MAA6B,UAAkB;AAGjF,QADe,OAAO,QADV,SAAS,YAAY,gBAAgB,cACf,CAAC,MAAM,SAAS,KAAK,OAAO,MACjD,GAAG"}
@@ -85,6 +85,11 @@ const staticStylish = createStaticStyles(({ css, cssVar }) => ({
85
85
  color: ${cssVar.colorText};
86
86
  }
87
87
  `,
88
+ /**
89
+ * Shadow style using CSS variables.
90
+ * Note: This uses CSS variables which automatically adapt to light/dark mode.
91
+ * For more control, use the dynamic version from customStylish.
92
+ */
88
93
  shadow: css`
89
94
  box-shadow:
90
95
  0 1px 0 -1px ${cssVar.colorBorder},
@@ -101,6 +106,11 @@ const staticStylish = createStaticStyles(({ css, cssVar }) => ({
101
106
  background: ${cssVar.colorFillTertiary};
102
107
  }
103
108
  `,
109
+ /**
110
+ * Variant borderless danger style.
111
+ * Note: Uses colorErrorBg as fallback since colorErrorFillTertiary is not in cssVar.
112
+ * For exact match, use the dynamic version from customStylish.
113
+ */
104
114
  variantBorderlessDanger: css`
105
115
  border: none;
106
116
  background: none;
@@ -123,6 +133,11 @@ const staticStylish = createStaticStyles(({ css, cssVar }) => ({
123
133
  background: ${cssVar.colorFillSecondary};
124
134
  }
125
135
  `,
136
+ /**
137
+ * Variant filled danger style.
138
+ * Note: Uses colorErrorBg as fallback since colorErrorFillTertiary/Secondary are not in cssVar.
139
+ * For exact match, use the dynamic version from customStylish.
140
+ */
126
141
  variantFilledDanger: css`
127
142
  background: ${cssVar.colorErrorBg};
128
143
 
@@ -1 +1 @@
1
- {"version":3,"file":"customStylishStatic.mjs","names":[],"sources":["../../../src/styles/theme/customStylishStatic.ts"],"sourcesContent":["import { createStaticStyles, keyframes } from 'antd-style';\n\nimport type { LobeCustomStylish } from '@/types/customStylish';\n\n/**\n * Static version of custom stylish utilities.\n * This can be used with createStaticStyles for better performance.\n *\n * Note: Some styles that depend on isDarkMode or custom tokens may have limitations.\n * For full dynamic support, use the regular customStylish from './customStylish'.\n */\nconst gradient = keyframes`\n 0% {\n background-position: 0% 50%;\n }\n 50% {\n background-position: 100% 50%;\n }\n 100% {\n background-position: 0% 50%;\n }\n`;\n\nexport const staticStylish = createStaticStyles(({ css, cssVar }) => ({\n active: css`\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFill};\n }\n `,\n\n blur: css`\n backdrop-filter: saturate(150%) blur(10px);\n `,\n\n blurStrong: css`\n backdrop-filter: saturate(150%) blur(36px);\n `,\n\n bottomScrollbar: css`\n ::-webkit-scrollbar {\n width: 0;\n height: 4px;\n background-color: transparent;\n\n &-thumb {\n border-radius: 4px;\n background-color: ${cssVar.colorFill};\n transition: background-color 500ms ${cssVar.motionEaseOut};\n }\n\n &-corner {\n display: none;\n width: 0;\n height: 0;\n }\n }\n `,\n\n disabled: css`\n cursor: not-allowed;\n opacity: 0.5;\n `,\n\n gradientAnimation: css`\n border-radius: inherit;\n background-image: linear-gradient(\n -45deg,\n ${cssVar.gold},\n ${cssVar.magenta},\n ${cssVar.geekblue},\n ${cssVar.cyan}\n );\n background-size: 400% 400%;\n animation: 5s ${gradient} 5s ease infinite;\n `,\n\n noScrollbar: css`\n ::-webkit-scrollbar {\n display: none;\n width: 0;\n height: 0;\n background-color: transparent;\n }\n `,\n\n resetLinkColor: css`\n cursor: pointer;\n color: ${cssVar.colorTextSecondary};\n\n &:hover {\n color: ${cssVar.colorText};\n }\n `,\n\n /**\n * Shadow style using CSS variables.\n * Note: This uses CSS variables which automatically adapt to light/dark mode.\n * For more control, use the dynamic version from customStylish.\n */\n shadow: css`\n box-shadow:\n 0 1px 0 -1px ${cssVar.colorBorder},\n 0 1px 2px -0.5px ${cssVar.colorBorder},\n 0 2px 2px -1px ${cssVar.colorBorderSecondary},\n 0 3px 6px -4px ${cssVar.colorBorderSecondary};\n `,\n\n variantBorderless: css`\n border: none;\n background: none;\n box-shadow: none;\n\n &:hover {\n background: ${cssVar.colorFillTertiary};\n }\n `,\n\n /**\n * Variant borderless danger style.\n * Note: Uses colorErrorBg as fallback since colorErrorFillTertiary is not in cssVar.\n * For exact match, use the dynamic version from customStylish.\n */\n variantBorderlessDanger: css`\n border: none;\n background: none;\n box-shadow: none;\n\n &:hover {\n background: ${cssVar.colorErrorBg};\n box-shadow: inset 0 0 0 1px ${cssVar.colorErrorBg};\n }\n `,\n\n variantBorderlessWithoutHover: css`\n border: none;\n background: none;\n box-shadow: none;\n `,\n\n variantFilled: css`\n background: ${cssVar.colorFillTertiary};\n\n &:hover {\n background: ${cssVar.colorFillSecondary};\n }\n `,\n\n /**\n * Variant filled danger style.\n * Note: Uses colorErrorBg as fallback since colorErrorFillTertiary/Secondary are not in cssVar.\n * For exact match, use the dynamic version from customStylish.\n */\n variantFilledDanger: css`\n background: ${cssVar.colorErrorBg};\n\n &:hover {\n background: ${cssVar.colorErrorBgHover};\n }\n `,\n\n variantFilledWithoutHover: css`\n background: ${cssVar.colorFillTertiary};\n `,\n\n variantOutlined: css`\n border: 1px solid ${cssVar.colorBorderSecondary};\n background: ${cssVar.colorBgContainer};\n\n &:hover {\n border: 1px solid ${cssVar.colorBorder};\n background: ${cssVar.colorBgContainer};\n }\n `,\n\n variantOutlinedDanger: css`\n border: 1px solid ${cssVar.colorErrorBorder};\n\n &:hover {\n border: 1px solid ${cssVar.colorErrorBorder};\n }\n `,\n\n variantOutlinedWithoutHover: css`\n border: 1px solid ${cssVar.colorBorderSecondary};\n background: ${cssVar.colorBgContainer};\n `,\n})) as LobeCustomStylish;\n"],"mappings":";;;;;;;;;AAWA,MAAM,WAAW,SAAS;;;;;;;;;;;AAY1B,MAAa,gBAAgB,oBAAoB,EAAE,KAAK,cAAc;CACpE,QAAQ,GAAG;aACA,OAAO,UAAU;kBACZ,OAAO,mBAAmB;;;eAG7B,OAAO,UAAU;oBACZ,OAAO,UAAU;;;CAInC,MAAM,GAAG;;;CAIT,YAAY,GAAG;;;CAIf,iBAAiB,GAAG;;;;;;;;4BAQM,OAAO,UAAU;6CACA,OAAO,cAAc;;;;;;;;;;CAWhE,UAAU,GAAG;;;;CAKb,mBAAmB,GAAG;;;;QAIhB,OAAO,KAAK;QACZ,OAAO,QAAQ;QACf,OAAO,SAAS;QAChB,OAAO,KAAK;;;oBAGA,SAAS;;CAG3B,aAAa,GAAG;;;;;;;;CAShB,gBAAgB,GAAG;;aAER,OAAO,mBAAmB;;;eAGxB,OAAO,UAAU;;;CAS9B,QAAQ,GAAG;;qBAEQ,OAAO,YAAY;yBACf,OAAO,YAAY;uBACrB,OAAO,qBAAqB;uBAC5B,OAAO,qBAAqB;;CAGjD,mBAAmB,GAAG;;;;;;oBAMJ,OAAO,kBAAkB;;;CAS3C,yBAAyB,GAAG;;;;;;oBAMV,OAAO,aAAa;oCACJ,OAAO,aAAa;;;CAItD,+BAA+B,GAAG;;;;;CAMlC,eAAe,GAAG;kBACF,OAAO,kBAAkB;;;oBAGvB,OAAO,mBAAmB;;;CAS5C,qBAAqB,GAAG;kBACR,OAAO,aAAa;;;oBAGlB,OAAO,kBAAkB;;;CAI3C,2BAA2B,GAAG;kBACd,OAAO,kBAAkB;;CAGzC,iBAAiB,GAAG;wBACE,OAAO,qBAAqB;kBAClC,OAAO,iBAAiB;;;0BAGhB,OAAO,YAAY;oBACzB,OAAO,iBAAiB;;;CAI1C,uBAAuB,GAAG;wBACJ,OAAO,iBAAiB;;;0BAGtB,OAAO,iBAAiB;;;CAIhD,6BAA6B,GAAG;wBACV,OAAO,qBAAqB;kBAClC,OAAO,iBAAiB;;CAEzC,EAAE"}
1
+ {"version":3,"file":"customStylishStatic.mjs","names":[],"sources":["../../../src/styles/theme/customStylishStatic.ts"],"sourcesContent":["import { createStaticStyles, keyframes } from 'antd-style';\n\nimport type { LobeCustomStylish } from '@/types/customStylish';\n\n/**\n * Static version of custom stylish utilities.\n * This can be used with createStaticStyles for better performance.\n *\n * Note: Some styles that depend on isDarkMode or custom tokens may have limitations.\n * For full dynamic support, use the regular customStylish from './customStylish'.\n */\nconst gradient = keyframes`\n 0% {\n background-position: 0% 50%;\n }\n 50% {\n background-position: 100% 50%;\n }\n 100% {\n background-position: 0% 50%;\n }\n`;\n\nexport const staticStylish = createStaticStyles(({ css, cssVar }) => ({\n active: css`\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFill};\n }\n `,\n\n blur: css`\n backdrop-filter: saturate(150%) blur(10px);\n `,\n\n blurStrong: css`\n backdrop-filter: saturate(150%) blur(36px);\n `,\n\n bottomScrollbar: css`\n ::-webkit-scrollbar {\n width: 0;\n height: 4px;\n background-color: transparent;\n\n &-thumb {\n border-radius: 4px;\n background-color: ${cssVar.colorFill};\n transition: background-color 500ms ${cssVar.motionEaseOut};\n }\n\n &-corner {\n display: none;\n width: 0;\n height: 0;\n }\n }\n `,\n\n disabled: css`\n cursor: not-allowed;\n opacity: 0.5;\n `,\n\n gradientAnimation: css`\n border-radius: inherit;\n background-image: linear-gradient(\n -45deg,\n ${cssVar.gold},\n ${cssVar.magenta},\n ${cssVar.geekblue},\n ${cssVar.cyan}\n );\n background-size: 400% 400%;\n animation: 5s ${gradient} 5s ease infinite;\n `,\n\n noScrollbar: css`\n ::-webkit-scrollbar {\n display: none;\n width: 0;\n height: 0;\n background-color: transparent;\n }\n `,\n\n resetLinkColor: css`\n cursor: pointer;\n color: ${cssVar.colorTextSecondary};\n\n &:hover {\n color: ${cssVar.colorText};\n }\n `,\n\n /**\n * Shadow style using CSS variables.\n * Note: This uses CSS variables which automatically adapt to light/dark mode.\n * For more control, use the dynamic version from customStylish.\n */\n shadow: css`\n box-shadow:\n 0 1px 0 -1px ${cssVar.colorBorder},\n 0 1px 2px -0.5px ${cssVar.colorBorder},\n 0 2px 2px -1px ${cssVar.colorBorderSecondary},\n 0 3px 6px -4px ${cssVar.colorBorderSecondary};\n `,\n\n variantBorderless: css`\n border: none;\n background: none;\n box-shadow: none;\n\n &:hover {\n background: ${cssVar.colorFillTertiary};\n }\n `,\n\n /**\n * Variant borderless danger style.\n * Note: Uses colorErrorBg as fallback since colorErrorFillTertiary is not in cssVar.\n * For exact match, use the dynamic version from customStylish.\n */\n variantBorderlessDanger: css`\n border: none;\n background: none;\n box-shadow: none;\n\n &:hover {\n background: ${cssVar.colorErrorBg};\n box-shadow: inset 0 0 0 1px ${cssVar.colorErrorBg};\n }\n `,\n\n variantBorderlessWithoutHover: css`\n border: none;\n background: none;\n box-shadow: none;\n `,\n\n variantFilled: css`\n background: ${cssVar.colorFillTertiary};\n\n &:hover {\n background: ${cssVar.colorFillSecondary};\n }\n `,\n\n /**\n * Variant filled danger style.\n * Note: Uses colorErrorBg as fallback since colorErrorFillTertiary/Secondary are not in cssVar.\n * For exact match, use the dynamic version from customStylish.\n */\n variantFilledDanger: css`\n background: ${cssVar.colorErrorBg};\n\n &:hover {\n background: ${cssVar.colorErrorBgHover};\n }\n `,\n\n variantFilledWithoutHover: css`\n background: ${cssVar.colorFillTertiary};\n `,\n\n variantOutlined: css`\n border: 1px solid ${cssVar.colorBorderSecondary};\n background: ${cssVar.colorBgContainer};\n\n &:hover {\n border: 1px solid ${cssVar.colorBorder};\n background: ${cssVar.colorBgContainer};\n }\n `,\n\n variantOutlinedDanger: css`\n border: 1px solid ${cssVar.colorErrorBorder};\n\n &:hover {\n border: 1px solid ${cssVar.colorErrorBorder};\n }\n `,\n\n variantOutlinedWithoutHover: css`\n border: 1px solid ${cssVar.colorBorderSecondary};\n background: ${cssVar.colorBgContainer};\n `,\n})) as LobeCustomStylish;\n"],"mappings":";;;;;;;;;AAWA,MAAM,WAAW,SAAS;;;;;;;;;;;AAY1B,MAAa,gBAAgB,oBAAoB,EAAE,KAAK,cAAc;CACpE,QAAQ,GAAG;aACA,OAAO,UAAU;kBACZ,OAAO,mBAAmB;;;eAG7B,OAAO,UAAU;oBACZ,OAAO,UAAU;;;CAInC,MAAM,GAAG;;;CAIT,YAAY,GAAG;;;CAIf,iBAAiB,GAAG;;;;;;;;4BAQM,OAAO,UAAU;6CACA,OAAO,cAAc;;;;;;;;;;CAWhE,UAAU,GAAG;;;;CAKb,mBAAmB,GAAG;;;;QAIhB,OAAO,KAAK;QACZ,OAAO,QAAQ;QACf,OAAO,SAAS;QAChB,OAAO,KAAK;;;oBAGA,SAAS;;CAG3B,aAAa,GAAG;;;;;;;;CAShB,gBAAgB,GAAG;;aAER,OAAO,mBAAmB;;;eAGxB,OAAO,UAAU;;;;;;;;CAS9B,QAAQ,GAAG;;qBAEQ,OAAO,YAAY;yBACf,OAAO,YAAY;uBACrB,OAAO,qBAAqB;uBAC5B,OAAO,qBAAqB;;CAGjD,mBAAmB,GAAG;;;;;;oBAMJ,OAAO,kBAAkB;;;;;;;;CAS3C,yBAAyB,GAAG;;;;;;oBAMV,OAAO,aAAa;oCACJ,OAAO,aAAa;;;CAItD,+BAA+B,GAAG;;;;;CAMlC,eAAe,GAAG;kBACF,OAAO,kBAAkB;;;oBAGvB,OAAO,mBAAmB;;;;;;;;CAS5C,qBAAqB,GAAG;kBACR,OAAO,aAAa;;;oBAGlB,OAAO,kBAAkB;;;CAI3C,2BAA2B,GAAG;kBACd,OAAO,kBAAkB;;CAGzC,iBAAiB,GAAG;wBACE,OAAO,qBAAqB;kBAClC,OAAO,iBAAiB;;;0BAGhB,OAAO,YAAY;oBACzB,OAAO,iBAAiB;;;CAI1C,uBAAuB,GAAG;wBACJ,OAAO,iBAAiB;;;0BAGtB,OAAO,iBAAiB;;;CAIhD,6BAA6B,GAAG;wBACV,OAAO,qBAAqB;kBAClC,OAAO,iBAAiB;;CAEzC,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"base.mjs","names":[],"sources":["../../../../src/styles/theme/token/base.ts"],"sourcesContent":["import type { AliasToken } from 'antd/es/theme/interface';\n\nconst FONT_EMOJI = `\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Apple Color Emoji\",\"Twemoji Mozilla\",\"Noto Color Emoji\",\"Android Emoji\"`;\nconst FONT_EN = `\"HarmonyOS Sans\",\"Segoe UI\",\"SF Pro Display\",-apple-system,BlinkMacSystemFont,Roboto,Oxygen,Ubuntu,Cantarell,\"Open Sans\",\"Helvetica Neue\",sans-serif`;\nconst FONT_CN = `\"HarmonyOS Sans SC\",\"PingFang SC\",\"Hiragino Sans GB\",\"Microsoft Yahei UI\",\"Microsoft Yahei\",\"Source Han Sans CN\",sans-serif`;\nconst FONT_CODE = `Hack,ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas`;\n\nexport const baseToken: Partial<AliasToken> = {\n borderRadius: 8,\n borderRadiusLG: 12,\n borderRadiusSM: 6,\n borderRadiusXS: 4,\n controlHeight: 36,\n fontFamily: [FONT_EN, FONT_CN, FONT_EMOJI].join(','),\n fontFamilyCode: [FONT_CODE, FONT_CN, FONT_EMOJI].join(','),\n};\n"],"mappings":";AAEA,MAAM,aAAa;AACnB,MAAM,UAAU;AAChB,MAAM,UAAU;AAGhB,MAAa,YAAiC;CAC5C,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,eAAe;CACf,YAAY;EAAC;EAAS;EAAS;EAAW,CAAC,KAAK,IAAI;CACpD,gBAAgB;EATA;EASY;EAAS;EAAW,CAAC,KAAK,IAAI;CAC3D"}
1
+ {"version":3,"file":"base.mjs","names":[],"sources":["../../../../src/styles/theme/token/base.ts"],"sourcesContent":["import type { AliasToken } from 'antd/es/theme/interface';\n\nconst FONT_EMOJI = `\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Apple Color Emoji\",\"Twemoji Mozilla\",\"Noto Color Emoji\",\"Android Emoji\"`;\nconst FONT_EN = `\"HarmonyOS Sans\",\"Segoe UI\",\"SF Pro Display\",-apple-system,BlinkMacSystemFont,Roboto,Oxygen,Ubuntu,Cantarell,\"Open Sans\",\"Helvetica Neue\",sans-serif`;\nconst FONT_CN = `\"HarmonyOS Sans SC\",\"PingFang SC\",\"Hiragino Sans GB\",\"Microsoft Yahei UI\",\"Microsoft Yahei\",\"Source Han Sans CN\",sans-serif`;\nconst FONT_CODE = `Hack,ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas`;\n\nexport const baseToken: Partial<AliasToken> = {\n borderRadius: 8,\n borderRadiusLG: 12,\n borderRadiusSM: 6,\n borderRadiusXS: 4,\n controlHeight: 36,\n fontFamily: [FONT_EN, FONT_CN, FONT_EMOJI].join(','),\n fontFamilyCode: [FONT_CODE, FONT_CN, FONT_EMOJI].join(','),\n};\n"],"mappings":";AAEA,MAAM,aAAa;AACnB,MAAM,UAAU;AAChB,MAAM,UAAU;AAGhB,MAAa,YAAiC;CAC5C,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,eAAe;CACf,YAAY;EAAC;EAAS;EAAS;EAAW,CAAC,KAAK,IAAI;CACpD,gBAAgB;EAAC;EAAW;EAAS;EAAW,CAAC,KAAK,IAAI;CAC3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"blobToPng.mjs","names":[],"sources":["../../src/utils/blobToPng.ts"],"sourcesContent":["/**\n * Convert image blob to PNG format.\n * Clipboard API only supports image/png and image/svg+xml.\n * WebP, JPEG and other formats need to be converted for clipboard copy.\n */\nexport const blobToPng = (blob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(blob);\n\n img.onload = () => {\n URL.revokeObjectURL(url);\n const canvas = document.createElement('canvas');\n canvas.width = img.naturalWidth;\n canvas.height = img.naturalHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Canvas context not available'));\n return;\n }\n ctx.drawImage(img, 0, 0);\n canvas.toBlob(\n (pngBlob) => {\n if (pngBlob) {\n resolve(pngBlob);\n } else {\n reject(new Error('Failed to convert to PNG'));\n }\n },\n 'image/png',\n 1,\n );\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load image'));\n };\n\n img.src = url;\n });\n\nconst CLIPBOARD_SUPPORTED_TYPES = ['image/png', 'image/svg+xml'] as const;\n\nexport const getClipboardBlob = async (\n blob: Blob,\n): Promise<{ 'image/png': Blob } | { 'image/svg+xml': Blob }> => {\n const type = (blob.type || '').toLowerCase();\n\n if (type === 'image/png' || type === 'image/svg+xml') {\n return { [type]: blob } as { 'image/png': Blob } | { 'image/svg+xml': Blob };\n }\n\n const pngBlob = await blobToPng(blob);\n return { 'image/png': pngBlob };\n};\n"],"mappings":";;;;;;AAKA,MAAa,aAAa,SACxB,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,MAAM,IAAI,gBAAgB,KAAK;AAErC,KAAI,eAAe;AACjB,MAAI,gBAAgB,IAAI;EACxB,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,QAAQ,IAAI;AACnB,SAAO,SAAS,IAAI;EACpB,MAAM,MAAM,OAAO,WAAW,KAAK;AACnC,MAAI,CAAC,KAAK;AACR,0BAAO,IAAI,MAAM,+BAA+B,CAAC;AACjD;;AAEF,MAAI,UAAU,KAAK,GAAG,EAAE;AACxB,SAAO,QACJ,YAAY;AACX,OAAI,QACF,SAAQ,QAAQ;OAEhB,wBAAO,IAAI,MAAM,2BAA2B,CAAC;KAGjD,aACA,EACD;;AAGH,KAAI,gBAAgB;AAClB,MAAI,gBAAgB,IAAI;AACxB,yBAAO,IAAI,MAAM,uBAAuB,CAAC;;AAG3C,KAAI,MAAM;EACV;AAIJ,MAAa,mBAAmB,OAC9B,SAC+D;CAC/D,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;AAE5C,KAAI,SAAS,eAAe,SAAS,gBACnC,QAAO,GAAG,OAAO,MAAM;AAIzB,QAAO,EAAE,aADO,MAAM,UAAU,KAAK,EACN"}
1
+ {"version":3,"file":"blobToPng.mjs","names":[],"sources":["../../src/utils/blobToPng.ts"],"sourcesContent":["/**\n * Convert image blob to PNG format.\n * Clipboard API only supports image/png and image/svg+xml.\n * WebP, JPEG and other formats need to be converted for clipboard copy.\n */\nexport const blobToPng = (blob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(blob);\n\n img.onload = () => {\n URL.revokeObjectURL(url);\n const canvas = document.createElement('canvas');\n canvas.width = img.naturalWidth;\n canvas.height = img.naturalHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Canvas context not available'));\n return;\n }\n ctx.drawImage(img, 0, 0);\n canvas.toBlob(\n (pngBlob) => {\n if (pngBlob) {\n resolve(pngBlob);\n } else {\n reject(new Error('Failed to convert to PNG'));\n }\n },\n 'image/png',\n 1,\n );\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load image'));\n };\n\n img.src = url;\n });\n\nconst CLIPBOARD_SUPPORTED_TYPES = ['image/png', 'image/svg+xml'] as const;\n\nexport const getClipboardBlob = async (\n blob: Blob,\n): Promise<{ 'image/png': Blob } | { 'image/svg+xml': Blob }> => {\n const type = (blob.type || '').toLowerCase();\n\n if (type === 'image/png' || type === 'image/svg+xml') {\n return { [type]: blob } as { 'image/png': Blob } | { 'image/svg+xml': Blob };\n }\n\n const pngBlob = await blobToPng(blob);\n return { 'image/png': pngBlob };\n};\n"],"mappings":";;;;;;AAKA,MAAa,aAAa,SACxB,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,MAAM,IAAI,gBAAgB,KAAK;AAErC,KAAI,eAAe;AACjB,MAAI,gBAAgB,IAAI;EACxB,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,QAAQ,IAAI;AACnB,SAAO,SAAS,IAAI;EACpB,MAAM,MAAM,OAAO,WAAW,KAAK;AACnC,MAAI,CAAC,KAAK;AACR,0BAAO,IAAI,MAAM,+BAA+B,CAAC;AACjD;;AAEF,MAAI,UAAU,KAAK,GAAG,EAAE;AACxB,SAAO,QACJ,YAAY;AACX,OAAI,QACF,SAAQ,QAAQ;OAEhB,wBAAO,IAAI,MAAM,2BAA2B,CAAC;KAGjD,aACA,EACD;;AAGH,KAAI,gBAAgB;AAClB,MAAI,gBAAgB,IAAI;AACxB,yBAAO,IAAI,MAAM,uBAAuB,CAAC;;AAG3C,KAAI,MAAM;EACV;AAIJ,MAAa,mBAAmB,OAC9B,SAC+D;CAC/D,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;AAE5C,KAAI,SAAS,eAAe,SAAS,gBACnC,QAAO,GAAG,OAAO,MAAM;AAIzB,QAAO,EAAE,aAAa,MADA,UAAU,KAAK,EACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"placement.mjs","names":[],"sources":["../../src/utils/placement.ts"],"sourcesContent":["import type { Placement as FloatingUIPlacement } from '@floating-ui/react';\n\n/**\n * Base UI uses a small set of string literal unions for alignment and side.\n * We re-declare them here to avoid importing internal/non-exported Base UI paths.\n */\nexport type Side = 'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start';\nexport type Align = 'start' | 'center' | 'end';\n\nexport type PlacementConfig = {\n align: Align;\n side: Side;\n};\n\n/**\n * All supported placement values\n * - Unified placement names for Tooltip, Popover, and DropdownMenu\n * - Ant Design style: topLeft, topCenter, topRight, etc.\n * - Additional aliases: top (same as topCenter), bottom (same as bottomCenter)\n */\nexport type Placement =\n | 'top'\n | 'topLeft'\n | 'topCenter'\n | 'topRight'\n | 'bottom'\n | 'bottomLeft'\n | 'bottomCenter'\n | 'bottomRight'\n | 'left'\n | 'leftTop'\n | 'leftBottom'\n | 'right'\n | 'rightTop'\n | 'rightBottom';\n\nconst top: PlacementConfig = { align: 'center', side: 'top' };\nconst topLeft: PlacementConfig = { align: 'start', side: 'top' };\nconst topRight: PlacementConfig = { align: 'end', side: 'top' };\nconst bottom: PlacementConfig = { align: 'center', side: 'bottom' };\nconst bottomLeft: PlacementConfig = { align: 'start', side: 'bottom' };\nconst bottomRight: PlacementConfig = { align: 'end', side: 'bottom' };\nconst left: PlacementConfig = { align: 'center', side: 'left' };\nconst leftTop: PlacementConfig = { align: 'start', side: 'left' };\nconst leftBottom: PlacementConfig = { align: 'end', side: 'left' };\nconst right: PlacementConfig = { align: 'center', side: 'right' };\nconst rightTop: PlacementConfig = { align: 'start', side: 'right' };\nconst rightBottom: PlacementConfig = { align: 'end', side: 'right' };\n\n/**\n * Map of placement values to Base UI placement config\n * Used by Popover and DropdownMenu components\n */\nexport const placementMap: Record<Placement, PlacementConfig> = {\n bottom,\n bottomCenter: bottom,\n bottomLeft,\n bottomRight,\n left,\n leftBottom,\n leftTop,\n right,\n rightBottom,\n rightTop,\n top,\n topCenter: top,\n topLeft,\n topRight,\n};\n\n/**\n * Convert unified Placement to Floating UI placement format\n * Used by Tooltip component which uses @floating-ui/react\n *\n * @param placement - Unified placement value\n * @returns Floating UI placement (e.g., 'top-start', 'bottom-end')\n */\nexport const toFloatingUIPlacement = (placement?: Placement): FloatingUIPlacement => {\n if (!placement) return 'top';\n\n switch (placement) {\n case 'topLeft': {\n return 'top-start';\n }\n case 'top':\n case 'topCenter': {\n return 'top';\n }\n case 'topRight': {\n return 'top-end';\n }\n case 'bottomLeft': {\n return 'bottom-start';\n }\n case 'bottom':\n case 'bottomCenter': {\n return 'bottom';\n }\n case 'bottomRight': {\n return 'bottom-end';\n }\n case 'leftTop': {\n return 'left-start';\n }\n case 'left': {\n return 'left';\n }\n case 'leftBottom': {\n return 'left-end';\n }\n case 'rightTop': {\n return 'right-start';\n }\n case 'right': {\n return 'right';\n }\n case 'rightBottom': {\n return 'right-end';\n }\n default: {\n return 'top';\n }\n }\n};\n"],"mappings":";AAoCA,MAAM,MAAuB;CAAE,OAAO;CAAU,MAAM;CAAO;AAC7D,MAAM,UAA2B;CAAE,OAAO;CAAS,MAAM;CAAO;AAChE,MAAM,WAA4B;CAAE,OAAO;CAAO,MAAM;CAAO;AAC/D,MAAM,SAA0B;CAAE,OAAO;CAAU,MAAM;CAAU;;;;;AAcnE,MAAa,eAAmD;CAC9D;CACA,cAAc;CACd,YAhBkC;EAAE,OAAO;EAAS,MAAM;EAAU;CAiBpE,aAhBmC;EAAE,OAAO;EAAO,MAAM;EAAU;CAiBnE,MAhB4B;EAAE,OAAO;EAAU,MAAM;EAAQ;CAiB7D,YAfkC;EAAE,OAAO;EAAO,MAAM;EAAQ;CAgBhE,SAjB+B;EAAE,OAAO;EAAS,MAAM;EAAQ;CAkB/D,OAhB6B;EAAE,OAAO;EAAU,MAAM;EAAS;CAiB/D,aAfmC;EAAE,OAAO;EAAO,MAAM;EAAS;CAgBlE,UAjBgC;EAAE,OAAO;EAAS,MAAM;EAAS;CAkBjE;CACA,WAAW;CACX;CACA;CACD;;;;;;;;AASD,MAAa,yBAAyB,cAA+C;AACnF,KAAI,CAAC,UAAW,QAAO;AAEvB,SAAQ,WAAR;EACE,KAAK,UACH,QAAO;EAET,KAAK;EACL,KAAK,YACH,QAAO;EAET,KAAK,WACH,QAAO;EAET,KAAK,aACH,QAAO;EAET,KAAK;EACL,KAAK,eACH,QAAO;EAET,KAAK,cACH,QAAO;EAET,KAAK,UACH,QAAO;EAET,KAAK,OACH,QAAO;EAET,KAAK,aACH,QAAO;EAET,KAAK,WACH,QAAO;EAET,KAAK,QACH,QAAO;EAET,KAAK,cACH,QAAO;EAET,QACE,QAAO"}
1
+ {"version":3,"file":"placement.mjs","names":[],"sources":["../../src/utils/placement.ts"],"sourcesContent":["import type { Placement as FloatingUIPlacement } from '@floating-ui/react';\n\n/**\n * Base UI uses a small set of string literal unions for alignment and side.\n * We re-declare them here to avoid importing internal/non-exported Base UI paths.\n */\nexport type Side = 'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start';\nexport type Align = 'start' | 'center' | 'end';\n\nexport type PlacementConfig = {\n align: Align;\n side: Side;\n};\n\n/**\n * All supported placement values\n * - Unified placement names for Tooltip, Popover, and DropdownMenu\n * - Ant Design style: topLeft, topCenter, topRight, etc.\n * - Additional aliases: top (same as topCenter), bottom (same as bottomCenter)\n */\nexport type Placement =\n | 'top'\n | 'topLeft'\n | 'topCenter'\n | 'topRight'\n | 'bottom'\n | 'bottomLeft'\n | 'bottomCenter'\n | 'bottomRight'\n | 'left'\n | 'leftTop'\n | 'leftBottom'\n | 'right'\n | 'rightTop'\n | 'rightBottom';\n\nconst top: PlacementConfig = { align: 'center', side: 'top' };\nconst topLeft: PlacementConfig = { align: 'start', side: 'top' };\nconst topRight: PlacementConfig = { align: 'end', side: 'top' };\nconst bottom: PlacementConfig = { align: 'center', side: 'bottom' };\nconst bottomLeft: PlacementConfig = { align: 'start', side: 'bottom' };\nconst bottomRight: PlacementConfig = { align: 'end', side: 'bottom' };\nconst left: PlacementConfig = { align: 'center', side: 'left' };\nconst leftTop: PlacementConfig = { align: 'start', side: 'left' };\nconst leftBottom: PlacementConfig = { align: 'end', side: 'left' };\nconst right: PlacementConfig = { align: 'center', side: 'right' };\nconst rightTop: PlacementConfig = { align: 'start', side: 'right' };\nconst rightBottom: PlacementConfig = { align: 'end', side: 'right' };\n\n/**\n * Map of placement values to Base UI placement config\n * Used by Popover and DropdownMenu components\n */\nexport const placementMap: Record<Placement, PlacementConfig> = {\n bottom,\n bottomCenter: bottom,\n bottomLeft,\n bottomRight,\n left,\n leftBottom,\n leftTop,\n right,\n rightBottom,\n rightTop,\n top,\n topCenter: top,\n topLeft,\n topRight,\n};\n\n/**\n * Convert unified Placement to Floating UI placement format\n * Used by Tooltip component which uses @floating-ui/react\n *\n * @param placement - Unified placement value\n * @returns Floating UI placement (e.g., 'top-start', 'bottom-end')\n */\nexport const toFloatingUIPlacement = (placement?: Placement): FloatingUIPlacement => {\n if (!placement) return 'top';\n\n switch (placement) {\n case 'topLeft': {\n return 'top-start';\n }\n case 'top':\n case 'topCenter': {\n return 'top';\n }\n case 'topRight': {\n return 'top-end';\n }\n case 'bottomLeft': {\n return 'bottom-start';\n }\n case 'bottom':\n case 'bottomCenter': {\n return 'bottom';\n }\n case 'bottomRight': {\n return 'bottom-end';\n }\n case 'leftTop': {\n return 'left-start';\n }\n case 'left': {\n return 'left';\n }\n case 'leftBottom': {\n return 'left-end';\n }\n case 'rightTop': {\n return 'right-start';\n }\n case 'right': {\n return 'right';\n }\n case 'rightBottom': {\n return 'right-end';\n }\n default: {\n return 'top';\n }\n }\n};\n"],"mappings":";AAoCA,MAAM,MAAuB;CAAE,OAAO;CAAU,MAAM;CAAO;AAC7D,MAAM,UAA2B;CAAE,OAAO;CAAS,MAAM;CAAO;AAChE,MAAM,WAA4B;CAAE,OAAO;CAAO,MAAM;CAAO;AAC/D,MAAM,SAA0B;CAAE,OAAO;CAAU,MAAM;CAAU;;;;;AAcnE,MAAa,eAAmD;CAC9D;CACA,cAAc;CACd;EAhBoC,OAAO;EAAS,MAAM;EAgB1D;CACA;EAhBqC,OAAO;EAAO,MAAM;EAgBzD;CACA;EAhB8B,OAAO;EAAU,MAAM;EAgBrD;CACA;EAfoC,OAAO;EAAO,MAAM;EAexD;CACA;EAjBiC,OAAO;EAAS,MAAM;EAiBvD;CACA;EAhB+B,OAAO;EAAU,MAAM;EAgBtD;CACA;EAfqC,OAAO;EAAO,MAAM;EAezD;CACA;EAjBkC,OAAO;EAAS,MAAM;EAiBxD;CACA;CACA,WAAW;CACX;CACA;CACD;;;;;;;;AASD,MAAa,yBAAyB,cAA+C;AACnF,KAAI,CAAC,UAAW,QAAO;AAEvB,SAAQ,WAAR;EACE,KAAK,UACH,QAAO;EAET,KAAK;EACL,KAAK,YACH,QAAO;EAET,KAAK,WACH,QAAO;EAET,KAAK,aACH,QAAO;EAET,KAAK;EACL,KAAK,eACH,QAAO;EAET,KAAK,cACH,QAAO;EAET,KAAK,UACH,QAAO;EAET,KAAK,OACH,QAAO;EAET,KAAK,aACH,QAAO;EAET,KAAK,WACH,QAAO;EAET,KAAK,QACH,QAAO;EAET,KAAK,cACH,QAAO;EAET,QACE,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"smoothCorners.mjs","names":[],"sources":["../../src/utils/smoothCorners.ts"],"sourcesContent":["import type { CSSProperties } from 'react';\n\n/**\n * Smooth Corners utility using Base64 SVG mask\n * A simpler alternative to CSS Houdini Paint API\n */\n\n/**\n * Generate a smooth corners SVG path using superellipse formula\n * @param size - The size of the shape\n * @param n - The superellipse exponent (4 = squircle, 5 = iOS style)\n */\nconst generateSuperellipsePath = (size: number, n: number = 4): string => {\n const r = size / 2;\n const points: string[] = [];\n\n // Generate points for the superellipse\n for (let i = 0; i <= 360; i += 2) {\n const angle = (i * Math.PI) / 180;\n const cosAngle = Math.cos(angle);\n const sinAngle = Math.sin(angle);\n\n // Superellipse formula\n const x = r * Math.sign(cosAngle) * Math.pow(Math.abs(cosAngle), 2 / n);\n const y = r * Math.sign(sinAngle) * Math.pow(Math.abs(sinAngle), 2 / n);\n\n points.push(`${r + x},${r + y}`);\n }\n\n return `M${points[0]}L${points.slice(1).join('L')}Z`;\n};\n\n/**\n * Create a Base64 encoded SVG mask for smooth corners\n * @param options - Configuration options\n */\nexport const createSmoothCornersMask = (\n options: {\n cornerValue?: number;\n size?: number;\n } = {},\n): string => {\n const { size = 100, cornerValue = 4 } = options;\n\n const path = generateSuperellipsePath(size, cornerValue);\n\n const svg = `\n <svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"${path}\" fill=\"white\"/>\n </svg>\n `\n .trim()\n .replaceAll(/\\s+/g, ' ');\n\n return `data:image/svg+xml;base64,${btoa(svg)}`;\n};\n\n/**\n * Create a Base64 encoded SVG mask for circle shape\n * @param options - Configuration options\n */\nexport const createCircleMask = (\n options: {\n size?: number;\n } = {},\n): string => {\n const { size = 100 } = options;\n const r = size / 2;\n\n const svg = `\n <svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"${r}\" cy=\"${r}\" r=\"${r}\" fill=\"white\"/>\n </svg>\n `\n .trim()\n .replaceAll(/\\s+/g, ' ');\n\n return `data:image/svg+xml;base64,${btoa(svg)}`;\n};\n\n/**\n * Create a Base64 encoded SVG mask for rounded rectangle\n * @param options - Configuration options\n */\nexport const createRoundedRectMask = (\n options: {\n borderRadius?: number;\n size?: number;\n } = {},\n): string => {\n const { size = 100, borderRadius = 15 } = options;\n\n const svg = `\n <svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"0\" y=\"0\" width=\"${size}\" height=\"${size}\" rx=\"${borderRadius}\" ry=\"${borderRadius}\" fill=\"white\"/>\n </svg>\n `\n .trim()\n .replaceAll(/\\s+/g, ' ');\n\n return `data:image/svg+xml;base64,${btoa(svg)}`;\n};\n\n/**\n * Predefined smooth corners masks for common corner values\n */\nexport const SMOOTH_CORNER_MASKS = {\n // Basic shapes\n circle: createCircleMask(),\n // Superellipse shapes\n ios: createSmoothCornersMask({ cornerValue: 5 }),\n\n sharp: createSmoothCornersMask({ cornerValue: 6 }),\n smooth: createSmoothCornersMask({ cornerValue: 3 }),\n square: createRoundedRectMask({ borderRadius: 15 }),\n squircle: createSmoothCornersMask({ cornerValue: 4 }),\n} as const;\n\n/**\n * CSS helper to apply smooth corners mask\n */\nexport const getSmoothCornersMaskStyle = (\n cornerType: keyof typeof SMOOTH_CORNER_MASKS = 'squircle',\n): CSSProperties => {\n return {\n // WebKit prefix for better browser support\n WebkitMaskImage: `url(\"${SMOOTH_CORNER_MASKS[cornerType]}\")`,\n\n WebkitMaskPosition: 'center',\n\n WebkitMaskRepeat: 'no-repeat',\n\n WebkitMaskSize: '100% 100%',\n\n maskImage: `url(\"${SMOOTH_CORNER_MASKS[cornerType]}\")`,\n maskPosition: 'center',\n maskRepeat: 'no-repeat',\n maskSize: '100% 100%',\n } as CSSProperties;\n};\n"],"mappings":";;;;;;;;;;AAYA,MAAM,4BAA4B,MAAc,IAAY,MAAc;CACxE,MAAM,IAAI,OAAO;CACjB,MAAM,SAAmB,EAAE;AAG3B,MAAK,IAAI,IAAI,GAAG,KAAK,KAAK,KAAK,GAAG;EAChC,MAAM,QAAS,IAAI,KAAK,KAAM;EAC9B,MAAM,WAAW,KAAK,IAAI,MAAM;EAChC,MAAM,WAAW,KAAK,IAAI,MAAM;EAGhC,MAAM,IAAI,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,EAAE,IAAI,EAAE;EACvE,MAAM,IAAI,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,EAAE,IAAI,EAAE;AAEvE,SAAO,KAAK,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI;;AAGlC,QAAO,IAAI,OAAO,GAAG,GAAG,OAAO,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;;;;;;AAOpD,MAAa,2BACX,UAGI,EAAE,KACK;CACX,MAAM,EAAE,OAAO,KAAK,cAAc,MAAM;CAIxC,MAAM,MAAM;kBACI,KAAK,YAAY,KAAK,iBAAiB,KAAK,GAAG,KAAK;iBAHvD,yBAAyB,MAAM,YAAY,CAIpC;;IAGjB,MAAM,CACN,WAAW,QAAQ,IAAI;AAE1B,QAAO,6BAA6B,KAAK,IAAI;;;;;;AAO/C,MAAa,oBACX,UAEI,EAAE,KACK;CACX,MAAM,EAAE,OAAO,QAAQ;CACvB,MAAM,IAAI,OAAO;CAEjB,MAAM,MAAM;kBACI,KAAK,YAAY,KAAK,iBAAiB,KAAK,GAAG,KAAK;oBAClD,EAAE,QAAQ,EAAE,OAAO,EAAE;;IAGpC,MAAM,CACN,WAAW,QAAQ,IAAI;AAE1B,QAAO,6BAA6B,KAAK,IAAI;;;;;;AAO/C,MAAa,yBACX,UAGI,EAAE,KACK;CACX,MAAM,EAAE,OAAO,KAAK,eAAe,OAAO;CAE1C,MAAM,MAAM;kBACI,KAAK,YAAY,KAAK,iBAAiB,KAAK,GAAG,KAAK;iCACrC,KAAK,YAAY,KAAK,QAAQ,aAAa,QAAQ,aAAa;;IAG5F,MAAM,CACN,WAAW,QAAQ,IAAI;AAE1B,QAAO,6BAA6B,KAAK,IAAI;;;;;AAM/C,MAAa,sBAAsB;CAEjC,QAAQ,kBAAkB;CAE1B,KAAK,wBAAwB,EAAE,aAAa,GAAG,CAAC;CAEhD,OAAO,wBAAwB,EAAE,aAAa,GAAG,CAAC;CAClD,QAAQ,wBAAwB,EAAE,aAAa,GAAG,CAAC;CACnD,QAAQ,sBAAsB,EAAE,cAAc,IAAI,CAAC;CACnD,UAAU,wBAAwB,EAAE,aAAa,GAAG,CAAC;CACtD"}
1
+ {"version":3,"file":"smoothCorners.mjs","names":[],"sources":["../../src/utils/smoothCorners.ts"],"sourcesContent":["import type { CSSProperties } from 'react';\n\n/**\n * Smooth Corners utility using Base64 SVG mask\n * A simpler alternative to CSS Houdini Paint API\n */\n\n/**\n * Generate a smooth corners SVG path using superellipse formula\n * @param size - The size of the shape\n * @param n - The superellipse exponent (4 = squircle, 5 = iOS style)\n */\nconst generateSuperellipsePath = (size: number, n: number = 4): string => {\n const r = size / 2;\n const points: string[] = [];\n\n // Generate points for the superellipse\n for (let i = 0; i <= 360; i += 2) {\n const angle = (i * Math.PI) / 180;\n const cosAngle = Math.cos(angle);\n const sinAngle = Math.sin(angle);\n\n // Superellipse formula\n const x = r * Math.sign(cosAngle) * Math.pow(Math.abs(cosAngle), 2 / n);\n const y = r * Math.sign(sinAngle) * Math.pow(Math.abs(sinAngle), 2 / n);\n\n points.push(`${r + x},${r + y}`);\n }\n\n return `M${points[0]}L${points.slice(1).join('L')}Z`;\n};\n\n/**\n * Create a Base64 encoded SVG mask for smooth corners\n * @param options - Configuration options\n */\nexport const createSmoothCornersMask = (\n options: {\n cornerValue?: number;\n size?: number;\n } = {},\n): string => {\n const { size = 100, cornerValue = 4 } = options;\n\n const path = generateSuperellipsePath(size, cornerValue);\n\n const svg = `\n <svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"${path}\" fill=\"white\"/>\n </svg>\n `\n .trim()\n .replaceAll(/\\s+/g, ' ');\n\n return `data:image/svg+xml;base64,${btoa(svg)}`;\n};\n\n/**\n * Create a Base64 encoded SVG mask for circle shape\n * @param options - Configuration options\n */\nexport const createCircleMask = (\n options: {\n size?: number;\n } = {},\n): string => {\n const { size = 100 } = options;\n const r = size / 2;\n\n const svg = `\n <svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"${r}\" cy=\"${r}\" r=\"${r}\" fill=\"white\"/>\n </svg>\n `\n .trim()\n .replaceAll(/\\s+/g, ' ');\n\n return `data:image/svg+xml;base64,${btoa(svg)}`;\n};\n\n/**\n * Create a Base64 encoded SVG mask for rounded rectangle\n * @param options - Configuration options\n */\nexport const createRoundedRectMask = (\n options: {\n borderRadius?: number;\n size?: number;\n } = {},\n): string => {\n const { size = 100, borderRadius = 15 } = options;\n\n const svg = `\n <svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"0\" y=\"0\" width=\"${size}\" height=\"${size}\" rx=\"${borderRadius}\" ry=\"${borderRadius}\" fill=\"white\"/>\n </svg>\n `\n .trim()\n .replaceAll(/\\s+/g, ' ');\n\n return `data:image/svg+xml;base64,${btoa(svg)}`;\n};\n\n/**\n * Predefined smooth corners masks for common corner values\n */\nexport const SMOOTH_CORNER_MASKS = {\n // Basic shapes\n circle: createCircleMask(),\n // Superellipse shapes\n ios: createSmoothCornersMask({ cornerValue: 5 }),\n\n sharp: createSmoothCornersMask({ cornerValue: 6 }),\n smooth: createSmoothCornersMask({ cornerValue: 3 }),\n square: createRoundedRectMask({ borderRadius: 15 }),\n squircle: createSmoothCornersMask({ cornerValue: 4 }),\n} as const;\n\n/**\n * CSS helper to apply smooth corners mask\n */\nexport const getSmoothCornersMaskStyle = (\n cornerType: keyof typeof SMOOTH_CORNER_MASKS = 'squircle',\n): CSSProperties => {\n return {\n // WebKit prefix for better browser support\n WebkitMaskImage: `url(\"${SMOOTH_CORNER_MASKS[cornerType]}\")`,\n\n WebkitMaskPosition: 'center',\n\n WebkitMaskRepeat: 'no-repeat',\n\n WebkitMaskSize: '100% 100%',\n\n maskImage: `url(\"${SMOOTH_CORNER_MASKS[cornerType]}\")`,\n maskPosition: 'center',\n maskRepeat: 'no-repeat',\n maskSize: '100% 100%',\n } as CSSProperties;\n};\n"],"mappings":";;;;;;;;;;AAYA,MAAM,4BAA4B,MAAc,IAAY,MAAc;CACxE,MAAM,IAAI,OAAO;CACjB,MAAM,SAAmB,EAAE;AAG3B,MAAK,IAAI,IAAI,GAAG,KAAK,KAAK,KAAK,GAAG;EAChC,MAAM,QAAS,IAAI,KAAK,KAAM;EAC9B,MAAM,WAAW,KAAK,IAAI,MAAM;EAChC,MAAM,WAAW,KAAK,IAAI,MAAM;EAGhC,MAAM,IAAI,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,EAAE,IAAI,EAAE;EACvE,MAAM,IAAI,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,EAAE,IAAI,EAAE;AAEvE,SAAO,KAAK,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI;;AAGlC,QAAO,IAAI,OAAO,GAAG,GAAG,OAAO,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;;;;;;AAOpD,MAAa,2BACX,UAGI,EAAE,KACK;CACX,MAAM,EAAE,OAAO,KAAK,cAAc,MAAM;CAIxC,MAAM,MAAM;kBACI,KAAK,YAAY,KAAK,iBAAiB,KAAK,GAAG,KAAK;iBAHvD,yBAAyB,MAAM,YAIzB,CAAC;;IAGjB,MAAM,CACN,WAAW,QAAQ,IAAI;AAE1B,QAAO,6BAA6B,KAAK,IAAI;;;;;;AAO/C,MAAa,oBACX,UAEI,EAAE,KACK;CACX,MAAM,EAAE,OAAO,QAAQ;CACvB,MAAM,IAAI,OAAO;CAEjB,MAAM,MAAM;kBACI,KAAK,YAAY,KAAK,iBAAiB,KAAK,GAAG,KAAK;oBAClD,EAAE,QAAQ,EAAE,OAAO,EAAE;;IAGpC,MAAM,CACN,WAAW,QAAQ,IAAI;AAE1B,QAAO,6BAA6B,KAAK,IAAI;;;;;;AAO/C,MAAa,yBACX,UAGI,EAAE,KACK;CACX,MAAM,EAAE,OAAO,KAAK,eAAe,OAAO;CAE1C,MAAM,MAAM;kBACI,KAAK,YAAY,KAAK,iBAAiB,KAAK,GAAG,KAAK;iCACrC,KAAK,YAAY,KAAK,QAAQ,aAAa,QAAQ,aAAa;;IAG5F,MAAM,CACN,WAAW,QAAQ,IAAI;AAE1B,QAAO,6BAA6B,KAAK,IAAI;;;;;AAM/C,MAAa,sBAAsB;CAEjC,QAAQ,kBAAkB;CAE1B,KAAK,wBAAwB,EAAE,aAAa,GAAG,CAAC;CAEhD,OAAO,wBAAwB,EAAE,aAAa,GAAG,CAAC;CAClD,QAAQ,wBAAwB,EAAE,aAAa,GAAG,CAAC;CACnD,QAAQ,sBAAsB,EAAE,cAAc,IAAI,CAAC;CACnD,UAAU,wBAAwB,EAAE,aAAa,GAAG,CAAC;CACtD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "5.9.2",
3
+ "version": "5.9.4",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",