@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.
- package/es/A/index.mjs.map +1 -1
- package/es/Accordion/Accordion.mjs.map +1 -1
- package/es/Alert/Alert.mjs.map +1 -1
- package/es/Checkbox/CheckboxGroup.mjs.map +1 -1
- package/es/ConfigProvider/index.mjs.map +1 -1
- package/es/DraggablePanel/DraggablePanel.mjs +19 -3
- package/es/DraggablePanel/DraggablePanel.mjs.map +1 -1
- package/es/DraggableSideNav/DraggableSideNav.mjs.map +1 -1
- package/es/EditorSlashMenu/useNormalizedItems.mjs.map +1 -1
- package/es/EmojiPicker/AvatarUploader.mjs.map +1 -1
- package/es/EmojiPicker/EmojiPicker.mjs.map +1 -1
- package/es/Form/components/FormFlatGroup.mjs.map +1 -1
- package/es/Grid/Grid.mjs.map +1 -1
- package/es/Highlighter/LangSelect.mjs.map +1 -1
- package/es/Highlighter/SyntaxHighlighter/StreamRenderer.mjs.map +1 -1
- package/es/Highlighter/const.mjs.map +1 -1
- package/es/Hotkey/Hotkey.mjs.map +1 -1
- package/es/HotkeyInput/HotkeyInput.mjs.map +1 -1
- package/es/Icon/style.mjs.map +1 -1
- package/es/Image/components/Toolbar.mjs.map +1 -1
- package/es/Img/index.mjs.map +1 -1
- package/es/Markdown/SyntaxMarkdown/StreamdownRender.mjs.map +1 -1
- package/es/Markdown/SyntaxMarkdown/useSmoothStreamContent.mjs.map +1 -1
- package/es/Markdown/components/CodeBlock.mjs.map +1 -1
- package/es/Markdown/markdown.style.mjs.map +1 -1
- package/es/Markdown/plugins/remarkBr.mjs.map +1 -1
- package/es/Markdown/plugins/remarkColor.mjs.map +1 -1
- package/es/Markdown/plugins/remarkGfmPlus.mjs.map +1 -1
- package/es/MaterialFileTypeIcon/MaterialFileTypeIcon.mjs.map +1 -1
- package/es/MaterialFileTypeIcon/utils.mjs.map +1 -1
- package/es/Mermaid/SyntaxMermaid/StaticMermaid.mjs.map +1 -1
- package/es/Mermaid/SyntaxMermaid/StreamMermaid.mjs.map +1 -1
- package/es/Modal/imperative.mjs.map +1 -1
- package/es/ScrollShadow/ScrollShadow.mjs.map +1 -1
- package/es/SortableList/SortableList.mjs.map +1 -1
- package/es/SortableList/components/SortableItem.mjs.map +1 -1
- package/es/Text/Text.mjs.map +1 -1
- package/es/ThemeProvider/ThemeProvider.mjs.map +1 -1
- package/es/Toc/style.mjs.map +1 -1
- package/es/Tooltip/TooltipGroup.mjs.map +1 -1
- package/es/Tooltip/TooltipInGroup.mjs.map +1 -1
- package/es/Tooltip/TooltipStandalone.mjs.map +1 -1
- package/es/Tooltip/useMergedTooltipProps.mjs.map +1 -1
- package/es/awesome/Spline/ParentSize.mjs.map +1 -1
- package/es/awesome/TypewriterEffect/TypewriterEffect.mjs.map +1 -1
- package/es/base-ui/ContextMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/ContextMenu/store.mjs.map +1 -1
- package/es/base-ui/DropdownMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/FloatingSheet/FloatingSheet.mjs.map +1 -1
- package/es/base-ui/FloatingSheet/useSheetDrag.mjs.map +1 -1
- package/es/base-ui/FloatingSheet/useSnapPoints.mjs.map +1 -1
- package/es/base-ui/Modal/atoms.mjs.map +1 -1
- package/es/base-ui/Modal/imperative.mjs.map +1 -1
- package/es/base-ui/Modal/style.mjs +1 -0
- package/es/base-ui/Modal/style.mjs.map +1 -1
- package/es/base-ui/Modal/zIndexManager.mjs.map +1 -1
- package/es/base-ui/Popover/useMergedPopoverProps.mjs.map +1 -1
- package/es/base-ui/Select/Select.mjs.map +1 -1
- package/es/base-ui/Toast/imperative.mjs.map +1 -1
- package/es/brand/BrandLoading/index.mjs.map +1 -1
- package/es/brand/Logo3d/index.mjs.map +1 -1
- package/es/chat/ChatItem/components/Actions.mjs.map +1 -1
- package/es/chat/EditableMessageList/EditableMessageList.mjs.map +1 -1
- package/es/hooks/useHighlight.mjs.map +1 -1
- package/es/hooks/useMarkdown/latex.mjs.map +1 -1
- package/es/hooks/useMarkdown/useMarkdownComponents.mjs.map +1 -1
- package/es/hooks/useMarkdown/useMarkdownContent.mjs.map +1 -1
- package/es/hooks/useMermaid.mjs.map +1 -1
- package/es/hooks/useNativeButton.mjs.map +1 -1
- package/es/hooks/useStreamHighlight.mjs.map +1 -1
- package/es/hooks/useStreamMermaid.mjs.map +1 -1
- package/es/i18n/useTranslation.mjs.map +1 -1
- package/es/mdx/mdxComponents/CodeBlock.mjs.map +1 -1
- package/es/styles/customTheme.mjs.map +1 -1
- package/es/styles/theme/customStylishStatic.mjs +15 -0
- package/es/styles/theme/customStylishStatic.mjs.map +1 -1
- package/es/styles/theme/token/base.mjs.map +1 -1
- package/es/utils/blobToPng.mjs.map +1 -1
- package/es/utils/placement.mjs.map +1 -1
- package/es/utils/smoothCorners.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamRenderer.mjs","names":[],"sources":["../../../src/Highlighter/SyntaxHighlighter/StreamRenderer.tsx"],"sourcesContent":["'use client';\n\nimport { getTokenStyleObject } from '@shikijs/core';\nimport { cx } from 'antd-style';\nimport type { CSSProperties } from 'react';\nimport { memo } from 'react';\nimport type { BuiltinTheme, ThemedToken } from 'shiki';\n\nimport { useStreamHighlight } from '@/hooks/useStreamHighlight';\n\ninterface StreamRendererProps {\n children: string;\n className?: string;\n enableTransformer?: boolean;\n fallbackClassName?: string;\n language: string;\n style?: CSSProperties;\n theme?: BuiltinTheme;\n}\n\nconst normalizeStyleKeys = (style: Record<string, string | number>): CSSProperties => {\n const normalized: CSSProperties = {};\n Object.entries(style).forEach(([key, value]) => {\n const normalizedKey = key.replaceAll(/-([a-z])/g, (_, char) => char.toUpperCase());\n (normalized as Record<string, string | number>)[normalizedKey] = value;\n });\n return normalized;\n};\n\nconst getTokenInlineStyle = (token: ThemedToken): CSSProperties => {\n const rawStyle = token.htmlStyle || getTokenStyleObject(token);\n const baseStyle = normalizeStyleKeys(rawStyle);\n return { ...baseStyle, whiteSpace: 'pre' };\n};\n\nconst TokenSpan = memo(\n ({ token }: { token: ThemedToken }) => {\n return (\n <span key={token.content} style={getTokenInlineStyle(token)}>\n {token.content}\n </span>\n );\n },\n (prev, next) => prev.token === next.token,\n);\n\nconst TokenLine = memo(\n ({ line }: { line: ThemedToken[] }) => {\n if (!line.length) {\n return (\n <span className=\"line\">\n <span style={{ whiteSpace: 'pre' }}>{'\\u00A0'}</span>\n </span>\n );\n }\n\n return (\n <span className=\"line\">\n {line.map((token, tokenIndex) => (\n <TokenSpan key={`token-${tokenIndex}`} token={token} />\n ))}\n </span>\n );\n },\n (prev, next) => prev.line === next.line,\n);\n\nconst StreamRenderer = memo<StreamRendererProps>(\n ({ children, className, enableTransformer, fallbackClassName, language, style, theme }) => {\n // Safely handle empty or invalid children\n const safeChildren = children ?? '';\n\n const streaming = useStreamHighlight(safeChildren, {\n enableTransformer,\n language,\n streaming: true,\n theme,\n });\n\n const lines = streaming?.lines;\n const preStyle = streaming?.preStyle;\n\n if (!lines || lines.length === 0) {\n return (\n <div className={fallbackClassName} dir=\"ltr\" style={style}>\n <pre>\n <code>{safeChildren}</code>\n </pre>\n </div>\n );\n }\n\n return (\n <div className={className} dir=\"ltr\" style={style}>\n <pre className={cx('shiki', theme)} style={preStyle} tabIndex={0}>\n <code style={{ display: 'flex', flexDirection: 'column', whiteSpace: 'pre' }}>\n {lines.map((line, index) => (\n <TokenLine key={`line-${index}`} line={line} />\n ))}\n </code>\n </pre>\n </div>\n );\n },\n);\n\nStreamRenderer.displayName = 'StreamRenderer';\n\nexport default StreamRenderer;\n"],"mappings":";;;;;;;AAoBA,MAAM,sBAAsB,UAA0D;CACpF,MAAM,aAA4B,EAAE;AACpC,QAAO,QAAQ,MAAM,CAAC,SAAS,CAAC,KAAK,WAAW;EAC9C,MAAM,gBAAgB,IAAI,WAAW,cAAc,GAAG,SAAS,KAAK,aAAa,CAAC;AACjF,aAA+C,iBAAiB;GACjE;AACF,QAAO;;AAGT,MAAM,uBAAuB,UAAsC;AAGjE,QAAO;EAAE,GADS,mBADD,MAAM,aAAa,oBAAoB,MAAM,
|
|
1
|
+
{"version":3,"file":"StreamRenderer.mjs","names":[],"sources":["../../../src/Highlighter/SyntaxHighlighter/StreamRenderer.tsx"],"sourcesContent":["'use client';\n\nimport { getTokenStyleObject } from '@shikijs/core';\nimport { cx } from 'antd-style';\nimport type { CSSProperties } from 'react';\nimport { memo } from 'react';\nimport type { BuiltinTheme, ThemedToken } from 'shiki';\n\nimport { useStreamHighlight } from '@/hooks/useStreamHighlight';\n\ninterface StreamRendererProps {\n children: string;\n className?: string;\n enableTransformer?: boolean;\n fallbackClassName?: string;\n language: string;\n style?: CSSProperties;\n theme?: BuiltinTheme;\n}\n\nconst normalizeStyleKeys = (style: Record<string, string | number>): CSSProperties => {\n const normalized: CSSProperties = {};\n Object.entries(style).forEach(([key, value]) => {\n const normalizedKey = key.replaceAll(/-([a-z])/g, (_, char) => char.toUpperCase());\n (normalized as Record<string, string | number>)[normalizedKey] = value;\n });\n return normalized;\n};\n\nconst getTokenInlineStyle = (token: ThemedToken): CSSProperties => {\n const rawStyle = token.htmlStyle || getTokenStyleObject(token);\n const baseStyle = normalizeStyleKeys(rawStyle);\n return { ...baseStyle, whiteSpace: 'pre' };\n};\n\nconst TokenSpan = memo(\n ({ token }: { token: ThemedToken }) => {\n return (\n <span key={token.content} style={getTokenInlineStyle(token)}>\n {token.content}\n </span>\n );\n },\n (prev, next) => prev.token === next.token,\n);\n\nconst TokenLine = memo(\n ({ line }: { line: ThemedToken[] }) => {\n if (!line.length) {\n return (\n <span className=\"line\">\n <span style={{ whiteSpace: 'pre' }}>{'\\u00A0'}</span>\n </span>\n );\n }\n\n return (\n <span className=\"line\">\n {line.map((token, tokenIndex) => (\n <TokenSpan key={`token-${tokenIndex}`} token={token} />\n ))}\n </span>\n );\n },\n (prev, next) => prev.line === next.line,\n);\n\nconst StreamRenderer = memo<StreamRendererProps>(\n ({ children, className, enableTransformer, fallbackClassName, language, style, theme }) => {\n // Safely handle empty or invalid children\n const safeChildren = children ?? '';\n\n const streaming = useStreamHighlight(safeChildren, {\n enableTransformer,\n language,\n streaming: true,\n theme,\n });\n\n const lines = streaming?.lines;\n const preStyle = streaming?.preStyle;\n\n if (!lines || lines.length === 0) {\n return (\n <div className={fallbackClassName} dir=\"ltr\" style={style}>\n <pre>\n <code>{safeChildren}</code>\n </pre>\n </div>\n );\n }\n\n return (\n <div className={className} dir=\"ltr\" style={style}>\n <pre className={cx('shiki', theme)} style={preStyle} tabIndex={0}>\n <code style={{ display: 'flex', flexDirection: 'column', whiteSpace: 'pre' }}>\n {lines.map((line, index) => (\n <TokenLine key={`line-${index}`} line={line} />\n ))}\n </code>\n </pre>\n </div>\n );\n },\n);\n\nStreamRenderer.displayName = 'StreamRenderer';\n\nexport default StreamRenderer;\n"],"mappings":";;;;;;;AAoBA,MAAM,sBAAsB,UAA0D;CACpF,MAAM,aAA4B,EAAE;AACpC,QAAO,QAAQ,MAAM,CAAC,SAAS,CAAC,KAAK,WAAW;EAC9C,MAAM,gBAAgB,IAAI,WAAW,cAAc,GAAG,SAAS,KAAK,aAAa,CAAC;AACjF,aAA+C,iBAAiB;GACjE;AACF,QAAO;;AAGT,MAAM,uBAAuB,UAAsC;AAGjE,QAAO;EAAE,GADS,mBADD,MAAM,aAAa,oBAAoB,MAAM,CAEzC;EAAE,YAAY;EAAO;;AAG5C,MAAM,YAAY,MACf,EAAE,YAAoC;AACrC,QACE,oBAAC,QAAD;EAA0B,OAAO,oBAAoB,MAAM;YACxD,MAAM;EACF,EAFI,MAAM,QAEV;IAGV,MAAM,SAAS,KAAK,UAAU,KAAK,MACrC;AAED,MAAM,YAAY,MACf,EAAE,WAAoC;AACrC,KAAI,CAAC,KAAK,OACR,QACE,oBAAC,QAAD;EAAM,WAAU;YACd,oBAAC,QAAD;GAAM,OAAO,EAAE,YAAY,OAAO;aAAG;GAAgB,CAAA;EAChD,CAAA;AAIX,QACE,oBAAC,QAAD;EAAM,WAAU;YACb,KAAK,KAAK,OAAO,eAChB,oBAAC,WAAD,EAA8C,OAAS,EAAvC,SAAS,aAA8B,CACvD;EACG,CAAA;IAGV,MAAM,SAAS,KAAK,SAAS,KAAK,KACpC;AAED,MAAM,iBAAiB,MACpB,EAAE,UAAU,WAAW,mBAAmB,mBAAmB,UAAU,OAAO,YAAY;CAEzF,MAAM,eAAe,YAAY;CAEjC,MAAM,YAAY,mBAAmB,cAAc;EACjD;EACA;EACA,WAAW;EACX;EACD,CAAC;CAEF,MAAM,QAAQ,WAAW;CACzB,MAAM,WAAW,WAAW;AAE5B,KAAI,CAAC,SAAS,MAAM,WAAW,EAC7B,QACE,oBAAC,OAAD;EAAK,WAAW;EAAmB,KAAI;EAAa;YAClD,oBAAC,OAAD,EAAA,UACE,oBAAC,QAAD,EAAA,UAAO,cAAoB,CAAA,EACvB,CAAA;EACF,CAAA;AAIV,QACE,oBAAC,OAAD;EAAgB;EAAW,KAAI;EAAa;YAC1C,oBAAC,OAAD;GAAK,WAAW,GAAG,SAAS,MAAM;GAAE,OAAO;GAAU,UAAU;aAC7D,oBAAC,QAAD;IAAM,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,YAAY;KAAO;cACzE,MAAM,KAAK,MAAM,UAChB,oBAAC,WAAD,EAAuC,MAAQ,EAA/B,QAAQ,QAAuB,CAC/C;IACG,CAAA;GACH,CAAA;EACF,CAAA;EAGX;AAED,eAAe,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const.mjs","names":[],"sources":["../../src/Highlighter/const.ts"],"sourcesContent":["import { bundledLanguagesInfo, bundledThemesInfo } from 'shiki';\n\ninterface HighlighterThemeItem {\n displayName: string;\n id: string;\n}\n\nexport const highlighterThemes: HighlighterThemeItem[] = [\n {\n displayName: 'Lobe Theme',\n id: 'lobe-theme',\n },\n ...bundledThemesInfo.map((item) => ({\n displayName: item.displayName,\n id: item.id,\n })),\n];\n\nexport const FALLBACK_LANG = 'plaintext';\n\nexport const getCodeLanguageByInput = (input: string): string => {\n if (!input) {\n return 'plaintext';\n }\n const inputLang = input.toLocaleLowerCase();\n\n const matchLang = bundledLanguagesInfo.find(\n (lang) => lang.id === inputLang || lang.aliases?.includes(inputLang),\n );\n return matchLang?.id || 'plaintext';\n};\n\nexport const getCodeLanguageFilename = (input: string): string => {\n if (!input) {\n return 'Plaintext';\n }\n const inputLang = input.toLocaleLowerCase();\n\n const matchLang = bundledLanguagesInfo.find(\n (lang) => lang.id === inputLang || lang.aliases?.includes(inputLang),\n );\n const type = matchLang?.aliases?.[0] || matchLang?.id || 'txt';\n return `*.${type}`;\n};\n\nexport const getCodeLanguageDisplayName = (input: string): string => {\n if (!input) {\n return 'Plaintext';\n }\n const inputLang = input.toLocaleLowerCase();\n\n const matchLang = bundledLanguagesInfo.find(\n (lang) => lang.id === inputLang || lang.aliases?.includes(inputLang),\n );\n return matchLang?.name || 'Plaintext';\n};\n"],"mappings":";;AAOA,MAAa,oBAA4C,CACvD;CACE,aAAa;CACb,IAAI;CACL,EACD,GAAG,kBAAkB,KAAK,UAAU;CAClC,aAAa,KAAK;CAClB,IAAI,KAAK;CACV,EAAE,CACJ;AAED,MAAa,gBAAgB;AAE7B,MAAa,0BAA0B,UAA0B;AAC/D,KAAI,CAAC,MACH,QAAO;CAET,MAAM,YAAY,MAAM,mBAAmB;AAK3C,QAHkB,qBAAqB,MACpC,SAAS,KAAK,OAAO,aAAa,KAAK,SAAS,SAAS,UAAU,
|
|
1
|
+
{"version":3,"file":"const.mjs","names":[],"sources":["../../src/Highlighter/const.ts"],"sourcesContent":["import { bundledLanguagesInfo, bundledThemesInfo } from 'shiki';\n\ninterface HighlighterThemeItem {\n displayName: string;\n id: string;\n}\n\nexport const highlighterThemes: HighlighterThemeItem[] = [\n {\n displayName: 'Lobe Theme',\n id: 'lobe-theme',\n },\n ...bundledThemesInfo.map((item) => ({\n displayName: item.displayName,\n id: item.id,\n })),\n];\n\nexport const FALLBACK_LANG = 'plaintext';\n\nexport const getCodeLanguageByInput = (input: string): string => {\n if (!input) {\n return 'plaintext';\n }\n const inputLang = input.toLocaleLowerCase();\n\n const matchLang = bundledLanguagesInfo.find(\n (lang) => lang.id === inputLang || lang.aliases?.includes(inputLang),\n );\n return matchLang?.id || 'plaintext';\n};\n\nexport const getCodeLanguageFilename = (input: string): string => {\n if (!input) {\n return 'Plaintext';\n }\n const inputLang = input.toLocaleLowerCase();\n\n const matchLang = bundledLanguagesInfo.find(\n (lang) => lang.id === inputLang || lang.aliases?.includes(inputLang),\n );\n const type = matchLang?.aliases?.[0] || matchLang?.id || 'txt';\n return `*.${type}`;\n};\n\nexport const getCodeLanguageDisplayName = (input: string): string => {\n if (!input) {\n return 'Plaintext';\n }\n const inputLang = input.toLocaleLowerCase();\n\n const matchLang = bundledLanguagesInfo.find(\n (lang) => lang.id === inputLang || lang.aliases?.includes(inputLang),\n );\n return matchLang?.name || 'Plaintext';\n};\n"],"mappings":";;AAOA,MAAa,oBAA4C,CACvD;CACE,aAAa;CACb,IAAI;CACL,EACD,GAAG,kBAAkB,KAAK,UAAU;CAClC,aAAa,KAAK;CAClB,IAAI,KAAK;CACV,EAAE,CACJ;AAED,MAAa,gBAAgB;AAE7B,MAAa,0BAA0B,UAA0B;AAC/D,KAAI,CAAC,MACH,QAAO;CAET,MAAM,YAAY,MAAM,mBAAmB;AAK3C,QAHkB,qBAAqB,MACpC,SAAS,KAAK,OAAO,aAAa,KAAK,SAAS,SAAS,UAAU,CAEtD,EAAE,MAAM;;AAG1B,MAAa,2BAA2B,UAA0B;AAChE,KAAI,CAAC,MACH,QAAO;CAET,MAAM,YAAY,MAAM,mBAAmB;CAE3C,MAAM,YAAY,qBAAqB,MACpC,SAAS,KAAK,OAAO,aAAa,KAAK,SAAS,SAAS,UAAU,CACrE;AAED,QAAO,KADM,WAAW,UAAU,MAAM,WAAW,MAAM;;AAI3D,MAAa,8BAA8B,UAA0B;AACnE,KAAI,CAAC,MACH,QAAO;CAET,MAAM,YAAY,MAAM,mBAAmB;AAK3C,QAHkB,qBAAqB,MACpC,SAAS,KAAK,OAAO,aAAa,KAAK,SAAS,SAAS,UAAU,CAEtD,EAAE,QAAQ"}
|
package/es/Hotkey/Hotkey.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Hotkey.mjs","names":["Flexbox"],"sources":["../../src/Hotkey/Hotkey.tsx"],"sourcesContent":["'use client';\n\nimport { cx, useThemeMode } from 'antd-style';\nimport {\n ArrowBigUpIcon,\n ArrowDownIcon,\n ArrowLeftIcon,\n ArrowRightIcon,\n ArrowRightToLineIcon,\n ArrowUpIcon,\n ChevronUpIcon,\n Command,\n CornerDownLeftIcon,\n Delete,\n Grid2X2Icon,\n MouseIcon,\n Option,\n SpaceIcon,\n} from 'lucide-react';\nimport { memo, useEffect, useMemo, useState } from 'react';\n\nimport { Center, Flexbox } from '@/Flex';\nimport Icon from '@/Icon';\nimport LeftClickIcon from '@/icons/lucideExtra/LeftClickIcon';\nimport LeftDoubleClickIcon from '@/icons/lucideExtra/LeftDoubleClickIcon';\nimport RightClickIcon from '@/icons/lucideExtra/RightClickIcon';\nimport RightDoubleClickIcon from '@/icons/lucideExtra/RightDoubleClickIcon';\n\nimport { KeyMapEnum } from './const';\nimport { variants } from './style';\nimport type { HotkeyProps } from './type';\nimport { checkIsAppleDevice, splitKeysByPlus, startCase } from './utils';\n\nconst mappingKey = (isAppleDevice: boolean) => ({\n [KeyMapEnum.Alt]: isAppleDevice ? <Icon icon={Option} size={{ size: '0.95em' }} /> : 'Alt',\n [KeyMapEnum.Backspace]: isAppleDevice ? <Icon icon={Delete} /> : 'Backspace',\n [KeyMapEnum.CommandOrControl]: isAppleDevice ? (\n <Icon icon={Command} size={{ size: '0.95em' }} />\n ) : (\n 'Ctrl'\n ),\n [KeyMapEnum.Ctrl]: isAppleDevice ? <Icon icon={ChevronUpIcon} /> : 'Ctrl',\n [KeyMapEnum.Control]: isAppleDevice ? <Icon icon={ChevronUpIcon} /> : 'Ctrl',\n [KeyMapEnum.Down]: <Icon icon={ArrowDownIcon} />,\n [KeyMapEnum.Enter]: isAppleDevice ? <Icon icon={CornerDownLeftIcon} /> : 'Enter',\n [KeyMapEnum.LeftClick]: <Icon icon={LeftClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />,\n [KeyMapEnum.Left]: <Icon icon={ArrowLeftIcon} />,\n [KeyMapEnum.Meta]: isAppleDevice ? (\n <Icon icon={Command} size={{ size: '0.95em' }} />\n ) : (\n <Icon icon={Grid2X2Icon} />\n ),\n [KeyMapEnum.MiddleClick]: <Icon icon={MouseIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />,\n [KeyMapEnum.Mod]: isAppleDevice ? <Icon icon={Command} size={{ size: '0.95em' }} /> : 'Ctrl',\n [KeyMapEnum.RightClick]: (\n <Icon icon={RightClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />\n ),\n [KeyMapEnum.RightDoubleClick]: (\n <Icon icon={RightDoubleClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />\n ),\n [KeyMapEnum.LeftDoubleClick]: (\n <Icon icon={LeftDoubleClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />\n ),\n [KeyMapEnum.Right]: <Icon icon={ArrowRightIcon} />,\n [KeyMapEnum.Shift]: isAppleDevice ? (\n <Icon icon={ArrowBigUpIcon} size={{ size: '1.15em', strokeWidth: 1.75 }} />\n ) : (\n 'Shift'\n ),\n [KeyMapEnum.Space]: <Icon icon={SpaceIcon} />,\n [KeyMapEnum.Tab]: isAppleDevice ? <Icon icon={ArrowRightToLineIcon} /> : 'Tab',\n [KeyMapEnum.Up]: <Icon icon={ArrowUpIcon} />,\n [KeyMapEnum.Comma]: ',',\n [KeyMapEnum.Period]: '.',\n [KeyMapEnum.Slash]: '?',\n [KeyMapEnum.Semicolon]: ';',\n [KeyMapEnum.Quote]: \"'\",\n [KeyMapEnum.Backquote]: '`',\n [KeyMapEnum.Backslash]: '\\\\',\n [KeyMapEnum.BracketLeft]: '[',\n [KeyMapEnum.BracketRight]: ']',\n [KeyMapEnum.Minus]: '-',\n [KeyMapEnum.Equal]: '+',\n});\n\nconst Hotkey = memo<HotkeyProps>(\n ({\n variant = 'filled',\n classNames,\n styles: customStyles,\n keys,\n inverseTheme,\n isApple,\n compact,\n className,\n style,\n ...rest\n }) => {\n const { isDarkMode } = useThemeMode();\n const isBorderless = variant === 'borderless';\n const [keysGroup, setKeysGroup] = useState(() => splitKeysByPlus(keys));\n const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n\n useEffect(() => {\n const newValue = splitKeysByPlus(keys);\n setKeysGroup(newValue);\n }, [keys]);\n\n const mapping: Record<string, any> = useMemo(() => mappingKey(isAppleDevice), [isAppleDevice]);\n\n return (\n <Flexbox\n horizontal\n align={'center'}\n className={className}\n gap={isBorderless ? 6 : 2}\n style={style}\n {...rest}\n >\n {compact || isBorderless ? (\n <Center\n horizontal\n as={'kbd'}\n gap={6}\n style={customStyles?.kbdStyle}\n className={cx(\n variants({ inverseTheme, isDarkMode, variant }),\n classNames?.kbdClassName,\n )}\n >\n {keysGroup.map((key, index) => (\n <div key={index}>{mapping[key] ?? startCase(key)}</div>\n ))}\n </Center>\n ) : (\n keysGroup.map((key, index) => (\n <Center\n as={'kbd'}\n key={index}\n style={customStyles?.kbdStyle}\n className={cx(\n variants({ inverseTheme, isDarkMode, variant }),\n classNames?.kbdClassName,\n )}\n >\n {mapping[key] ?? startCase(key)}\n </Center>\n ))\n )}\n </Flexbox>\n );\n },\n);\n\nHotkey.displayName = 'Hotkey';\n\nexport default Hotkey;\n"],"mappings":";;;;;;;;;;;;;;;;AAiCA,MAAM,cAAc,mBAA4B;EAC7C,WAAW,MAAM,gBAAgB,oBAAC,MAAD;EAAM,MAAM;EAAQ,MAAM,EAAE,MAAM,UAAU;EAAI,CAAA,GAAG;EACpF,WAAW,YAAY,gBAAgB,oBAAC,MAAD,EAAM,MAAM,QAAU,CAAA,GAAG;EAChE,WAAW,mBAAmB,gBAC7B,oBAAC,MAAD;EAAM,MAAM;EAAS,MAAM,EAAE,MAAM,UAAU;EAAI,CAAA,GAEjD;EAED,WAAW,OAAO,gBAAgB,oBAAC,MAAD,EAAM,MAAM,eAAiB,CAAA,GAAG;EAClE,WAAW,UAAU,gBAAgB,oBAAC,MAAD,EAAM,MAAM,eAAiB,CAAA,GAAG;EACrE,WAAW,OAAO,oBAAC,MAAD,EAAM,MAAM,eAAiB,CAAA;EAC/C,WAAW,QAAQ,gBAAgB,oBAAC,MAAD,EAAM,MAAM,oBAAsB,CAAA,GAAG;EACxE,WAAW,YAAY,oBAAC,MAAD;EAAM,MAAM;EAAe,MAAM;GAAE,MAAM;GAAS,aAAa;GAAM;EAAI,CAAA;EAChG,WAAW,OAAO,oBAAC,MAAD,EAAM,MAAM,eAAiB,CAAA;EAC/C,WAAW,OAAO,gBACjB,oBAAC,MAAD;EAAM,MAAM;EAAS,MAAM,EAAE,MAAM,UAAU;EAAI,CAAA,GAEjD,oBAAC,MAAD,EAAM,MAAM,aAAe,CAAA;EAE5B,WAAW,cAAc,oBAAC,MAAD;EAAM,MAAM;EAAW,MAAM;GAAE,MAAM;GAAS,aAAa;GAAM;EAAI,CAAA;EAC9F,WAAW,MAAM,gBAAgB,oBAAC,MAAD;EAAM,MAAM;EAAS,MAAM,EAAE,MAAM,UAAU;EAAI,CAAA,GAAG;EACrF,WAAW,aACV,oBAAC,MAAD;EAAM,MAAM;EAAgB,MAAM;GAAE,MAAM;GAAS,aAAa;GAAM;EAAI,CAAA;EAE3E,WAAW,mBACV,oBAAC,MAAD;EAAM,MAAM;EAAsB,MAAM;GAAE,MAAM;GAAS,aAAa;GAAM;EAAI,CAAA;EAEjF,WAAW,kBACV,oBAAC,MAAD;EAAM,MAAM;EAAqB,MAAM;GAAE,MAAM;GAAS,aAAa;GAAM;EAAI,CAAA;EAEhF,WAAW,QAAQ,oBAAC,MAAD,EAAM,MAAM,gBAAkB,CAAA;EACjD,WAAW,QAAQ,gBAClB,oBAAC,MAAD;EAAM,MAAM;EAAgB,MAAM;GAAE,MAAM;GAAU,aAAa;GAAM;EAAI,CAAA,GAE3E;EAED,WAAW,QAAQ,oBAAC,MAAD,EAAM,MAAM,WAAa,CAAA;EAC5C,WAAW,MAAM,gBAAgB,oBAAC,MAAD,EAAM,MAAM,sBAAwB,CAAA,GAAG;EACxE,WAAW,KAAK,oBAAC,MAAD,EAAM,MAAM,aAAe,CAAA;EAC3C,WAAW,QAAQ;EACnB,WAAW,SAAS;EACpB,WAAW,QAAQ;EACnB,WAAW,YAAY;EACvB,WAAW,QAAQ;EACnB,WAAW,YAAY;EACvB,WAAW,YAAY;EACvB,WAAW,cAAc;EACzB,WAAW,eAAe;EAC1B,WAAW,QAAQ;EACnB,WAAW,QAAQ;CACrB;AAED,MAAM,SAAS,MACZ,EACC,UAAU,UACV,YACA,QAAQ,cACR,MACA,cACA,SACA,SACA,WACA,OACA,GAAG,WACC;CACJ,MAAM,EAAE,eAAe,cAAc;CACrC,MAAM,eAAe,YAAY;CACjC,MAAM,CAAC,WAAW,gBAAgB,eAAe,gBAAgB,KAAK,CAAC;CACvE,MAAM,gBAAgB,cAAc,mBAAmB,QAAQ,EAAE,CAAC,QAAQ,CAAC;AAE3E,iBAAgB;AAEd,eADiB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"Hotkey.mjs","names":["Flexbox"],"sources":["../../src/Hotkey/Hotkey.tsx"],"sourcesContent":["'use client';\n\nimport { cx, useThemeMode } from 'antd-style';\nimport {\n ArrowBigUpIcon,\n ArrowDownIcon,\n ArrowLeftIcon,\n ArrowRightIcon,\n ArrowRightToLineIcon,\n ArrowUpIcon,\n ChevronUpIcon,\n Command,\n CornerDownLeftIcon,\n Delete,\n Grid2X2Icon,\n MouseIcon,\n Option,\n SpaceIcon,\n} from 'lucide-react';\nimport { memo, useEffect, useMemo, useState } from 'react';\n\nimport { Center, Flexbox } from '@/Flex';\nimport Icon from '@/Icon';\nimport LeftClickIcon from '@/icons/lucideExtra/LeftClickIcon';\nimport LeftDoubleClickIcon from '@/icons/lucideExtra/LeftDoubleClickIcon';\nimport RightClickIcon from '@/icons/lucideExtra/RightClickIcon';\nimport RightDoubleClickIcon from '@/icons/lucideExtra/RightDoubleClickIcon';\n\nimport { KeyMapEnum } from './const';\nimport { variants } from './style';\nimport type { HotkeyProps } from './type';\nimport { checkIsAppleDevice, splitKeysByPlus, startCase } from './utils';\n\nconst mappingKey = (isAppleDevice: boolean) => ({\n [KeyMapEnum.Alt]: isAppleDevice ? <Icon icon={Option} size={{ size: '0.95em' }} /> : 'Alt',\n [KeyMapEnum.Backspace]: isAppleDevice ? <Icon icon={Delete} /> : 'Backspace',\n [KeyMapEnum.CommandOrControl]: isAppleDevice ? (\n <Icon icon={Command} size={{ size: '0.95em' }} />\n ) : (\n 'Ctrl'\n ),\n [KeyMapEnum.Ctrl]: isAppleDevice ? <Icon icon={ChevronUpIcon} /> : 'Ctrl',\n [KeyMapEnum.Control]: isAppleDevice ? <Icon icon={ChevronUpIcon} /> : 'Ctrl',\n [KeyMapEnum.Down]: <Icon icon={ArrowDownIcon} />,\n [KeyMapEnum.Enter]: isAppleDevice ? <Icon icon={CornerDownLeftIcon} /> : 'Enter',\n [KeyMapEnum.LeftClick]: <Icon icon={LeftClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />,\n [KeyMapEnum.Left]: <Icon icon={ArrowLeftIcon} />,\n [KeyMapEnum.Meta]: isAppleDevice ? (\n <Icon icon={Command} size={{ size: '0.95em' }} />\n ) : (\n <Icon icon={Grid2X2Icon} />\n ),\n [KeyMapEnum.MiddleClick]: <Icon icon={MouseIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />,\n [KeyMapEnum.Mod]: isAppleDevice ? <Icon icon={Command} size={{ size: '0.95em' }} /> : 'Ctrl',\n [KeyMapEnum.RightClick]: (\n <Icon icon={RightClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />\n ),\n [KeyMapEnum.RightDoubleClick]: (\n <Icon icon={RightDoubleClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />\n ),\n [KeyMapEnum.LeftDoubleClick]: (\n <Icon icon={LeftDoubleClickIcon} size={{ size: '1.2em', strokeWidth: 1.75 }} />\n ),\n [KeyMapEnum.Right]: <Icon icon={ArrowRightIcon} />,\n [KeyMapEnum.Shift]: isAppleDevice ? (\n <Icon icon={ArrowBigUpIcon} size={{ size: '1.15em', strokeWidth: 1.75 }} />\n ) : (\n 'Shift'\n ),\n [KeyMapEnum.Space]: <Icon icon={SpaceIcon} />,\n [KeyMapEnum.Tab]: isAppleDevice ? <Icon icon={ArrowRightToLineIcon} /> : 'Tab',\n [KeyMapEnum.Up]: <Icon icon={ArrowUpIcon} />,\n [KeyMapEnum.Comma]: ',',\n [KeyMapEnum.Period]: '.',\n [KeyMapEnum.Slash]: '?',\n [KeyMapEnum.Semicolon]: ';',\n [KeyMapEnum.Quote]: \"'\",\n [KeyMapEnum.Backquote]: '`',\n [KeyMapEnum.Backslash]: '\\\\',\n [KeyMapEnum.BracketLeft]: '[',\n [KeyMapEnum.BracketRight]: ']',\n [KeyMapEnum.Minus]: '-',\n [KeyMapEnum.Equal]: '+',\n});\n\nconst Hotkey = memo<HotkeyProps>(\n ({\n variant = 'filled',\n classNames,\n styles: customStyles,\n keys,\n inverseTheme,\n isApple,\n compact,\n className,\n style,\n ...rest\n }) => {\n const { isDarkMode } = useThemeMode();\n const isBorderless = variant === 'borderless';\n const [keysGroup, setKeysGroup] = useState(() => splitKeysByPlus(keys));\n const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n\n useEffect(() => {\n const newValue = splitKeysByPlus(keys);\n setKeysGroup(newValue);\n }, [keys]);\n\n const mapping: Record<string, any> = useMemo(() => mappingKey(isAppleDevice), [isAppleDevice]);\n\n return (\n <Flexbox\n horizontal\n align={'center'}\n className={className}\n gap={isBorderless ? 6 : 2}\n style={style}\n {...rest}\n >\n {compact || isBorderless ? (\n <Center\n horizontal\n as={'kbd'}\n gap={6}\n style={customStyles?.kbdStyle}\n className={cx(\n variants({ inverseTheme, isDarkMode, variant }),\n classNames?.kbdClassName,\n )}\n >\n {keysGroup.map((key, index) => (\n <div key={index}>{mapping[key] ?? startCase(key)}</div>\n ))}\n </Center>\n ) : (\n keysGroup.map((key, index) => (\n <Center\n as={'kbd'}\n key={index}\n style={customStyles?.kbdStyle}\n className={cx(\n variants({ inverseTheme, isDarkMode, variant }),\n classNames?.kbdClassName,\n )}\n >\n {mapping[key] ?? startCase(key)}\n </Center>\n ))\n )}\n </Flexbox>\n );\n },\n);\n\nHotkey.displayName = 'Hotkey';\n\nexport default Hotkey;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HotkeyInput.mjs","names":["hotkeyMessages","useControlledState","Flexbox"],"sources":["../../src/HotkeyInput/HotkeyInput.tsx"],"sourcesContent":["'use client';\n\nimport { type InputRef } from 'antd';\nimport { cx, useThemeMode } from 'antd-style';\nimport { isEqual } from 'es-toolkit/compat';\nimport { Undo2Icon, XIcon } from 'lucide-react';\nimport {\n type FocusEvent,\n memo,\n type MouseEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useHotkeys, useRecordHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport Hotkey from '@/Hotkey';\nimport { checkIsAppleDevice, NORMATIVE_MODIFIER, splitKeysByPlus } from '@/Hotkey/utils';\nimport hotkeyMessages from '@/i18n/resources/en/hotkey';\nimport { useTranslation } from '@/i18n/useTranslation';\n\nimport { styles, variants } from './style';\nimport { type HotkeyInputProps } from './type';\n\nconst HotkeyInput = memo<HotkeyInputProps>(\n ({\n value = '',\n defaultValue = '',\n resetValue = '',\n onChange,\n onClear,\n onConflict,\n placeholder,\n disabled,\n shadow,\n allowClear,\n allowReset = true,\n style,\n className,\n hotkeyConflicts = [],\n variant,\n texts,\n isApple,\n onBlur,\n onReset,\n onFocus,\n }) => {\n const [isFocused, setIsFocused] = useState(false);\n const [hasConflict, setHasConflict] = useState(false);\n const [hasInvalidCombination, setHasInvalidCombination] = useState(false);\n const inputRef = useRef<InputRef>(null);\n const { isDarkMode } = useThemeMode();\n const { t } = useTranslation(hotkeyMessages);\n const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n const [hotkeyValue, setHotkeyValue] = useControlledState(defaultValue, {\n defaultValue,\n onChange,\n value,\n });\n\n // 使用 useRecordHotkeys 处理快捷键录入\n const [recordedKeys, { start, stop, isRecording, resetKeys }] = useRecordHotkeys();\n\n useHotkeys(\n '*',\n () => {\n inputRef.current?.blur();\n },\n {\n enableOnContentEditable: true,\n enableOnFormTags: true,\n enabled: isRecording && !disabled,\n keydown: false,\n keyup: true,\n preventDefault: true,\n },\n );\n\n // 处理按键,保证格式正确:修饰键在前,最多一个非修饰键在后\n const formatKeys = useCallback((keysSet: Set<string>) => {\n const modifiers: string[] = [];\n const normalKeys: string[] = [];\n\n for (const key of keysSet) {\n // 处理不同表示的修饰键\n const normalizedKey: any = key.toLowerCase();\n if (NORMATIVE_MODIFIER.includes(normalizedKey)) {\n // 统一修饰键表示\n if (\n (!isAppleDevice && normalizedKey === 'ctrl') ||\n (isAppleDevice && normalizedKey === 'meta')\n ) {\n if (!modifiers.includes('mod')) modifiers.push('mod');\n } else if (!modifiers.includes(normalizedKey)) {\n modifiers.push(normalizedKey);\n }\n } else {\n normalKeys.push(key);\n }\n }\n\n // 至少需要一个修饰键\n if (modifiers.length === 0 && normalKeys.length > 0) {\n return { isValid: false, keys: [] };\n }\n\n // 只允许一个非修饰键,如果有多个,只保留最后一个\n const finalKey = normalKeys.length > 0 ? [normalKeys.at(-1)] : [];\n const shortcuts = [modifiers, finalKey];\n\n return {\n // 组合必须包含至少一个按键\n isValid: shortcuts.every((k) => k.length > 0),\n keys: shortcuts.flat(),\n };\n }, []);\n\n // 获取格式化后的按键字符串\n const { isValid, keys } = formatKeys(recordedKeys);\n const keysString = keys.join('+');\n\n // 检查快捷键冲突\n const checkHotkeyConflict = useCallback(\n (newHotkey: string): boolean => {\n return hotkeyConflicts\n .filter((conflictKey) => conflictKey !== resetValue)\n .some((conflictKey) => {\n const newKeys = splitKeysByPlus(newHotkey);\n const conflictKeys = splitKeysByPlus(conflictKey);\n return isEqual(newKeys, conflictKeys);\n });\n },\n [hotkeyConflicts],\n );\n\n // 当按键组合完成时处理结果\n useEffect(() => {\n if (recordedKeys.size > 0 && !isRecording) {\n if (!isValid) {\n setHasInvalidCombination(true);\n setHasConflict(false);\n return;\n }\n\n setHasInvalidCombination(false);\n const newKeysString = keysString;\n\n // 检查冲突\n const conflict = checkHotkeyConflict(newKeysString);\n if (conflict) {\n setHasConflict(true);\n onConflict?.(newKeysString);\n } else {\n setHasConflict(false);\n setHotkeyValue?.(newKeysString);\n }\n }\n }, [\n recordedKeys,\n isRecording,\n isValid,\n keysString,\n checkHotkeyConflict,\n setHotkeyValue,\n onConflict,\n ]);\n\n // 处理输入框焦点\n const handleFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (disabled) return;\n setIsFocused(true);\n setHasConflict(false);\n setHasInvalidCombination(false);\n start(); // 开始记录\n onFocus?.(e);\n };\n\n const handleBlur = (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(false);\n stop(); // 停止记录\n onBlur?.(e);\n };\n\n const handleClear = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.('');\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop();\n onClear?.(hotkeyValue);\n };\n\n // 重置功能\n const handleReset = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.(resetValue);\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop(); // 停止记录\n onReset?.(hotkeyValue, resetValue);\n };\n\n const handleClick = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled || isFocused) return;\n inputRef.current?.focus();\n };\n\n const placeholderText = placeholder ?? t('hotkey.placeholder');\n const resetTitle = texts?.reset ?? t('hotkey.reset');\n const clearTitle = texts?.clear ?? t('hotkey.clear');\n const conflictText = texts?.conflicts ?? t('hotkey.conflict');\n const invalidText = texts?.invalidCombination ?? t('hotkey.invalidCombination');\n\n return (\n <Flexbox\n className={className}\n gap={8}\n style={{\n position: 'relative',\n ...style,\n }}\n >\n <Flexbox\n horizontal\n align={'center'}\n justify={'space-between'}\n className={cx(\n variants({\n disabled,\n error: hasConflict || hasInvalidCombination,\n focused: isFocused,\n shadow,\n variant: variant || (isDarkMode ? 'filled' : 'outlined'),\n }),\n )}\n onClick={handleClick}\n >\n <div style={{ pointerEvents: 'none' }}>\n {isRecording ? (\n <span className={styles.placeholder}>\n {keys.length > 0 ? <Hotkey keys={keysString} /> : placeholderText}\n </span>\n ) : hotkeyValue ? (\n <Hotkey keys={hotkeyValue} />\n ) : (\n <span className={styles.placeholder}>{placeholderText}</span>\n )}\n </div>\n\n {/* 隐藏的输入框,用于接收焦点 */}\n <input\n readOnly\n className={styles.hiddenInput}\n disabled={disabled}\n ref={inputRef as any}\n style={{ pointerEvents: 'none' }}\n onBlur={handleBlur}\n onFocus={handleFocus}\n />\n\n {!isFocused && hotkeyValue && !disabled && (allowReset || allowClear) && (\n <Flexbox horizontal gap={4}>\n {allowReset && hotkeyValue !== resetValue && (\n <ActionIcon\n icon={Undo2Icon}\n size={'small'}\n title={resetTitle}\n variant={'filled'}\n onClick={handleReset}\n />\n )}\n {allowClear && (\n <ActionIcon\n icon={XIcon}\n size={'small'}\n title={clearTitle}\n variant={'filled'}\n onClick={handleClear}\n />\n )}\n </Flexbox>\n )}\n </Flexbox>\n {hasConflict && <div className={styles.errorText}>{conflictText}</div>}\n {hasInvalidCombination && <div className={styles.errorText}>{invalidText}</div>}\n </Flexbox>\n );\n },\n);\n\nHotkeyInput.displayName = 'HotkeyInput';\n\nexport default HotkeyInput;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HotkeyInput.mjs","names":["hotkeyMessages","useControlledState","Flexbox"],"sources":["../../src/HotkeyInput/HotkeyInput.tsx"],"sourcesContent":["'use client';\n\nimport { type InputRef } from 'antd';\nimport { cx, useThemeMode } from 'antd-style';\nimport { isEqual } from 'es-toolkit/compat';\nimport { Undo2Icon, XIcon } from 'lucide-react';\nimport {\n type FocusEvent,\n memo,\n type MouseEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useHotkeys, useRecordHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport Hotkey from '@/Hotkey';\nimport { checkIsAppleDevice, NORMATIVE_MODIFIER, splitKeysByPlus } from '@/Hotkey/utils';\nimport hotkeyMessages from '@/i18n/resources/en/hotkey';\nimport { useTranslation } from '@/i18n/useTranslation';\n\nimport { styles, variants } from './style';\nimport { type HotkeyInputProps } from './type';\n\nconst HotkeyInput = memo<HotkeyInputProps>(\n ({\n value = '',\n defaultValue = '',\n resetValue = '',\n onChange,\n onClear,\n onConflict,\n placeholder,\n disabled,\n shadow,\n allowClear,\n allowReset = true,\n style,\n className,\n hotkeyConflicts = [],\n variant,\n texts,\n isApple,\n onBlur,\n onReset,\n onFocus,\n }) => {\n const [isFocused, setIsFocused] = useState(false);\n const [hasConflict, setHasConflict] = useState(false);\n const [hasInvalidCombination, setHasInvalidCombination] = useState(false);\n const inputRef = useRef<InputRef>(null);\n const { isDarkMode } = useThemeMode();\n const { t } = useTranslation(hotkeyMessages);\n const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n const [hotkeyValue, setHotkeyValue] = useControlledState(defaultValue, {\n defaultValue,\n onChange,\n value,\n });\n\n // 使用 useRecordHotkeys 处理快捷键录入\n const [recordedKeys, { start, stop, isRecording, resetKeys }] = useRecordHotkeys();\n\n useHotkeys(\n '*',\n () => {\n inputRef.current?.blur();\n },\n {\n enableOnContentEditable: true,\n enableOnFormTags: true,\n enabled: isRecording && !disabled,\n keydown: false,\n keyup: true,\n preventDefault: true,\n },\n );\n\n // 处理按键,保证格式正确:修饰键在前,最多一个非修饰键在后\n const formatKeys = useCallback((keysSet: Set<string>) => {\n const modifiers: string[] = [];\n const normalKeys: string[] = [];\n\n for (const key of keysSet) {\n // 处理不同表示的修饰键\n const normalizedKey: any = key.toLowerCase();\n if (NORMATIVE_MODIFIER.includes(normalizedKey)) {\n // 统一修饰键表示\n if (\n (!isAppleDevice && normalizedKey === 'ctrl') ||\n (isAppleDevice && normalizedKey === 'meta')\n ) {\n if (!modifiers.includes('mod')) modifiers.push('mod');\n } else if (!modifiers.includes(normalizedKey)) {\n modifiers.push(normalizedKey);\n }\n } else {\n normalKeys.push(key);\n }\n }\n\n // 至少需要一个修饰键\n if (modifiers.length === 0 && normalKeys.length > 0) {\n return { isValid: false, keys: [] };\n }\n\n // 只允许一个非修饰键,如果有多个,只保留最后一个\n const finalKey = normalKeys.length > 0 ? [normalKeys.at(-1)] : [];\n const shortcuts = [modifiers, finalKey];\n\n return {\n // 组合必须包含至少一个按键\n isValid: shortcuts.every((k) => k.length > 0),\n keys: shortcuts.flat(),\n };\n }, []);\n\n // 获取格式化后的按键字符串\n const { isValid, keys } = formatKeys(recordedKeys);\n const keysString = keys.join('+');\n\n // 检查快捷键冲突\n const checkHotkeyConflict = useCallback(\n (newHotkey: string): boolean => {\n return hotkeyConflicts\n .filter((conflictKey) => conflictKey !== resetValue)\n .some((conflictKey) => {\n const newKeys = splitKeysByPlus(newHotkey);\n const conflictKeys = splitKeysByPlus(conflictKey);\n return isEqual(newKeys, conflictKeys);\n });\n },\n [hotkeyConflicts],\n );\n\n // 当按键组合完成时处理结果\n useEffect(() => {\n if (recordedKeys.size > 0 && !isRecording) {\n if (!isValid) {\n setHasInvalidCombination(true);\n setHasConflict(false);\n return;\n }\n\n setHasInvalidCombination(false);\n const newKeysString = keysString;\n\n // 检查冲突\n const conflict = checkHotkeyConflict(newKeysString);\n if (conflict) {\n setHasConflict(true);\n onConflict?.(newKeysString);\n } else {\n setHasConflict(false);\n setHotkeyValue?.(newKeysString);\n }\n }\n }, [\n recordedKeys,\n isRecording,\n isValid,\n keysString,\n checkHotkeyConflict,\n setHotkeyValue,\n onConflict,\n ]);\n\n // 处理输入框焦点\n const handleFocus = (e: FocusEvent<HTMLInputElement>) => {\n if (disabled) return;\n setIsFocused(true);\n setHasConflict(false);\n setHasInvalidCombination(false);\n start(); // 开始记录\n onFocus?.(e);\n };\n\n const handleBlur = (e: FocusEvent<HTMLInputElement>) => {\n setIsFocused(false);\n stop(); // 停止记录\n onBlur?.(e);\n };\n\n const handleClear = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.('');\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop();\n onClear?.(hotkeyValue);\n };\n\n // 重置功能\n const handleReset = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setHotkeyValue?.(resetValue);\n resetKeys();\n setHasConflict(false);\n setHasInvalidCombination(false);\n setIsFocused(false);\n stop(); // 停止记录\n onReset?.(hotkeyValue, resetValue);\n };\n\n const handleClick = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled || isFocused) return;\n inputRef.current?.focus();\n };\n\n const placeholderText = placeholder ?? t('hotkey.placeholder');\n const resetTitle = texts?.reset ?? t('hotkey.reset');\n const clearTitle = texts?.clear ?? t('hotkey.clear');\n const conflictText = texts?.conflicts ?? t('hotkey.conflict');\n const invalidText = texts?.invalidCombination ?? t('hotkey.invalidCombination');\n\n return (\n <Flexbox\n className={className}\n gap={8}\n style={{\n position: 'relative',\n ...style,\n }}\n >\n <Flexbox\n horizontal\n align={'center'}\n justify={'space-between'}\n className={cx(\n variants({\n disabled,\n error: hasConflict || hasInvalidCombination,\n focused: isFocused,\n shadow,\n variant: variant || (isDarkMode ? 'filled' : 'outlined'),\n }),\n )}\n onClick={handleClick}\n >\n <div style={{ pointerEvents: 'none' }}>\n {isRecording ? (\n <span className={styles.placeholder}>\n {keys.length > 0 ? <Hotkey keys={keysString} /> : placeholderText}\n </span>\n ) : hotkeyValue ? (\n <Hotkey keys={hotkeyValue} />\n ) : (\n <span className={styles.placeholder}>{placeholderText}</span>\n )}\n </div>\n\n {/* 隐藏的输入框,用于接收焦点 */}\n <input\n readOnly\n className={styles.hiddenInput}\n disabled={disabled}\n ref={inputRef as any}\n style={{ pointerEvents: 'none' }}\n onBlur={handleBlur}\n onFocus={handleFocus}\n />\n\n {!isFocused && hotkeyValue && !disabled && (allowReset || allowClear) && (\n <Flexbox horizontal gap={4}>\n {allowReset && hotkeyValue !== resetValue && (\n <ActionIcon\n icon={Undo2Icon}\n size={'small'}\n title={resetTitle}\n variant={'filled'}\n onClick={handleReset}\n />\n )}\n {allowClear && (\n <ActionIcon\n icon={XIcon}\n size={'small'}\n title={clearTitle}\n variant={'filled'}\n onClick={handleClear}\n />\n )}\n </Flexbox>\n )}\n </Flexbox>\n {hasConflict && <div className={styles.errorText}>{conflictText}</div>}\n {hasInvalidCombination && <div className={styles.errorText}>{invalidText}</div>}\n </Flexbox>\n );\n },\n);\n\nHotkeyInput.displayName = 'HotkeyInput';\n\nexport default HotkeyInput;\n"],"mappings":"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"}
|
package/es/Icon/style.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.mjs","names":[],"sources":["../../src/Icon/style.ts"],"sourcesContent":["import { createStaticStyles, keyframes } from 'antd-style';\nimport { cva } from 'class-variance-authority';\n\nconst spin = keyframes`\n 0% {\n rotate: 0deg;\n }\n 100% {\n rotate: 360deg;\n }\n`;\n\nexport const styles = createStaticStyles(({ css }) => {\n return {\n spin: css`\n animation: ${spin} 1s linear infinite;\n `,\n };\n});\n\nexport const variants = cva('anticon', {\n defaultVariants: {\n spin: false,\n },\n\n variants: {\n spin: {\n false: null,\n true: styles.spin,\n },\n },\n});\n"],"mappings":";;;AAGA,MAAM,OAAO,SAAS;;;;;;;;AAiBtB,MAAa,WAAW,IAAI,WAAW;CACrC,iBAAiB,EACf,MAAM,OACP;CAED,UAAU,EACR,MAAM;EACJ,OAAO;EACP,MAhBgB,oBAAoB,EAAE,UAAU;AACpD,UAAO,EACL,MAAM,GAAG;mBACM,KAAK;OAErB;
|
|
1
|
+
{"version":3,"file":"style.mjs","names":[],"sources":["../../src/Icon/style.ts"],"sourcesContent":["import { createStaticStyles, keyframes } from 'antd-style';\nimport { cva } from 'class-variance-authority';\n\nconst spin = keyframes`\n 0% {\n rotate: 0deg;\n }\n 100% {\n rotate: 360deg;\n }\n`;\n\nexport const styles = createStaticStyles(({ css }) => {\n return {\n spin: css`\n animation: ${spin} 1s linear infinite;\n `,\n };\n});\n\nexport const variants = cva('anticon', {\n defaultVariants: {\n spin: false,\n },\n\n variants: {\n spin: {\n false: null,\n true: styles.spin,\n },\n },\n});\n"],"mappings":";;;AAGA,MAAM,OAAO,SAAS;;;;;;;;AAiBtB,MAAa,WAAW,IAAI,WAAW;CACrC,iBAAiB,EACf,MAAM,OACP;CAED,UAAU,EACR,MAAM;EACJ,OAAO;EACP,MAhBgB,oBAAoB,EAAE,UAAU;AACpD,UAAO,EACL,MAAM,GAAG;mBACM,KAAK;OAErB;IAWS,CAAO;EACd,EACF;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.mjs","names":["imageMessages","Flexbox"],"sources":["../../../src/Image/components/Toolbar.tsx"],"sourcesContent":["import { message } from 'antd';\nimport {\n Copy,\n Download,\n FlipHorizontal,\n FlipVertical,\n RotateCcw,\n RotateCw,\n ZoomIn,\n ZoomOut,\n} from 'lucide-react';\nimport { type ToolbarRenderInfoType } from 'rc-image/lib/Preview';\nimport { memo, type ReactNode, useCallback, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport imageMessages from '@/i18n/resources/en/image';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport { TooltipGroup } from '@/Tooltip';\nimport { getClipboardBlob } from '@/utils/blobToPng';\nimport { downloadBlob } from '@/utils/downloadBlob';\n\nimport { styles } from '../style';\n\nconst getFileNameFromUrl = (url: string): string => {\n try {\n const pathname = new URL(url).pathname;\n const match = pathname.match(/\\/([^/]+)$/);\n return match ? decodeURIComponent(match[1]) : 'image';\n } catch {\n return 'image';\n }\n};\n\nconst getExtensionFromMimeType = (mimeType: string): string => {\n const map: Record<string, string> = {\n 'image/svg+xml': 'svg',\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/jpg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n };\n return map[mimeType?.toLowerCase()] || mimeType?.split('/')[1]?.split('+')[0] || 'png';\n};\n\nexport interface ToolbarProps {\n children?: ReactNode;\n info: Omit<ToolbarRenderInfoType, 'current' | 'total'>;\n maxScale: number;\n minScale: number;\n}\n\nconst Toolbar = memo<ToolbarProps>(({ children, info, minScale, maxScale }) => {\n const { t } = useTranslation(imageMessages);\n const [containerEl, setContainerEl] = useState<HTMLElement | null>(null);\n const [copyLoading, setCopyLoading] = useState(false);\n const [downloadLoading, setDownloadLoading] = useState(false);\n const {\n transform: { scale },\n actions: { onFlipY, onFlipX, onRotateLeft, onRotateRight, onZoomOut, onZoomIn },\n image: { url },\n } = info;\n\n const handleDownload = useCallback(async () => {\n setDownloadLoading(true);\n try {\n const response = await fetch(url, { mode: 'cors' });\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n let fileName = getFileNameFromUrl(url);\n const ext = getExtensionFromMimeType(blob.type);\n if (!fileName.includes('.')) {\n fileName = `${fileName}.${ext}`;\n } else if (fileName.endsWith('.svg+xml')) {\n fileName = fileName.replace(/\\.svg\\+xml$/i, '.svg');\n }\n await downloadBlob(blobUrl, fileName);\n URL.revokeObjectURL(blobUrl);\n message.success(t('image.downloadSuccess'));\n } catch {\n message.error(t('image.downloadFailed'));\n } finally {\n setDownloadLoading(false);\n }\n }, [url, t]);\n\n const handleCopy = useCallback(async () => {\n setCopyLoading(true);\n try {\n const response = await fetch(url, { mode: 'cors' });\n const blob = await response.blob();\n const clipboardBlob = await getClipboardBlob(blob);\n await navigator.clipboard.write([new ClipboardItem(clipboardBlob)]);\n message.success(t('image.copySuccess'));\n } catch {\n message.error(t('image.copyFailed'));\n } finally {\n setCopyLoading(false);\n }\n }, [url, t]);\n\n return (\n <TooltipGroup popupContainer={containerEl ?? undefined}>\n <Flexbox horizontal className={styles.toolbar} gap={4} ref={setContainerEl}>\n <ActionIcon icon={FlipHorizontal} title={t('image.flipHorizontal')} onClick={onFlipX} />\n <ActionIcon icon={FlipVertical} title={t('image.flipVertical')} onClick={onFlipY} />\n <ActionIcon icon={RotateCcw} title={t('image.rotateLeft')} onClick={onRotateLeft} />\n <ActionIcon icon={RotateCw} title={t('image.rotateRight')} onClick={onRotateRight} />\n <ActionIcon\n disabled={scale === minScale}\n icon={ZoomOut}\n title={t('image.zoomOut')}\n onClick={onZoomOut}\n />\n <ActionIcon\n disabled={scale === maxScale}\n icon={ZoomIn}\n title={t('image.zoomIn')}\n onClick={onZoomIn}\n />\n <ActionIcon\n icon={Copy}\n loading={copyLoading}\n title={t('image.copy')}\n onClick={handleCopy}\n />\n <ActionIcon\n icon={Download}\n loading={downloadLoading}\n title={t('image.download')}\n onClick={handleDownload}\n />\n {children}\n </Flexbox>\n </TooltipGroup>\n );\n});\n\nexport default Toolbar;\n"],"mappings":";;;;;;;;;;;;;AAwBA,MAAM,sBAAsB,QAAwB;AAClD,KAAI;EAEF,MAAM,QADW,IAAI,IAAI,IAAI,CAAC,SACP,MAAM,aAAa;AAC1C,SAAO,QAAQ,mBAAmB,MAAM,GAAG,GAAG;SACxC;AACN,SAAO;;;AAIX,MAAM,4BAA4B,aAA6B;AAS7D,
|
|
1
|
+
{"version":3,"file":"Toolbar.mjs","names":["imageMessages","Flexbox"],"sources":["../../../src/Image/components/Toolbar.tsx"],"sourcesContent":["import { message } from 'antd';\nimport {\n Copy,\n Download,\n FlipHorizontal,\n FlipVertical,\n RotateCcw,\n RotateCw,\n ZoomIn,\n ZoomOut,\n} from 'lucide-react';\nimport { type ToolbarRenderInfoType } from 'rc-image/lib/Preview';\nimport { memo, type ReactNode, useCallback, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport imageMessages from '@/i18n/resources/en/image';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport { TooltipGroup } from '@/Tooltip';\nimport { getClipboardBlob } from '@/utils/blobToPng';\nimport { downloadBlob } from '@/utils/downloadBlob';\n\nimport { styles } from '../style';\n\nconst getFileNameFromUrl = (url: string): string => {\n try {\n const pathname = new URL(url).pathname;\n const match = pathname.match(/\\/([^/]+)$/);\n return match ? decodeURIComponent(match[1]) : 'image';\n } catch {\n return 'image';\n }\n};\n\nconst getExtensionFromMimeType = (mimeType: string): string => {\n const map: Record<string, string> = {\n 'image/svg+xml': 'svg',\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/jpg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n };\n return map[mimeType?.toLowerCase()] || mimeType?.split('/')[1]?.split('+')[0] || 'png';\n};\n\nexport interface ToolbarProps {\n children?: ReactNode;\n info: Omit<ToolbarRenderInfoType, 'current' | 'total'>;\n maxScale: number;\n minScale: number;\n}\n\nconst Toolbar = memo<ToolbarProps>(({ children, info, minScale, maxScale }) => {\n const { t } = useTranslation(imageMessages);\n const [containerEl, setContainerEl] = useState<HTMLElement | null>(null);\n const [copyLoading, setCopyLoading] = useState(false);\n const [downloadLoading, setDownloadLoading] = useState(false);\n const {\n transform: { scale },\n actions: { onFlipY, onFlipX, onRotateLeft, onRotateRight, onZoomOut, onZoomIn },\n image: { url },\n } = info;\n\n const handleDownload = useCallback(async () => {\n setDownloadLoading(true);\n try {\n const response = await fetch(url, { mode: 'cors' });\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n let fileName = getFileNameFromUrl(url);\n const ext = getExtensionFromMimeType(blob.type);\n if (!fileName.includes('.')) {\n fileName = `${fileName}.${ext}`;\n } else if (fileName.endsWith('.svg+xml')) {\n fileName = fileName.replace(/\\.svg\\+xml$/i, '.svg');\n }\n await downloadBlob(blobUrl, fileName);\n URL.revokeObjectURL(blobUrl);\n message.success(t('image.downloadSuccess'));\n } catch {\n message.error(t('image.downloadFailed'));\n } finally {\n setDownloadLoading(false);\n }\n }, [url, t]);\n\n const handleCopy = useCallback(async () => {\n setCopyLoading(true);\n try {\n const response = await fetch(url, { mode: 'cors' });\n const blob = await response.blob();\n const clipboardBlob = await getClipboardBlob(blob);\n await navigator.clipboard.write([new ClipboardItem(clipboardBlob)]);\n message.success(t('image.copySuccess'));\n } catch {\n message.error(t('image.copyFailed'));\n } finally {\n setCopyLoading(false);\n }\n }, [url, t]);\n\n return (\n <TooltipGroup popupContainer={containerEl ?? undefined}>\n <Flexbox horizontal className={styles.toolbar} gap={4} ref={setContainerEl}>\n <ActionIcon icon={FlipHorizontal} title={t('image.flipHorizontal')} onClick={onFlipX} />\n <ActionIcon icon={FlipVertical} title={t('image.flipVertical')} onClick={onFlipY} />\n <ActionIcon icon={RotateCcw} title={t('image.rotateLeft')} onClick={onRotateLeft} />\n <ActionIcon icon={RotateCw} title={t('image.rotateRight')} onClick={onRotateRight} />\n <ActionIcon\n disabled={scale === minScale}\n icon={ZoomOut}\n title={t('image.zoomOut')}\n onClick={onZoomOut}\n />\n <ActionIcon\n disabled={scale === maxScale}\n icon={ZoomIn}\n title={t('image.zoomIn')}\n onClick={onZoomIn}\n />\n <ActionIcon\n icon={Copy}\n loading={copyLoading}\n title={t('image.copy')}\n onClick={handleCopy}\n />\n <ActionIcon\n icon={Download}\n loading={downloadLoading}\n title={t('image.download')}\n onClick={handleDownload}\n />\n {children}\n </Flexbox>\n </TooltipGroup>\n );\n});\n\nexport default Toolbar;\n"],"mappings":"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"}
|
package/es/Img/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/Img/index.tsx"],"sourcesContent":["'use client';\n\nimport { type ImageProps } from 'antd';\nimport { createElement, type ElementType, type FC, memo, type Ref, use, useMemo } from 'react';\n\nimport { ConfigContext } from '@/ConfigProvider';\nimport { type ImgProps as HtmlImgeProps } from '@/types';\n\nconst createContainer = (as: ElementType) => memo((props: any) => createElement(as, props));\n\ntype ImgProps = HtmlImgeProps & ImageProps & { ref?: Ref<HTMLImageElement>; unoptimized?: boolean };\n\nconst Img: FC<ImgProps> = ({ unoptimized, ...rest }) => {\n const config = use(ConfigContext);\n const render = config?.imgAs || 'img';\n\n const ImgContainer = useMemo(() => createContainer(render), [render]);\n\n return (\n <ImgContainer\n unoptimized={unoptimized === undefined ? config?.imgUnoptimized : unoptimized}\n {...rest}\n />\n );\n};\n\nImg.displayName = 'Img';\n\nexport default Img;\n"],"mappings":";;;;;AAQA,MAAM,mBAAmB,OAAoB,MAAM,UAAe,cAAc,IAAI,MAAM,CAAC;AAI3F,MAAM,OAAqB,EAAE,aAAa,GAAG,WAAW;CACtD,MAAM,SAAS,IAAI,cAAc;CACjC,MAAM,SAAS,QAAQ,SAAS;AAIhC,QACE,oBAHmB,cAAc,gBAAgB,OAAO,EAAE,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/Img/index.tsx"],"sourcesContent":["'use client';\n\nimport { type ImageProps } from 'antd';\nimport { createElement, type ElementType, type FC, memo, type Ref, use, useMemo } from 'react';\n\nimport { ConfigContext } from '@/ConfigProvider';\nimport { type ImgProps as HtmlImgeProps } from '@/types';\n\nconst createContainer = (as: ElementType) => memo((props: any) => createElement(as, props));\n\ntype ImgProps = HtmlImgeProps & ImageProps & { ref?: Ref<HTMLImageElement>; unoptimized?: boolean };\n\nconst Img: FC<ImgProps> = ({ unoptimized, ...rest }) => {\n const config = use(ConfigContext);\n const render = config?.imgAs || 'img';\n\n const ImgContainer = useMemo(() => createContainer(render), [render]);\n\n return (\n <ImgContainer\n unoptimized={unoptimized === undefined ? config?.imgUnoptimized : unoptimized}\n {...rest}\n />\n );\n};\n\nImg.displayName = 'Img';\n\nexport default Img;\n"],"mappings":";;;;;AAQA,MAAM,mBAAmB,OAAoB,MAAM,UAAe,cAAc,IAAI,MAAM,CAAC;AAI3F,MAAM,OAAqB,EAAE,aAAa,GAAG,WAAW;CACtD,MAAM,SAAS,IAAI,cAAc;CACjC,MAAM,SAAS,QAAQ,SAAS;AAIhC,QACE,oBAHmB,cAAc,gBAAgB,OAAO,EAAE,CAAC,OAAO,CAGrD,EAAb;EACE,aAAa,gBAAgB,KAAA,IAAY,QAAQ,iBAAiB;EAClE,GAAI;EACJ,CAAA;;AAIN,IAAI,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamdownRender.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockBirthsRef = useRef<Map<number, number[]>>(new Map());\n\n const renderNow = getNow();\n\n const birthsResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const nextBirths = new Map<number, number[]>();\n const prevBirths = blockBirthsRef.current;\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n // Queued blocks are not rendered. Defer birth assignment so that\n // when the block later transitions to animating/streaming, its\n // chars start fading from that moment instead of having already\n // \"aged out\" of the fade window.\n if (state === 'queued') continue;\n\n const blockCharCount = countChars(block.content);\n const prev = prevBirths.get(block.startOffset);\n let arr: number[];\n\n if (prev && prev.length === blockCharCount) {\n arr = prev;\n } else if (prev && prev.length > blockCharCount) {\n // Block content shrunk (stream restart or upstream rewrite).\n arr = prev.slice(0, blockCharCount);\n } else {\n arr = prev ? prev.slice() : [];\n const startIdx = arr.length;\n // Chain each new char monotonically after the previous one so fades\n // never race out of order. Cap how far the fade queue can run ahead\n // of renderNow to prevent stream-faster-than-fade producing seconds\n // of invisible backlog at the tail.\n const cap = renderNow + STREAM_FADE_DURATION;\n for (let i = startIdx; i < blockCharCount; i++) {\n const prevBirth = i > 0 ? (arr[i - 1] as number) : renderNow - charDelay;\n const chained = prevBirth + charDelay;\n arr.push(Math.min(cap, Math.max(chained, renderNow)));\n }\n }\n\n nextBirths.set(block.startOffset, arr);\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: nextBirths,\n };\n }, [blocks, charDelay, getBlockState, profiler, renderNow]);\n const birthsForRender = birthsResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: birthsResult.durationMs,\n itemCount: blocks.length,\n name: 'block-births',\n textLength: processedContent.length,\n });\n }, [birthsResult.durationMs, blocks.length, processedContent.length, profiler]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const births = birthsForRender.get(block.startOffset);\n const lastBirthTs = births && births.length > 0 ? (births.at(-1) ?? renderNow) : renderNow;\n const lastElapsedMs = renderNow - lastBirthTs;\n const animationMeta = resolveBlockAnimationMeta({\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n lastElapsedMs,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [birthsForRender, blocks, charDelay, getBlockState, renderNow]);\n\n useEffect(() => {\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n blockBirthsRef.current = birthsForRender;\n }, [birthsForRender, blockAnimationMetaResult.blockCharDelay]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const births = birthsForRender.get(block.startOffset);\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n births,\n fadeDuration: STREAM_FADE_DURATION,\n nowMs: renderNow,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StreamdownRender.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockBirthsRef = useRef<Map<number, number[]>>(new Map());\n\n const renderNow = getNow();\n\n const birthsResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const nextBirths = new Map<number, number[]>();\n const prevBirths = blockBirthsRef.current;\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n // Queued blocks are not rendered. Defer birth assignment so that\n // when the block later transitions to animating/streaming, its\n // chars start fading from that moment instead of having already\n // \"aged out\" of the fade window.\n if (state === 'queued') continue;\n\n const blockCharCount = countChars(block.content);\n const prev = prevBirths.get(block.startOffset);\n let arr: number[];\n\n if (prev && prev.length === blockCharCount) {\n arr = prev;\n } else if (prev && prev.length > blockCharCount) {\n // Block content shrunk (stream restart or upstream rewrite).\n arr = prev.slice(0, blockCharCount);\n } else {\n arr = prev ? prev.slice() : [];\n const startIdx = arr.length;\n // Chain each new char monotonically after the previous one so fades\n // never race out of order. Cap how far the fade queue can run ahead\n // of renderNow to prevent stream-faster-than-fade producing seconds\n // of invisible backlog at the tail.\n const cap = renderNow + STREAM_FADE_DURATION;\n for (let i = startIdx; i < blockCharCount; i++) {\n const prevBirth = i > 0 ? (arr[i - 1] as number) : renderNow - charDelay;\n const chained = prevBirth + charDelay;\n arr.push(Math.min(cap, Math.max(chained, renderNow)));\n }\n }\n\n nextBirths.set(block.startOffset, arr);\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: nextBirths,\n };\n }, [blocks, charDelay, getBlockState, profiler, renderNow]);\n const birthsForRender = birthsResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: birthsResult.durationMs,\n itemCount: blocks.length,\n name: 'block-births',\n textLength: processedContent.length,\n });\n }, [birthsResult.durationMs, blocks.length, processedContent.length, profiler]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const births = birthsForRender.get(block.startOffset);\n const lastBirthTs = births && births.length > 0 ? (births.at(-1) ?? renderNow) : renderNow;\n const lastElapsedMs = renderNow - lastBirthTs;\n const animationMeta = resolveBlockAnimationMeta({\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n lastElapsedMs,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [birthsForRender, blocks, charDelay, getBlockState, renderNow]);\n\n useEffect(() => {\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n blockBirthsRef.current = birthsForRender;\n }, [birthsForRender, blockAnimationMetaResult.blockCharDelay]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const births = birthsForRender.get(block.startOffset);\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n births,\n fadeDuration: STREAM_FADE_DURATION,\n nowMs: renderNow,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSmoothStreamContent.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/useSmoothStreamContent.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\nimport { type StreamSmoothingPreset } from '@/Markdown/type';\n\ninterface StreamSmoothingPresetConfig {\n activeInputWindowMs: number;\n defaultCps: number;\n emaAlpha: number;\n flushCps: number;\n largeAppendChars: number;\n maxActiveCps: number;\n maxCps: number;\n maxFlushCps: number;\n minCps: number;\n settleAfterMs: number;\n settleDrainMaxMs: number;\n settleDrainMinMs: number;\n targetBufferMs: number;\n}\n\nconst PRESET_CONFIG: Record<StreamSmoothingPreset, StreamSmoothingPresetConfig> = {\n balanced: {\n activeInputWindowMs: 220,\n defaultCps: 38,\n emaAlpha: 0.2,\n flushCps: 120,\n largeAppendChars: 120,\n maxActiveCps: 132,\n maxCps: 72,\n maxFlushCps: 280,\n minCps: 18,\n settleAfterMs: 360,\n settleDrainMaxMs: 520,\n settleDrainMinMs: 180,\n targetBufferMs: 120,\n },\n realtime: {\n activeInputWindowMs: 140,\n defaultCps: 50,\n emaAlpha: 0.3,\n flushCps: 170,\n largeAppendChars: 180,\n maxActiveCps: 180,\n maxCps: 96,\n maxFlushCps: 360,\n minCps: 24,\n settleAfterMs: 260,\n settleDrainMaxMs: 360,\n settleDrainMinMs: 140,\n targetBufferMs: 40,\n },\n silky: {\n activeInputWindowMs: 320,\n defaultCps: 28,\n emaAlpha: 0.14,\n flushCps: 96,\n largeAppendChars: 100,\n maxActiveCps: 102,\n maxCps: 56,\n maxFlushCps: 220,\n minCps: 14,\n settleAfterMs: 460,\n settleDrainMaxMs: 680,\n settleDrainMinMs: 240,\n targetBufferMs: 170,\n },\n};\n\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.min(max, Math.max(min, value));\n};\n\nconst getNow = () => {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n};\n\nexport const countChars = (text: string): number => {\n return [...text].length;\n};\n\ninterface UseSmoothStreamContentOptions {\n enabled?: boolean;\n preset?: StreamSmoothingPreset;\n}\n\nexport const useSmoothStreamContent = (\n content: string,\n { enabled = true, preset = 'balanced' }: UseSmoothStreamContentOptions = {},\n): string => {\n const config = PRESET_CONFIG[preset];\n const profiler = useStreamdownProfiler();\n const [displayedContent, setDisplayedContent] = useState(content);\n\n const displayedContentRef = useRef(content);\n const displayedCountRef = useRef(countChars(content));\n\n const targetContentRef = useRef(content);\n const targetCharsRef = useRef([...content]);\n const targetCountRef = useRef(targetCharsRef.current.length);\n\n const emaCpsRef = useRef(config.defaultCps);\n const lastInputTsRef = useRef(0);\n const lastInputCountRef = useRef(targetCountRef.current);\n const chunkSizeEmaRef = useRef(1);\n const arrivalCpsEmaRef = useRef(config.defaultCps);\n\n const rafRef = useRef<number | null>(null);\n const lastFrameTsRef = useRef<number | null>(null);\n const wakeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearWakeTimer = useCallback(() => {\n if (wakeTimerRef.current !== null) {\n clearTimeout(wakeTimerRef.current);\n wakeTimerRef.current = null;\n }\n }, []);\n\n const stopFrameLoop = useCallback(() => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n lastFrameTsRef.current = null;\n }, []);\n\n const stopScheduling = useCallback(() => {\n stopFrameLoop();\n clearWakeTimer();\n }, [clearWakeTimer, stopFrameLoop]);\n\n const startFrameLoopRef = useRef<() => void>(() => {});\n\n const scheduleFrameWake = useCallback(\n (delayMs: number) => {\n clearWakeTimer();\n\n wakeTimerRef.current = setTimeout(\n () => {\n wakeTimerRef.current = null;\n startFrameLoopRef.current();\n },\n Math.max(1, Math.ceil(delayMs)),\n );\n },\n [clearWakeTimer],\n );\n\n const syncImmediate = useCallback(\n (nextContent: string) => {\n stopScheduling();\n\n const chars = [...nextContent];\n const now = getNow();\n\n targetContentRef.current = nextContent;\n targetCharsRef.current = chars;\n targetCountRef.current = chars.length;\n\n displayedContentRef.current = nextContent;\n displayedCountRef.current = chars.length;\n setDisplayedContent(nextContent);\n\n emaCpsRef.current = config.defaultCps;\n chunkSizeEmaRef.current = 1;\n arrivalCpsEmaRef.current = config.defaultCps;\n lastInputTsRef.current = now;\n lastInputCountRef.current = chars.length;\n },\n [config.defaultCps, stopScheduling],\n );\n\n const startFrameLoop = useCallback(() => {\n clearWakeTimer();\n if (rafRef.current !== null) return;\n\n const tick = (ts: number) => {\n const frameStart = getNow();\n\n if (lastFrameTsRef.current === null) {\n lastFrameTsRef.current = ts;\n rafRef.current = requestAnimationFrame(tick);\n return;\n }\n\n const frameIntervalMs = Math.max(0, ts - lastFrameTsRef.current);\n const dtSeconds = Math.max(0.001, Math.min(frameIntervalMs / 1000, 0.05));\n lastFrameTsRef.current = ts;\n\n const targetCount = targetCountRef.current;\n const displayedCount = displayedCountRef.current;\n const backlog = targetCount - displayedCount;\n\n if (backlog <= 0) {\n stopFrameLoop();\n return;\n }\n\n const now = getNow();\n const idleMs = now - lastInputTsRef.current;\n const inputActive = idleMs <= config.activeInputWindowMs;\n const settling = !inputActive && idleMs >= config.settleAfterMs;\n\n const baseCps = clamp(emaCpsRef.current, config.minCps, config.maxCps);\n const baseLagChars = Math.max(1, Math.round((baseCps * config.targetBufferMs) / 1000));\n const lagUpperBound = Math.max(baseLagChars + 2, baseLagChars * 3);\n const targetLagChars = inputActive\n ? Math.round(\n clamp(baseLagChars + chunkSizeEmaRef.current * 0.35, baseLagChars, lagUpperBound),\n )\n : 0;\n const desiredDisplayed = Math.max(0, targetCount - targetLagChars);\n\n let currentCps: number;\n if (inputActive) {\n const backlogPressure = targetLagChars > 0 ? backlog / targetLagChars : 1;\n const chunkPressure = targetLagChars > 0 ? chunkSizeEmaRef.current / targetLagChars : 1;\n const arrivalPressure = arrivalCpsEmaRef.current / Math.max(baseCps, 1);\n const combinedPressure = clamp(\n backlogPressure * 0.6 + chunkPressure * 0.25 + arrivalPressure * 0.15,\n 1,\n 4.5,\n );\n const activeCap = clamp(\n config.maxActiveCps + chunkSizeEmaRef.current * 6,\n config.maxActiveCps,\n config.maxFlushCps,\n );\n currentCps = clamp(baseCps * combinedPressure, config.minCps, activeCap);\n } else if (settling) {\n // If upstream likely ended, cap the remaining tail duration so\n // we do not keep replaying old backlog for seconds.\n const drainTargetMs = clamp(backlog * 8, config.settleDrainMinMs, config.settleDrainMaxMs);\n const settleCps = (backlog * 1000) / drainTargetMs;\n currentCps = clamp(settleCps, config.flushCps, config.maxFlushCps);\n } else {\n const idleFlushCps = Math.max(\n config.flushCps,\n baseCps * 1.8,\n arrivalCpsEmaRef.current * 0.8,\n );\n currentCps = clamp(idleFlushCps, config.flushCps, config.maxFlushCps);\n }\n\n const urgentBacklog = inputActive && targetLagChars > 0 && backlog > targetLagChars * 2.2;\n const burstyInput = inputActive && chunkSizeEmaRef.current >= targetLagChars * 0.9;\n const minRevealChars = inputActive ? (urgentBacklog || burstyInput ? 2 : 1) : 2;\n let revealChars = Math.max(minRevealChars, Math.round(currentCps * dtSeconds));\n\n if (inputActive) {\n const shortfall = desiredDisplayed - displayedCount;\n if (shortfall <= 0) {\n stopFrameLoop();\n scheduleFrameWake(config.activeInputWindowMs - idleMs);\n\n profiler?.recordAnimationFrame({\n backlog,\n durationMs: getNow() - frameStart,\n frameIntervalMs,\n inputActive,\n revealChars: 0,\n settling,\n });\n return;\n }\n revealChars = Math.min(revealChars, shortfall, backlog);\n } else {\n revealChars = Math.min(revealChars, backlog);\n }\n\n const nextCount = displayedCount + revealChars;\n const segment = targetCharsRef.current.slice(displayedCount, nextCount).join('');\n\n if (segment) {\n const nextDisplayed = displayedContentRef.current + segment;\n displayedContentRef.current = nextDisplayed;\n displayedCountRef.current = nextCount;\n setDisplayedContent(nextDisplayed);\n } else {\n displayedContentRef.current = targetContentRef.current;\n displayedCountRef.current = targetCount;\n setDisplayedContent(targetContentRef.current);\n }\n\n profiler?.recordAnimationFrame({\n backlog,\n durationMs: getNow() - frameStart,\n frameIntervalMs,\n inputActive,\n revealChars: segment ? revealChars : backlog,\n settling,\n });\n\n rafRef.current = requestAnimationFrame(tick);\n };\n\n rafRef.current = requestAnimationFrame(tick);\n }, [\n clearWakeTimer,\n config.activeInputWindowMs,\n config.flushCps,\n config.maxActiveCps,\n config.maxCps,\n config.maxFlushCps,\n config.minCps,\n config.settleAfterMs,\n config.settleDrainMaxMs,\n config.settleDrainMinMs,\n config.targetBufferMs,\n scheduleFrameWake,\n stopFrameLoop,\n ]);\n startFrameLoopRef.current = startFrameLoop;\n\n useEffect(() => {\n if (!enabled) {\n syncImmediate(content);\n return;\n }\n\n const prevTargetContent = targetContentRef.current;\n if (content === prevTargetContent) return;\n\n const now = getNow();\n const appendOnly = content.startsWith(prevTargetContent);\n\n if (!appendOnly) {\n syncImmediate(content);\n return;\n }\n\n const appended = content.slice(prevTargetContent.length);\n const appendedChars = [...appended];\n const appendedCount = appendedChars.length;\n\n profiler?.recordInputAppend({\n appendedChars: appendedCount,\n contentLength: countChars(content),\n });\n\n if (appendedCount > config.largeAppendChars) {\n syncImmediate(content);\n return;\n }\n\n targetContentRef.current = content;\n targetCharsRef.current = [...targetCharsRef.current, ...appendedChars];\n targetCountRef.current += appendedCount;\n\n const deltaChars = targetCountRef.current - lastInputCountRef.current;\n const deltaMs = Math.max(1, now - lastInputTsRef.current);\n\n if (deltaChars > 0) {\n const instantCps = (deltaChars * 1000) / deltaMs;\n const normalizedInstantCps = clamp(instantCps, config.minCps, config.maxFlushCps * 2);\n const chunkEmaAlpha = 0.35;\n chunkSizeEmaRef.current =\n chunkSizeEmaRef.current * (1 - chunkEmaAlpha) + appendedCount * chunkEmaAlpha;\n arrivalCpsEmaRef.current =\n arrivalCpsEmaRef.current * (1 - chunkEmaAlpha) + normalizedInstantCps * chunkEmaAlpha;\n\n const clampedCps = clamp(instantCps, config.minCps, config.maxActiveCps);\n emaCpsRef.current = emaCpsRef.current * (1 - config.emaAlpha) + clampedCps * config.emaAlpha;\n }\n\n lastInputTsRef.current = now;\n lastInputCountRef.current = targetCountRef.current;\n\n startFrameLoop();\n }, [\n config.emaAlpha,\n config.largeAppendChars,\n config.maxActiveCps,\n config.maxCps,\n config.maxFlushCps,\n config.minCps,\n content,\n enabled,\n startFrameLoop,\n syncImmediate,\n profiler,\n ]);\n\n useEffect(() => {\n return () => {\n stopScheduling();\n };\n }, [stopScheduling]);\n\n return displayedContent;\n};\n"],"mappings":";;;AAqBA,MAAM,gBAA4E;CAChF,UAAU;EACR,qBAAqB;EACrB,YAAY;EACZ,UAAU;EACV,UAAU;EACV,kBAAkB;EAClB,cAAc;EACd,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,eAAe;EACf,kBAAkB;EAClB,kBAAkB;EAClB,gBAAgB;EACjB;CACD,UAAU;EACR,qBAAqB;EACrB,YAAY;EACZ,UAAU;EACV,UAAU;EACV,kBAAkB;EAClB,cAAc;EACd,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,eAAe;EACf,kBAAkB;EAClB,kBAAkB;EAClB,gBAAgB;EACjB;CACD,OAAO;EACL,qBAAqB;EACrB,YAAY;EACZ,UAAU;EACV,UAAU;EACV,kBAAkB;EAClB,cAAc;EACd,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,eAAe;EACf,kBAAkB;EAClB,kBAAkB;EAClB,gBAAgB;EACjB;CACF;AAED,MAAM,SAAS,OAAe,KAAa,QAAwB;AACjE,QAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM,CAAC;;AAG5C,MAAM,eAAe;AACnB,QAAO,OAAO,gBAAgB,cAAc,KAAK,KAAK,GAAG,YAAY,KAAK;;AAG5E,MAAa,cAAc,SAAyB;AAClD,QAAO,CAAC,GAAG,KAAK,CAAC;;AAQnB,MAAa,0BACX,SACA,EAAE,UAAU,MAAM,SAAS,eAA8C,EAAE,KAChE;CACX,MAAM,SAAS,cAAc;CAC7B,MAAM,WAAW,uBAAuB;CACxC,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,QAAQ;CAEjE,MAAM,sBAAsB,OAAO,QAAQ;CAC3C,MAAM,oBAAoB,OAAO,WAAW,QAAQ,CAAC;CAErD,MAAM,mBAAmB,OAAO,QAAQ;CACxC,MAAM,iBAAiB,OAAO,CAAC,GAAG,QAAQ,CAAC;CAC3C,MAAM,iBAAiB,OAAO,eAAe,QAAQ,OAAO;CAE5D,MAAM,YAAY,OAAO,OAAO,WAAW;CAC3C,MAAM,iBAAiB,OAAO,EAAE;CAChC,MAAM,oBAAoB,OAAO,eAAe,QAAQ;CACxD,MAAM,kBAAkB,OAAO,EAAE;CACjC,MAAM,mBAAmB,OAAO,OAAO,WAAW;CAElD,MAAM,SAAS,OAAsB,KAAK;CAC1C,MAAM,iBAAiB,OAAsB,KAAK;CAClD,MAAM,eAAe,OAA6C,KAAK;CAEvE,MAAM,iBAAiB,kBAAkB;AACvC,MAAI,aAAa,YAAY,MAAM;AACjC,gBAAa,aAAa,QAAQ;AAClC,gBAAa,UAAU;;IAExB,EAAE,CAAC;CAEN,MAAM,gBAAgB,kBAAkB;AACtC,MAAI,OAAO,YAAY,MAAM;AAC3B,wBAAqB,OAAO,QAAQ;AACpC,UAAO,UAAU;;AAEnB,iBAAe,UAAU;IACxB,EAAE,CAAC;CAEN,MAAM,iBAAiB,kBAAkB;AACvC,iBAAe;AACf,kBAAgB;IACf,CAAC,gBAAgB,cAAc,CAAC;CAEnC,MAAM,oBAAoB,aAAyB,GAAG;CAEtD,MAAM,oBAAoB,aACvB,YAAoB;AACnB,kBAAgB;AAEhB,eAAa,UAAU,iBACf;AACJ,gBAAa,UAAU;AACvB,qBAAkB,SAAS;KAE7B,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,CAAC,CAChC;IAEH,CAAC,eAAe,CACjB;CAED,MAAM,gBAAgB,aACnB,gBAAwB;AACvB,kBAAgB;EAEhB,MAAM,QAAQ,CAAC,GAAG,YAAY;EAC9B,MAAM,MAAM,QAAQ;AAEpB,mBAAiB,UAAU;AAC3B,iBAAe,UAAU;AACzB,iBAAe,UAAU,MAAM;AAE/B,sBAAoB,UAAU;AAC9B,oBAAkB,UAAU,MAAM;AAClC,sBAAoB,YAAY;AAEhC,YAAU,UAAU,OAAO;AAC3B,kBAAgB,UAAU;AAC1B,mBAAiB,UAAU,OAAO;AAClC,iBAAe,UAAU;AACzB,oBAAkB,UAAU,MAAM;IAEpC,CAAC,OAAO,YAAY,eAAe,CACpC;CAED,MAAM,iBAAiB,kBAAkB;AACvC,kBAAgB;AAChB,MAAI,OAAO,YAAY,KAAM;EAE7B,MAAM,QAAQ,OAAe;GAC3B,MAAM,aAAa,QAAQ;AAE3B,OAAI,eAAe,YAAY,MAAM;AACnC,mBAAe,UAAU;AACzB,WAAO,UAAU,sBAAsB,KAAK;AAC5C;;GAGF,MAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,eAAe,QAAQ;GAChE,MAAM,YAAY,KAAK,IAAI,MAAO,KAAK,IAAI,kBAAkB,KAAM,IAAK,CAAC;AACzE,kBAAe,UAAU;GAEzB,MAAM,cAAc,eAAe;GACnC,MAAM,iBAAiB,kBAAkB;GACzC,MAAM,UAAU,cAAc;AAE9B,OAAI,WAAW,GAAG;AAChB,mBAAe;AACf;;GAIF,MAAM,SADM,QAAQ,GACC,eAAe;GACpC,MAAM,cAAc,UAAU,OAAO;GACrC,MAAM,WAAW,CAAC,eAAe,UAAU,OAAO;GAElD,MAAM,UAAU,MAAM,UAAU,SAAS,OAAO,QAAQ,OAAO,OAAO;GACtE,MAAM,eAAe,KAAK,IAAI,GAAG,KAAK,MAAO,UAAU,OAAO,iBAAkB,IAAK,CAAC;GACtF,MAAM,gBAAgB,KAAK,IAAI,eAAe,GAAG,eAAe,EAAE;GAClE,MAAM,iBAAiB,cACnB,KAAK,MACH,MAAM,eAAe,gBAAgB,UAAU,KAAM,cAAc,cAAc,CAClF,GACD;GACJ,MAAM,mBAAmB,KAAK,IAAI,GAAG,cAAc,eAAe;GAElE,IAAI;AACJ,OAAI,aAAa;IACf,MAAM,kBAAkB,iBAAiB,IAAI,UAAU,iBAAiB;IACxE,MAAM,gBAAgB,iBAAiB,IAAI,gBAAgB,UAAU,iBAAiB;IACtF,MAAM,kBAAkB,iBAAiB,UAAU,KAAK,IAAI,SAAS,EAAE;IACvE,MAAM,mBAAmB,MACvB,kBAAkB,KAAM,gBAAgB,MAAO,kBAAkB,KACjE,GACA,IACD;IACD,MAAM,YAAY,MAChB,OAAO,eAAe,gBAAgB,UAAU,GAChD,OAAO,cACP,OAAO,YACR;AACD,iBAAa,MAAM,UAAU,kBAAkB,OAAO,QAAQ,UAAU;cAC/D,UAAU;IAGnB,MAAM,gBAAgB,MAAM,UAAU,GAAG,OAAO,kBAAkB,OAAO,iBAAiB;AAE1F,iBAAa,MADM,UAAU,MAAQ,eACP,OAAO,UAAU,OAAO,YAAY;SAOlE,cAAa,MALQ,KAAK,IACxB,OAAO,UACP,UAAU,KACV,iBAAiB,UAAU,GAC5B,EACgC,OAAO,UAAU,OAAO,YAAY;GAGvE,MAAM,gBAAgB,eAAe,iBAAiB,KAAK,UAAU,iBAAiB;GACtF,MAAM,cAAc,eAAe,gBAAgB,WAAW,iBAAiB;GAE/E,IAAI,cAAc,KAAK,IADA,cAAe,iBAAiB,cAAc,IAAI,IAAK,GACnC,KAAK,MAAM,aAAa,UAAU,CAAC;AAE9E,OAAI,aAAa;IACf,MAAM,YAAY,mBAAmB;AACrC,QAAI,aAAa,GAAG;AAClB,oBAAe;AACf,uBAAkB,OAAO,sBAAsB,OAAO;AAEtD,eAAU,qBAAqB;MAC7B;MACA,YAAY,QAAQ,GAAG;MACvB;MACA;MACA,aAAa;MACb;MACD,CAAC;AACF;;AAEF,kBAAc,KAAK,IAAI,aAAa,WAAW,QAAQ;SAEvD,eAAc,KAAK,IAAI,aAAa,QAAQ;GAG9C,MAAM,YAAY,iBAAiB;GACnC,MAAM,UAAU,eAAe,QAAQ,MAAM,gBAAgB,UAAU,CAAC,KAAK,GAAG;AAEhF,OAAI,SAAS;IACX,MAAM,gBAAgB,oBAAoB,UAAU;AACpD,wBAAoB,UAAU;AAC9B,sBAAkB,UAAU;AAC5B,wBAAoB,cAAc;UAC7B;AACL,wBAAoB,UAAU,iBAAiB;AAC/C,sBAAkB,UAAU;AAC5B,wBAAoB,iBAAiB,QAAQ;;AAG/C,aAAU,qBAAqB;IAC7B;IACA,YAAY,QAAQ,GAAG;IACvB;IACA;IACA,aAAa,UAAU,cAAc;IACrC;IACD,CAAC;AAEF,UAAO,UAAU,sBAAsB,KAAK;;AAG9C,SAAO,UAAU,sBAAsB,KAAK;IAC3C;EACD;EACA,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP;EACA;EACD,CAAC;AACF,mBAAkB,UAAU;AAE5B,iBAAgB;AACd,MAAI,CAAC,SAAS;AACZ,iBAAc,QAAQ;AACtB;;EAGF,MAAM,oBAAoB,iBAAiB;AAC3C,MAAI,YAAY,kBAAmB;EAEnC,MAAM,MAAM,QAAQ;AAGpB,MAAI,CAFe,QAAQ,WAAW,kBAAkB,EAEvC;AACf,iBAAc,QAAQ;AACtB;;EAIF,MAAM,gBAAgB,CAAC,GADN,QAAQ,MAAM,kBAAkB,OAAO,CACrB;EACnC,MAAM,gBAAgB,cAAc;AAEpC,YAAU,kBAAkB;GAC1B,eAAe;GACf,eAAe,WAAW,QAAQ;GACnC,CAAC;AAEF,MAAI,gBAAgB,OAAO,kBAAkB;AAC3C,iBAAc,QAAQ;AACtB;;AAGF,mBAAiB,UAAU;AAC3B,iBAAe,UAAU,CAAC,GAAG,eAAe,SAAS,GAAG,cAAc;AACtE,iBAAe,WAAW;EAE1B,MAAM,aAAa,eAAe,UAAU,kBAAkB;EAC9D,MAAM,UAAU,KAAK,IAAI,GAAG,MAAM,eAAe,QAAQ;AAEzD,MAAI,aAAa,GAAG;GAClB,MAAM,aAAc,aAAa,MAAQ;GACzC,MAAM,uBAAuB,MAAM,YAAY,OAAO,QAAQ,OAAO,cAAc,EAAE;GACrF,MAAM,gBAAgB;AACtB,mBAAgB,UACd,gBAAgB,WAAW,IAAI,iBAAiB,gBAAgB;AAClE,oBAAiB,UACf,iBAAiB,WAAW,IAAI,iBAAiB,uBAAuB;GAE1E,MAAM,aAAa,MAAM,YAAY,OAAO,QAAQ,OAAO,aAAa;AACxE,aAAU,UAAU,UAAU,WAAW,IAAI,OAAO,YAAY,aAAa,OAAO;;AAGtF,iBAAe,UAAU;AACzB,oBAAkB,UAAU,eAAe;AAE3C,kBAAgB;IACf;EACD,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,iBAAgB;AACd,eAAa;AACX,mBAAgB;;IAEjB,CAAC,eAAe,CAAC;AAEpB,QAAO"}
|
|
1
|
+
{"version":3,"file":"useSmoothStreamContent.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/useSmoothStreamContent.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\nimport { type StreamSmoothingPreset } from '@/Markdown/type';\n\ninterface StreamSmoothingPresetConfig {\n activeInputWindowMs: number;\n defaultCps: number;\n emaAlpha: number;\n flushCps: number;\n largeAppendChars: number;\n maxActiveCps: number;\n maxCps: number;\n maxFlushCps: number;\n minCps: number;\n settleAfterMs: number;\n settleDrainMaxMs: number;\n settleDrainMinMs: number;\n targetBufferMs: number;\n}\n\nconst PRESET_CONFIG: Record<StreamSmoothingPreset, StreamSmoothingPresetConfig> = {\n balanced: {\n activeInputWindowMs: 220,\n defaultCps: 38,\n emaAlpha: 0.2,\n flushCps: 120,\n largeAppendChars: 120,\n maxActiveCps: 132,\n maxCps: 72,\n maxFlushCps: 280,\n minCps: 18,\n settleAfterMs: 360,\n settleDrainMaxMs: 520,\n settleDrainMinMs: 180,\n targetBufferMs: 120,\n },\n realtime: {\n activeInputWindowMs: 140,\n defaultCps: 50,\n emaAlpha: 0.3,\n flushCps: 170,\n largeAppendChars: 180,\n maxActiveCps: 180,\n maxCps: 96,\n maxFlushCps: 360,\n minCps: 24,\n settleAfterMs: 260,\n settleDrainMaxMs: 360,\n settleDrainMinMs: 140,\n targetBufferMs: 40,\n },\n silky: {\n activeInputWindowMs: 320,\n defaultCps: 28,\n emaAlpha: 0.14,\n flushCps: 96,\n largeAppendChars: 100,\n maxActiveCps: 102,\n maxCps: 56,\n maxFlushCps: 220,\n minCps: 14,\n settleAfterMs: 460,\n settleDrainMaxMs: 680,\n settleDrainMinMs: 240,\n targetBufferMs: 170,\n },\n};\n\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.min(max, Math.max(min, value));\n};\n\nconst getNow = () => {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n};\n\nexport const countChars = (text: string): number => {\n return [...text].length;\n};\n\ninterface UseSmoothStreamContentOptions {\n enabled?: boolean;\n preset?: StreamSmoothingPreset;\n}\n\nexport const useSmoothStreamContent = (\n content: string,\n { enabled = true, preset = 'balanced' }: UseSmoothStreamContentOptions = {},\n): string => {\n const config = PRESET_CONFIG[preset];\n const profiler = useStreamdownProfiler();\n const [displayedContent, setDisplayedContent] = useState(content);\n\n const displayedContentRef = useRef(content);\n const displayedCountRef = useRef(countChars(content));\n\n const targetContentRef = useRef(content);\n const targetCharsRef = useRef([...content]);\n const targetCountRef = useRef(targetCharsRef.current.length);\n\n const emaCpsRef = useRef(config.defaultCps);\n const lastInputTsRef = useRef(0);\n const lastInputCountRef = useRef(targetCountRef.current);\n const chunkSizeEmaRef = useRef(1);\n const arrivalCpsEmaRef = useRef(config.defaultCps);\n\n const rafRef = useRef<number | null>(null);\n const lastFrameTsRef = useRef<number | null>(null);\n const wakeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearWakeTimer = useCallback(() => {\n if (wakeTimerRef.current !== null) {\n clearTimeout(wakeTimerRef.current);\n wakeTimerRef.current = null;\n }\n }, []);\n\n const stopFrameLoop = useCallback(() => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n lastFrameTsRef.current = null;\n }, []);\n\n const stopScheduling = useCallback(() => {\n stopFrameLoop();\n clearWakeTimer();\n }, [clearWakeTimer, stopFrameLoop]);\n\n const startFrameLoopRef = useRef<() => void>(() => {});\n\n const scheduleFrameWake = useCallback(\n (delayMs: number) => {\n clearWakeTimer();\n\n wakeTimerRef.current = setTimeout(\n () => {\n wakeTimerRef.current = null;\n startFrameLoopRef.current();\n },\n Math.max(1, Math.ceil(delayMs)),\n );\n },\n [clearWakeTimer],\n );\n\n const syncImmediate = useCallback(\n (nextContent: string) => {\n stopScheduling();\n\n const chars = [...nextContent];\n const now = getNow();\n\n targetContentRef.current = nextContent;\n targetCharsRef.current = chars;\n targetCountRef.current = chars.length;\n\n displayedContentRef.current = nextContent;\n displayedCountRef.current = chars.length;\n setDisplayedContent(nextContent);\n\n emaCpsRef.current = config.defaultCps;\n chunkSizeEmaRef.current = 1;\n arrivalCpsEmaRef.current = config.defaultCps;\n lastInputTsRef.current = now;\n lastInputCountRef.current = chars.length;\n },\n [config.defaultCps, stopScheduling],\n );\n\n const startFrameLoop = useCallback(() => {\n clearWakeTimer();\n if (rafRef.current !== null) return;\n\n const tick = (ts: number) => {\n const frameStart = getNow();\n\n if (lastFrameTsRef.current === null) {\n lastFrameTsRef.current = ts;\n rafRef.current = requestAnimationFrame(tick);\n return;\n }\n\n const frameIntervalMs = Math.max(0, ts - lastFrameTsRef.current);\n const dtSeconds = Math.max(0.001, Math.min(frameIntervalMs / 1000, 0.05));\n lastFrameTsRef.current = ts;\n\n const targetCount = targetCountRef.current;\n const displayedCount = displayedCountRef.current;\n const backlog = targetCount - displayedCount;\n\n if (backlog <= 0) {\n stopFrameLoop();\n return;\n }\n\n const now = getNow();\n const idleMs = now - lastInputTsRef.current;\n const inputActive = idleMs <= config.activeInputWindowMs;\n const settling = !inputActive && idleMs >= config.settleAfterMs;\n\n const baseCps = clamp(emaCpsRef.current, config.minCps, config.maxCps);\n const baseLagChars = Math.max(1, Math.round((baseCps * config.targetBufferMs) / 1000));\n const lagUpperBound = Math.max(baseLagChars + 2, baseLagChars * 3);\n const targetLagChars = inputActive\n ? Math.round(\n clamp(baseLagChars + chunkSizeEmaRef.current * 0.35, baseLagChars, lagUpperBound),\n )\n : 0;\n const desiredDisplayed = Math.max(0, targetCount - targetLagChars);\n\n let currentCps: number;\n if (inputActive) {\n const backlogPressure = targetLagChars > 0 ? backlog / targetLagChars : 1;\n const chunkPressure = targetLagChars > 0 ? chunkSizeEmaRef.current / targetLagChars : 1;\n const arrivalPressure = arrivalCpsEmaRef.current / Math.max(baseCps, 1);\n const combinedPressure = clamp(\n backlogPressure * 0.6 + chunkPressure * 0.25 + arrivalPressure * 0.15,\n 1,\n 4.5,\n );\n const activeCap = clamp(\n config.maxActiveCps + chunkSizeEmaRef.current * 6,\n config.maxActiveCps,\n config.maxFlushCps,\n );\n currentCps = clamp(baseCps * combinedPressure, config.minCps, activeCap);\n } else if (settling) {\n // If upstream likely ended, cap the remaining tail duration so\n // we do not keep replaying old backlog for seconds.\n const drainTargetMs = clamp(backlog * 8, config.settleDrainMinMs, config.settleDrainMaxMs);\n const settleCps = (backlog * 1000) / drainTargetMs;\n currentCps = clamp(settleCps, config.flushCps, config.maxFlushCps);\n } else {\n const idleFlushCps = Math.max(\n config.flushCps,\n baseCps * 1.8,\n arrivalCpsEmaRef.current * 0.8,\n );\n currentCps = clamp(idleFlushCps, config.flushCps, config.maxFlushCps);\n }\n\n const urgentBacklog = inputActive && targetLagChars > 0 && backlog > targetLagChars * 2.2;\n const burstyInput = inputActive && chunkSizeEmaRef.current >= targetLagChars * 0.9;\n const minRevealChars = inputActive ? (urgentBacklog || burstyInput ? 2 : 1) : 2;\n let revealChars = Math.max(minRevealChars, Math.round(currentCps * dtSeconds));\n\n if (inputActive) {\n const shortfall = desiredDisplayed - displayedCount;\n if (shortfall <= 0) {\n stopFrameLoop();\n scheduleFrameWake(config.activeInputWindowMs - idleMs);\n\n profiler?.recordAnimationFrame({\n backlog,\n durationMs: getNow() - frameStart,\n frameIntervalMs,\n inputActive,\n revealChars: 0,\n settling,\n });\n return;\n }\n revealChars = Math.min(revealChars, shortfall, backlog);\n } else {\n revealChars = Math.min(revealChars, backlog);\n }\n\n const nextCount = displayedCount + revealChars;\n const segment = targetCharsRef.current.slice(displayedCount, nextCount).join('');\n\n if (segment) {\n const nextDisplayed = displayedContentRef.current + segment;\n displayedContentRef.current = nextDisplayed;\n displayedCountRef.current = nextCount;\n setDisplayedContent(nextDisplayed);\n } else {\n displayedContentRef.current = targetContentRef.current;\n displayedCountRef.current = targetCount;\n setDisplayedContent(targetContentRef.current);\n }\n\n profiler?.recordAnimationFrame({\n backlog,\n durationMs: getNow() - frameStart,\n frameIntervalMs,\n inputActive,\n revealChars: segment ? revealChars : backlog,\n settling,\n });\n\n rafRef.current = requestAnimationFrame(tick);\n };\n\n rafRef.current = requestAnimationFrame(tick);\n }, [\n clearWakeTimer,\n config.activeInputWindowMs,\n config.flushCps,\n config.maxActiveCps,\n config.maxCps,\n config.maxFlushCps,\n config.minCps,\n config.settleAfterMs,\n config.settleDrainMaxMs,\n config.settleDrainMinMs,\n config.targetBufferMs,\n scheduleFrameWake,\n stopFrameLoop,\n ]);\n startFrameLoopRef.current = startFrameLoop;\n\n useEffect(() => {\n if (!enabled) {\n syncImmediate(content);\n return;\n }\n\n const prevTargetContent = targetContentRef.current;\n if (content === prevTargetContent) return;\n\n const now = getNow();\n const appendOnly = content.startsWith(prevTargetContent);\n\n if (!appendOnly) {\n syncImmediate(content);\n return;\n }\n\n const appended = content.slice(prevTargetContent.length);\n const appendedChars = [...appended];\n const appendedCount = appendedChars.length;\n\n profiler?.recordInputAppend({\n appendedChars: appendedCount,\n contentLength: countChars(content),\n });\n\n if (appendedCount > config.largeAppendChars) {\n syncImmediate(content);\n return;\n }\n\n targetContentRef.current = content;\n targetCharsRef.current = [...targetCharsRef.current, ...appendedChars];\n targetCountRef.current += appendedCount;\n\n const deltaChars = targetCountRef.current - lastInputCountRef.current;\n const deltaMs = Math.max(1, now - lastInputTsRef.current);\n\n if (deltaChars > 0) {\n const instantCps = (deltaChars * 1000) / deltaMs;\n const normalizedInstantCps = clamp(instantCps, config.minCps, config.maxFlushCps * 2);\n const chunkEmaAlpha = 0.35;\n chunkSizeEmaRef.current =\n chunkSizeEmaRef.current * (1 - chunkEmaAlpha) + appendedCount * chunkEmaAlpha;\n arrivalCpsEmaRef.current =\n arrivalCpsEmaRef.current * (1 - chunkEmaAlpha) + normalizedInstantCps * chunkEmaAlpha;\n\n const clampedCps = clamp(instantCps, config.minCps, config.maxActiveCps);\n emaCpsRef.current = emaCpsRef.current * (1 - config.emaAlpha) + clampedCps * config.emaAlpha;\n }\n\n lastInputTsRef.current = now;\n lastInputCountRef.current = targetCountRef.current;\n\n startFrameLoop();\n }, [\n config.emaAlpha,\n config.largeAppendChars,\n config.maxActiveCps,\n config.maxCps,\n config.maxFlushCps,\n config.minCps,\n content,\n enabled,\n startFrameLoop,\n syncImmediate,\n profiler,\n ]);\n\n useEffect(() => {\n return () => {\n stopScheduling();\n };\n }, [stopScheduling]);\n\n return displayedContent;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.mjs","names":[],"sources":["../../../src/Markdown/components/CodeBlock.tsx"],"sourcesContent":["import { memo } from 'react';\n\nimport type { HighlighterProps } from '@/Highlighter';\nimport { FALLBACK_LANG } from '@/Highlighter/const';\nimport Pre, { PreMermaid, PreSingleLine } from '@/mdx/mdxComponents/Pre';\nimport type { MermaidProps } from '@/Mermaid';\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\nexport const 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 animated?: boolean;\n children: any;\n enableMermaid?: boolean;\n fullFeatured?: boolean;\n highlight?: HighlighterProps;\n mermaid?: MermaidProps;\n}\n\nexport const CodeBlock = memo<CodeBlockProps>(\n ({ fullFeatured, enableMermaid, highlight, mermaid, children, animated, ...rest }) => {\n const code = useCode(children);\n\n if (!code) return;\n\n if (enableMermaid && code.lang === 'mermaid')\n return (\n <PreMermaid animated={animated} fullFeatured={fullFeatured} {...mermaid} {...rest}>\n {code.content}\n </PreMermaid>\n );\n\n if (!highlight && code.isSingleLine)\n return <PreSingleLine language={code.lang}>{code.content}</PreSingleLine>;\n\n return (\n <Pre\n animated={animated}\n fullFeatured={fullFeatured}\n language={code.lang}\n {...highlight}\n {...rest}\n >\n {code.content}\n </Pre>\n );\n },\n (prevProps, nextProps) => prevProps.children === nextProps.children,\n);\n"],"mappings":";;;;;AAOA,MAAM,cAAc,QAAwB;CAE1C,MAAM,UAAU,IAAI,
|
|
1
|
+
{"version":3,"file":"CodeBlock.mjs","names":[],"sources":["../../../src/Markdown/components/CodeBlock.tsx"],"sourcesContent":["import { memo } from 'react';\n\nimport type { HighlighterProps } from '@/Highlighter';\nimport { FALLBACK_LANG } from '@/Highlighter/const';\nimport Pre, { PreMermaid, PreSingleLine } from '@/mdx/mdxComponents/Pre';\nimport type { MermaidProps } from '@/Mermaid';\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\nexport const 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 animated?: boolean;\n children: any;\n enableMermaid?: boolean;\n fullFeatured?: boolean;\n highlight?: HighlighterProps;\n mermaid?: MermaidProps;\n}\n\nexport const CodeBlock = memo<CodeBlockProps>(\n ({ fullFeatured, enableMermaid, highlight, mermaid, children, animated, ...rest }) => {\n const code = useCode(children);\n\n if (!code) return;\n\n if (enableMermaid && code.lang === 'mermaid')\n return (\n <PreMermaid animated={animated} fullFeatured={fullFeatured} {...mermaid} {...rest}>\n {code.content}\n </PreMermaid>\n );\n\n if (!highlight && code.isSingleLine)\n return <PreSingleLine language={code.lang}>{code.content}</PreSingleLine>;\n\n return (\n <Pre\n animated={animated}\n fullFeatured={fullFeatured}\n language={code.lang}\n {...highlight}\n {...rest}\n >\n {code.content}\n </Pre>\n );\n },\n (prevProps, nextProps) => prevProps.children === nextProps.children,\n);\n"],"mappings":";;;;;AAOA,MAAM,cAAc,QAAwB;CAE1C,MAAM,UAAU,IAAI,MAAM,MAAM;AAChC,QAAO,UAAU,QAAQ,SAAS;;AAGpC,MAAa,WAAW,QAAa;AACnC,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;;AAYH,MAAa,YAAY,MACtB,EAAE,cAAc,eAAe,WAAW,SAAS,UAAU,UAAU,GAAG,WAAW;CACpF,MAAM,OAAO,QAAQ,SAAS;AAE9B,KAAI,CAAC,KAAM;AAEX,KAAI,iBAAiB,KAAK,SAAS,UACjC,QACE,oBAAC,YAAD;EAAsB;EAAwB;EAAc,GAAI;EAAS,GAAI;YAC1E,KAAK;EACK,CAAA;AAGjB,KAAI,CAAC,aAAa,KAAK,aACrB,QAAO,oBAAC,eAAD;EAAe,UAAU,KAAK;YAAO,KAAK;EAAwB,CAAA;AAE3E,QACE,oBAAC,KAAD;EACY;EACI;EACd,UAAU,KAAK;EACf,GAAI;EACJ,GAAI;YAEH,KAAK;EACF,CAAA;IAGT,WAAW,cAAc,UAAU,aAAa,UAAU,SAC5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.style.mjs","names":[],"sources":["../../src/Markdown/markdown.style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nconst IGNORE_CLASSNAME = '.ignore-markdown-style';\n\nexport const styles = createStaticStyles(({ cssVar, css }) => {\n const __root = css`\n --lobe-markdown-font-size: 16px;\n --lobe-markdown-header-multiple: 1;\n --lobe-markdown-margin-multiple: 2;\n --lobe-markdown-line-height: 1.8;\n --lobe-markdown-border-radius: ${cssVar.borderRadiusLG};\n --lobe-markdown-border-color: ${cssVar.colorFillQuaternary};\n\n position: relative;\n\n width: 100%;\n max-width: 100%;\n padding-inline: 1px;\n\n font-size: var(--lobe-markdown-font-size);\n line-height: var(--lobe-markdown-line-height);\n overflow-wrap: break-word;\n `;\n const a = css`\n a {\n color: ${cssVar.colorInfoText};\n\n &:hover {\n color: ${cssVar.colorInfoHover};\n }\n }\n `;\n\n const blockquote = css`\n blockquote {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n margin-inline: 0;\n padding-block: 0;\n padding-inline: 1em;\n border-inline-start: solid 4px ${cssVar.colorBorder};\n\n color: ${cssVar.colorTextSecondary};\n }\n `;\n\n const code = css`\n code {\n &:not(:has(span)) {\n display: inline;\n\n margin-inline: 0.25em;\n padding-block: 0.2em;\n padding-inline: 0.4em;\n border: 1px solid var(--lobe-markdown-border-color);\n border-radius: 0.25em;\n\n font-family: ${cssVar.fontFamilyCode};\n font-size: 0.875em;\n line-height: 1;\n overflow-wrap: break-word;\n white-space: break-spaces;\n\n background: ${cssVar.colorFillSecondary};\n }\n }\n `;\n\n const del = css`\n del {\n color: ${cssVar.colorTextDescription};\n text-decoration: line-through;\n }\n `;\n\n const details = css`\n details {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n padding-block: 0.75em;\n padding-inline: 1em;\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n\n background: ${cssVar.colorFillTertiary};\n box-shadow: 0 0 0 1px var(--lobe-markdown-border-color);\n\n summary {\n cursor: pointer;\n display: flex;\n align-items: center;\n list-style: none;\n\n &::before {\n content: '';\n\n position: absolute;\n inset-inline-end: 1.25em;\n transform: rotateZ(-45deg);\n\n display: block;\n\n width: 0.4em;\n height: 0.4em;\n border-block-end: 1.5px solid ${cssVar.colorTextSecondary};\n border-inline-end: 1.5px solid ${cssVar.colorTextSecondary};\n\n font-family: ${cssVar.fontFamily};\n\n transition: transform 200ms ${cssVar.motionEaseOut};\n }\n }\n\n &[open] {\n summary {\n padding-block-end: 0.75em;\n border-block-end: 1px dashed ${cssVar.colorBorder};\n\n &::before {\n transform: rotateZ(45deg);\n }\n }\n }\n }\n `;\n const header = css`\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n margin-block: max(\n calc(var(--lobe-markdown-header-multiple) * var(--lobe-markdown-margin-multiple) * 0.4em),\n var(--lobe-markdown-font-size)\n );\n font-weight: bold;\n line-height: 1.25;\n }\n\n h1 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 1.5 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h2 {\n font-size: calc(var(--lobe-markdown-font-size) * (1 + var(--lobe-markdown-header-multiple)));\n }\n\n h3 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 0.5 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h4 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 0.25 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h5,\n h6 {\n font-size: calc(var(--lobe-markdown-font-size) * 1);\n }\n `;\n const hr = css`\n hr {\n width: 100%;\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 1.5em);\n border-color: ${cssVar.colorBorder};\n border-style: dashed;\n border-width: 1px;\n border-block-start: none;\n border-inline-start: none;\n border-inline-end: none;\n }\n `;\n const img = css`\n img {\n max-width: 100%;\n }\n\n > img,\n > p > img {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n box-shadow: 0 0 0 1px var(--lobe-markdown-border-color);\n }\n `;\n\n const list = css`\n li {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.33em);\n\n p:first-child {\n display: inline;\n }\n }\n\n ul,\n ol {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n margin-inline-start: 1em;\n padding-inline-start: 0;\n list-style-position: outside;\n\n > ul,\n > ol {\n margin-block: 0;\n }\n\n > li {\n margin-inline-start: 1em;\n }\n }\n\n ol {\n list-style: auto;\n }\n\n ul {\n list-style-type: none;\n\n > li {\n &::before {\n content: '-';\n\n position: absolute;\n\n display: inline-block;\n\n margin-inline: -1em 0.5em;\n\n opacity: 0.5;\n }\n }\n }\n\n .task-list-item {\n &::before {\n display: none !important;\n }\n\n input[type='checkbox'] {\n margin-block: 0 0.25em;\n margin-inline: -1.6em 0.2em;\n vertical-align: middle;\n }\n\n input[type='checkbox']:dir(rtl) {\n margin: 0 -1.6em 0.25em 0.2em;\n }\n }\n `;\n const p = css`\n p {\n margin-block: 4px;\n line-height: var(--lobe-markdown-line-height);\n letter-spacing: 0.02em;\n\n &:not(:first-child) {\n margin-block-start: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n }\n\n &:not(:last-child) {\n margin-block-end: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n }\n }\n `;\n const pre = css`\n pre {\n font-size: calc(var(--lobe-markdown-font-size) * 0.85);\n }\n `;\n const strong = css`\n strong {\n font-weight: 600;\n }\n `;\n const svg = css`\n svg {\n line-height: 1;\n }\n `;\n const table = css`\n table {\n unicode-bidi: isolate;\n overflow: auto hidden;\n display: block;\n border-spacing: 0;\n border-collapse: collapse;\n\n box-sizing: border-box;\n width: max-content;\n max-width: 100%;\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n\n text-align: start;\n text-indent: initial;\n text-wrap: pretty;\n word-break: auto-phrase;\n overflow-wrap: break-word;\n\n box-shadow: 0 0 0 1px ${cssVar.colorBorderSecondary};\n\n code {\n overflow-wrap: break-word;\n }\n\n thead {\n background: ${cssVar.colorFillQuaternary};\n }\n\n tr {\n box-shadow: 0 1px 0 ${cssVar.colorBorderSecondary};\n }\n\n th,\n td {\n min-width: 120px;\n padding-block: 0.75em;\n padding-inline: 1em;\n text-align: start;\n }\n }\n `;\n const video = css`\n > video,\n > p > video {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n box-shadow: 0 0 0 1px var(--lobe-markdown-border-color);\n }\n\n video {\n max-width: 100%;\n }\n `;\n\n const footnote = css`\n .footnotes {\n margin-block-start: calc(var(--lobe-markdown-margin-multiple) * 1em);\n font-size: smaller;\n color: #8b949e;\n\n #footnote-label {\n display: none;\n }\n\n > ol {\n margin: 0 !important;\n }\n }\n `;\n\n const sup = css`\n sup {\n position: relative;\n inset-block-start: -0.25em;\n\n font-size: 0.75em;\n line-height: var(--lobe-markdown-line-height);\n vertical-align: baseline;\n }\n `;\n\n const sub = css`\n sub {\n position: relative;\n inset-block-end: -0.25em;\n\n font-size: 0.75em;\n line-height: var(--lobe-markdown-line-height);\n vertical-align: baseline;\n }\n `;\n\n return {\n root: css`\n :not(:has(${IGNORE_CLASSNAME})),\n .markdown {\n ${[\n __root,\n a,\n blockquote,\n code,\n del,\n details,\n header,\n hr,\n img,\n list,\n p,\n pre,\n strong,\n svg,\n table,\n video,\n footnote,\n sub,\n sup,\n ]}\n }\n `,\n };\n});\n"],"mappings":";;AAEA,MAAM,mBAAmB;AAEzB,MAAa,SAAS,oBAAoB,EAAE,QAAQ,UAAU;CAC5D,MAAM,SAAS,GAAG;;;;;qCAKiB,OAAO,eAAe;oCACvB,OAAO,oBAAoB;;;;;;;;;;;;CAY7D,MAAM,IAAI,GAAG;;eAEA,OAAO,cAAc;;;iBAGnB,OAAO,eAAe;;;;CAKrC,MAAM,aAAa,GAAG;;;;;;uCAMe,OAAO,YAAY;;eAE3C,OAAO,mBAAmB;;;CAIvC,MAAM,OAAO,GAAG;;;;;;;;;;;uBAWK,OAAO,eAAe;;;;;;sBAMvB,OAAO,mBAAmB;;;;CAK9C,MAAM,MAAM,GAAG;;eAEF,OAAO,qBAAqB;;;;CAKzC,MAAM,UAAU,GAAG;;;;;;;oBAOD,OAAO,kBAAkB;;;;;;;;;;;;;;;;;;;;0CAoBH,OAAO,mBAAmB;2CACzB,OAAO,mBAAmB;;yBAE5C,OAAO,WAAW;;wCAEH,OAAO,cAAc;;;;;;;yCAOpB,OAAO,YAAY;;;;;;;;;CAS1D,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ClB,MAAM,KAAK,GAAG;;;;sBAIM,OAAO,YAAY;;;;;;;;CAQvC,MAAM,MAAM,GAAG;;;;;;;;;;;;CAaf,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEhB,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;CAeb,MAAM,MAAM,GAAG;;;;;CAKf,MAAM,SAAS,GAAG;;;;;CAKlB,MAAM,MAAM,GAAG;;;;;CAKf,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;8BAoBW,OAAO,qBAAqB;;;;;;;sBAOpC,OAAO,oBAAoB;;;;8BAInB,OAAO,qBAAqB;;;;;;;;;;;;CAYxD,MAAM,QAAQ,GAAG;;;;;;;;;;;;CAajB,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;CAgBpB,MAAM,MAAM,GAAG;;;;;;;;;;AAsBf,QAAO,EACL,MAAM,GAAG;kBACK,iBAAiB;;UAEzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAhCI,GAAG;;;;;;;;;;EAkCP;EACD,CAAC;;OAGP;EACD"}
|
|
1
|
+
{"version":3,"file":"markdown.style.mjs","names":[],"sources":["../../src/Markdown/markdown.style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nconst IGNORE_CLASSNAME = '.ignore-markdown-style';\n\nexport const styles = createStaticStyles(({ cssVar, css }) => {\n const __root = css`\n --lobe-markdown-font-size: 16px;\n --lobe-markdown-header-multiple: 1;\n --lobe-markdown-margin-multiple: 2;\n --lobe-markdown-line-height: 1.8;\n --lobe-markdown-border-radius: ${cssVar.borderRadiusLG};\n --lobe-markdown-border-color: ${cssVar.colorFillQuaternary};\n\n position: relative;\n\n width: 100%;\n max-width: 100%;\n padding-inline: 1px;\n\n font-size: var(--lobe-markdown-font-size);\n line-height: var(--lobe-markdown-line-height);\n overflow-wrap: break-word;\n `;\n const a = css`\n a {\n color: ${cssVar.colorInfoText};\n\n &:hover {\n color: ${cssVar.colorInfoHover};\n }\n }\n `;\n\n const blockquote = css`\n blockquote {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n margin-inline: 0;\n padding-block: 0;\n padding-inline: 1em;\n border-inline-start: solid 4px ${cssVar.colorBorder};\n\n color: ${cssVar.colorTextSecondary};\n }\n `;\n\n const code = css`\n code {\n &:not(:has(span)) {\n display: inline;\n\n margin-inline: 0.25em;\n padding-block: 0.2em;\n padding-inline: 0.4em;\n border: 1px solid var(--lobe-markdown-border-color);\n border-radius: 0.25em;\n\n font-family: ${cssVar.fontFamilyCode};\n font-size: 0.875em;\n line-height: 1;\n overflow-wrap: break-word;\n white-space: break-spaces;\n\n background: ${cssVar.colorFillSecondary};\n }\n }\n `;\n\n const del = css`\n del {\n color: ${cssVar.colorTextDescription};\n text-decoration: line-through;\n }\n `;\n\n const details = css`\n details {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n padding-block: 0.75em;\n padding-inline: 1em;\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n\n background: ${cssVar.colorFillTertiary};\n box-shadow: 0 0 0 1px var(--lobe-markdown-border-color);\n\n summary {\n cursor: pointer;\n display: flex;\n align-items: center;\n list-style: none;\n\n &::before {\n content: '';\n\n position: absolute;\n inset-inline-end: 1.25em;\n transform: rotateZ(-45deg);\n\n display: block;\n\n width: 0.4em;\n height: 0.4em;\n border-block-end: 1.5px solid ${cssVar.colorTextSecondary};\n border-inline-end: 1.5px solid ${cssVar.colorTextSecondary};\n\n font-family: ${cssVar.fontFamily};\n\n transition: transform 200ms ${cssVar.motionEaseOut};\n }\n }\n\n &[open] {\n summary {\n padding-block-end: 0.75em;\n border-block-end: 1px dashed ${cssVar.colorBorder};\n\n &::before {\n transform: rotateZ(45deg);\n }\n }\n }\n }\n `;\n const header = css`\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n margin-block: max(\n calc(var(--lobe-markdown-header-multiple) * var(--lobe-markdown-margin-multiple) * 0.4em),\n var(--lobe-markdown-font-size)\n );\n font-weight: bold;\n line-height: 1.25;\n }\n\n h1 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 1.5 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h2 {\n font-size: calc(var(--lobe-markdown-font-size) * (1 + var(--lobe-markdown-header-multiple)));\n }\n\n h3 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 0.5 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h4 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 0.25 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h5,\n h6 {\n font-size: calc(var(--lobe-markdown-font-size) * 1);\n }\n `;\n const hr = css`\n hr {\n width: 100%;\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 1.5em);\n border-color: ${cssVar.colorBorder};\n border-style: dashed;\n border-width: 1px;\n border-block-start: none;\n border-inline-start: none;\n border-inline-end: none;\n }\n `;\n const img = css`\n img {\n max-width: 100%;\n }\n\n > img,\n > p > img {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n box-shadow: 0 0 0 1px var(--lobe-markdown-border-color);\n }\n `;\n\n const list = css`\n li {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.33em);\n\n p:first-child {\n display: inline;\n }\n }\n\n ul,\n ol {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n margin-inline-start: 1em;\n padding-inline-start: 0;\n list-style-position: outside;\n\n > ul,\n > ol {\n margin-block: 0;\n }\n\n > li {\n margin-inline-start: 1em;\n }\n }\n\n ol {\n list-style: auto;\n }\n\n ul {\n list-style-type: none;\n\n > li {\n &::before {\n content: '-';\n\n position: absolute;\n\n display: inline-block;\n\n margin-inline: -1em 0.5em;\n\n opacity: 0.5;\n }\n }\n }\n\n .task-list-item {\n &::before {\n display: none !important;\n }\n\n input[type='checkbox'] {\n margin-block: 0 0.25em;\n margin-inline: -1.6em 0.2em;\n vertical-align: middle;\n }\n\n input[type='checkbox']:dir(rtl) {\n margin: 0 -1.6em 0.25em 0.2em;\n }\n }\n `;\n const p = css`\n p {\n margin-block: 4px;\n line-height: var(--lobe-markdown-line-height);\n letter-spacing: 0.02em;\n\n &:not(:first-child) {\n margin-block-start: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n }\n\n &:not(:last-child) {\n margin-block-end: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n }\n }\n `;\n const pre = css`\n pre {\n font-size: calc(var(--lobe-markdown-font-size) * 0.85);\n }\n `;\n const strong = css`\n strong {\n font-weight: 600;\n }\n `;\n const svg = css`\n svg {\n line-height: 1;\n }\n `;\n const table = css`\n table {\n unicode-bidi: isolate;\n overflow: auto hidden;\n display: block;\n border-spacing: 0;\n border-collapse: collapse;\n\n box-sizing: border-box;\n width: max-content;\n max-width: 100%;\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n\n text-align: start;\n text-indent: initial;\n text-wrap: pretty;\n word-break: auto-phrase;\n overflow-wrap: break-word;\n\n box-shadow: 0 0 0 1px ${cssVar.colorBorderSecondary};\n\n code {\n overflow-wrap: break-word;\n }\n\n thead {\n background: ${cssVar.colorFillQuaternary};\n }\n\n tr {\n box-shadow: 0 1px 0 ${cssVar.colorBorderSecondary};\n }\n\n th,\n td {\n min-width: 120px;\n padding-block: 0.75em;\n padding-inline: 1em;\n text-align: start;\n }\n }\n `;\n const video = css`\n > video,\n > p > video {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n border-radius: calc(var(--lobe-markdown-border-radius) * 1px);\n box-shadow: 0 0 0 1px var(--lobe-markdown-border-color);\n }\n\n video {\n max-width: 100%;\n }\n `;\n\n const footnote = css`\n .footnotes {\n margin-block-start: calc(var(--lobe-markdown-margin-multiple) * 1em);\n font-size: smaller;\n color: #8b949e;\n\n #footnote-label {\n display: none;\n }\n\n > ol {\n margin: 0 !important;\n }\n }\n `;\n\n const sup = css`\n sup {\n position: relative;\n inset-block-start: -0.25em;\n\n font-size: 0.75em;\n line-height: var(--lobe-markdown-line-height);\n vertical-align: baseline;\n }\n `;\n\n const sub = css`\n sub {\n position: relative;\n inset-block-end: -0.25em;\n\n font-size: 0.75em;\n line-height: var(--lobe-markdown-line-height);\n vertical-align: baseline;\n }\n `;\n\n return {\n root: css`\n :not(:has(${IGNORE_CLASSNAME})),\n .markdown {\n ${[\n __root,\n a,\n blockquote,\n code,\n del,\n details,\n header,\n hr,\n img,\n list,\n p,\n pre,\n strong,\n svg,\n table,\n video,\n footnote,\n sub,\n sup,\n ]}\n }\n `,\n };\n});\n"],"mappings":";;AAEA,MAAM,mBAAmB;AAEzB,MAAa,SAAS,oBAAoB,EAAE,QAAQ,UAAU;CAC5D,MAAM,SAAS,GAAG;;;;;qCAKiB,OAAO,eAAe;oCACvB,OAAO,oBAAoB;;;;;;;;;;;;CAY7D,MAAM,IAAI,GAAG;;eAEA,OAAO,cAAc;;;iBAGnB,OAAO,eAAe;;;;CAKrC,MAAM,aAAa,GAAG;;;;;;uCAMe,OAAO,YAAY;;eAE3C,OAAO,mBAAmB;;;CAIvC,MAAM,OAAO,GAAG;;;;;;;;;;;uBAWK,OAAO,eAAe;;;;;;sBAMvB,OAAO,mBAAmB;;;;CAK9C,MAAM,MAAM,GAAG;;eAEF,OAAO,qBAAqB;;;;CAKzC,MAAM,UAAU,GAAG;;;;;;;oBAOD,OAAO,kBAAkB;;;;;;;;;;;;;;;;;;;;0CAoBH,OAAO,mBAAmB;2CACzB,OAAO,mBAAmB;;yBAE5C,OAAO,WAAW;;wCAEH,OAAO,cAAc;;;;;;;yCAOpB,OAAO,YAAY;;;;;;;;;CAS1D,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ClB,MAAM,KAAK,GAAG;;;;sBAIM,OAAO,YAAY;;;;;;;;CAQvC,MAAM,MAAM,GAAG;;;;;;;;;;;;CAaf,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEhB,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;CAeb,MAAM,MAAM,GAAG;;;;;CAKf,MAAM,SAAS,GAAG;;;;;CAKlB,MAAM,MAAM,GAAG;;;;;CAKf,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;8BAoBW,OAAO,qBAAqB;;;;;;;sBAOpC,OAAO,oBAAoB;;;;8BAInB,OAAO,qBAAqB;;;;;;;;;;;;CAYxD,MAAM,QAAQ,GAAG;;;;;;;;;;;;CAajB,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;CAgBpB,MAAM,MAAM,GAAG;;;;;;;;;;AAsBf,QAAO,EACL,MAAM,GAAG;kBACK,iBAAiB;;UAEzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAjCO;;;;;;;;;;EAkCP;EACD,CAAC;;OAGP;EACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remarkBr.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkBr.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\n/**\n * Remark plugin to handle <br> and <br/> tags in markdown text\n * This plugin converts <br> and <br/> tags to proper HTML elements\n * without requiring allowHtml to be enabled\n */\nexport const remarkBr = () => {\n return (tree: any) => {\n // First try to process html nodes that might contain ONLY br tags\n visit(tree, 'html', (node, index, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // Only handle standalone br tags, not complex HTML containing br tags\n const brRegex = /^\\s*<\\s*br\\s*\\/?>\\s*$/gi;\n if (brRegex.test(node.value)) {\n // Replace the html node with a break node\n parent.children.splice(index, 1, { type: 'break' });\n return index;\n }\n });\n\n // Also process text nodes\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // Check if the text contains <br> or <br/> tags\n const brRegex = /<\\s*br\\s*\\/?>/gi;\n\n if (!brRegex.test(node.value)) return;\n\n // Reset regex lastIndex for split operation\n brRegex.lastIndex = 0;\n\n // Split the text by br tags, but keep the matched tags\n const parts: string[] = [];\n const matches: string[] = [];\n let lastIndex = 0;\n let match;\n\n while ((match = brRegex.exec(node.value)) !== null) {\n // Add text before the match\n if (match.index > lastIndex) {\n parts.push(node.value.slice(lastIndex, match.index));\n }\n\n // Store the matched br tag\n matches.push(match[0]);\n lastIndex = match.index + match[0].length;\n }\n\n // Add remaining text after the last match\n if (lastIndex < node.value.length) {\n parts.push(node.value.slice(lastIndex));\n }\n\n // Create new nodes\n const newNodes: any[] = [];\n\n for (const [i, part] of parts.entries()) {\n // Add text node if not empty\n if (part) {\n newNodes.push({\n type: 'text',\n value: part,\n });\n }\n\n // Add br element if we have a corresponding match\n if (i < matches.length) {\n newNodes.push({\n type: 'break',\n });\n }\n }\n\n // Replace the original text node with the new nodes\n if (newNodes.length > 0) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1; // Skip the newly added nodes\n }\n });\n };\n};\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;AAC5B,SAAQ,SAAc;AAEpB,QAAM,MAAM,SAAS,MAAM,OAAO,WAAW;AAC3C,OAAI,CAAC,KAAK,SAAS,OAAO,KAAK,UAAU,SAAU;AAInD,
|
|
1
|
+
{"version":3,"file":"remarkBr.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkBr.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\n/**\n * Remark plugin to handle <br> and <br/> tags in markdown text\n * This plugin converts <br> and <br/> tags to proper HTML elements\n * without requiring allowHtml to be enabled\n */\nexport const remarkBr = () => {\n return (tree: any) => {\n // First try to process html nodes that might contain ONLY br tags\n visit(tree, 'html', (node, index, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // Only handle standalone br tags, not complex HTML containing br tags\n const brRegex = /^\\s*<\\s*br\\s*\\/?>\\s*$/gi;\n if (brRegex.test(node.value)) {\n // Replace the html node with a break node\n parent.children.splice(index, 1, { type: 'break' });\n return index;\n }\n });\n\n // Also process text nodes\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // Check if the text contains <br> or <br/> tags\n const brRegex = /<\\s*br\\s*\\/?>/gi;\n\n if (!brRegex.test(node.value)) return;\n\n // Reset regex lastIndex for split operation\n brRegex.lastIndex = 0;\n\n // Split the text by br tags, but keep the matched tags\n const parts: string[] = [];\n const matches: string[] = [];\n let lastIndex = 0;\n let match;\n\n while ((match = brRegex.exec(node.value)) !== null) {\n // Add text before the match\n if (match.index > lastIndex) {\n parts.push(node.value.slice(lastIndex, match.index));\n }\n\n // Store the matched br tag\n matches.push(match[0]);\n lastIndex = match.index + match[0].length;\n }\n\n // Add remaining text after the last match\n if (lastIndex < node.value.length) {\n parts.push(node.value.slice(lastIndex));\n }\n\n // Create new nodes\n const newNodes: any[] = [];\n\n for (const [i, part] of parts.entries()) {\n // Add text node if not empty\n if (part) {\n newNodes.push({\n type: 'text',\n value: part,\n });\n }\n\n // Add br element if we have a corresponding match\n if (i < matches.length) {\n newNodes.push({\n type: 'break',\n });\n }\n }\n\n // Replace the original text node with the new nodes\n if (newNodes.length > 0) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1; // Skip the newly added nodes\n }\n });\n };\n};\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;AAC5B,SAAQ,SAAc;AAEpB,QAAM,MAAM,SAAS,MAAM,OAAO,WAAW;AAC3C,OAAI,CAAC,KAAK,SAAS,OAAO,KAAK,UAAU,SAAU;AAInD,OAAI,0BAAQ,KAAK,KAAK,MAAM,EAAE;AAE5B,WAAO,SAAS,OAAO,OAAO,GAAG,EAAE,MAAM,SAAS,CAAC;AACnD,WAAO;;IAET;AAGF,QAAM,MAAM,SAAS,MAAM,QAAQ,GAAG,WAAW;AAC/C,OAAI,CAAC,KAAK,SAAS,OAAO,KAAK,UAAU,SAAU;GAGnD,MAAM,UAAU;AAEhB,OAAI,CAAC,QAAQ,KAAK,KAAK,MAAM,CAAE;AAG/B,WAAQ,YAAY;GAGpB,MAAM,QAAkB,EAAE;GAC1B,MAAM,UAAoB,EAAE;GAC5B,IAAI,YAAY;GAChB,IAAI;AAEJ,WAAQ,QAAQ,QAAQ,KAAK,KAAK,MAAM,MAAM,MAAM;AAElD,QAAI,MAAM,QAAQ,UAChB,OAAM,KAAK,KAAK,MAAM,MAAM,WAAW,MAAM,MAAM,CAAC;AAItD,YAAQ,KAAK,MAAM,GAAG;AACtB,gBAAY,MAAM,QAAQ,MAAM,GAAG;;AAIrC,OAAI,YAAY,KAAK,MAAM,OACzB,OAAM,KAAK,KAAK,MAAM,MAAM,UAAU,CAAC;GAIzC,MAAM,WAAkB,EAAE;AAE1B,QAAK,MAAM,CAAC,GAAG,SAAS,MAAM,SAAS,EAAE;AAEvC,QAAI,KACF,UAAS,KAAK;KACZ,MAAM;KACN,OAAO;KACR,CAAC;AAIJ,QAAI,IAAI,QAAQ,OACd,UAAS,KAAK,EACZ,MAAM,SACP,CAAC;;AAKN,OAAI,SAAS,SAAS,GAAG;AACvB,WAAO,SAAS,OAAO,OAAO,GAAG,GAAG,SAAS;AAC7C,WAAO,QAAQ,SAAS,SAAS;;IAEnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remarkColor.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkColor.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\ninterface RemarkColorOptions {\n /**\n * 自定义颜色验证函数\n */\n colorValidator?: (colorString: string) => boolean;\n}\n\n/**\n * Remark plugin to handle color syntax in markdown code spans\n * Supports GitHub-style color visualization for HEX, RGB, and HSL colors\n *\n * @example\n * `#FF0000` -> renders with red color preview\n * `rgb(255, 0, 0)` -> renders with red color preview\n * `hsl(0, 100%, 50%)` -> renders with red color preview\n */\nexport const remarkColor = (options: RemarkColorOptions = {}) => {\n const { colorValidator } = options;\n\n /**\n * 验证并标准化颜色值\n */\n const validateAndNormalizeColor = (colorString: string): string | null => {\n const trimmed = colorString.trim();\n\n // 如果有自定义验证函数,使用它\n if (colorValidator && !colorValidator(trimmed)) {\n return null;\n }\n\n // HEX 颜色: #RRGGBB 或 #RGB\n const hexPattern = /^#([\\da-f]{6}|[\\da-f]{3})$/i;\n if (hexPattern.test(trimmed)) {\n // 标准化为 6 位 HEX\n if (trimmed.length === 4) {\n const [, r, g, b] = trimmed;\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return trimmed.toUpperCase();\n }\n\n // RGB 颜色: rgb(r, g, b)\n const rgbPattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgbMatch = trimmed.match(rgbPattern);\n if (rgbMatch) {\n const [, r, g, b] = rgbMatch;\n const rNum = parseInt(r, 10);\n const gNum = parseInt(g, 10);\n const bNum = parseInt(b, 10);\n\n // 验证 RGB 值范围\n if (rNum >= 0 && rNum <= 255 && gNum >= 0 && gNum <= 255 && bNum >= 0 && bNum <= 255) {\n return `rgb(${rNum}, ${gNum}, ${bNum})`;\n }\n }\n\n // HSL 颜色: hsl(h, s%, l%)\n const hslPattern = /^hsl\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)%\\s*,\\s*(\\d+)%\\s*\\)$/i;\n const hslMatch = trimmed.match(hslPattern);\n if (hslMatch) {\n const [, h, s, l] = hslMatch;\n const hNum = parseInt(h, 10);\n const sNum = parseInt(s, 10);\n const lNum = parseInt(l, 10);\n\n // 验证 HSL 值范围\n if (hNum >= 0 && hNum <= 360 && sNum >= 0 && sNum <= 100 && lNum >= 0 && lNum <= 100) {\n return `hsl(${hNum}, ${sNum}%, ${lNum}%)`;\n }\n }\n\n return null;\n };\n\n return (tree: any) => {\n // 处理 inlineCode 节点(反引号包围的代码)\n visit(tree, 'inlineCode', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n const colorValue = validateAndNormalizeColor(node.value);\n\n if (colorValue) {\n // 创建自定义颜色节点\n const colorNode = {\n children: [{ type: 'text', value: node.value }],\n color: colorValue,\n data: {\n hName: 'code',\n hProperties: {\n 'className': 'color-preview',\n 'data-color': colorValue,\n 'data-original': node.value,\n 'style': `--color-preview-color: ${colorValue}`,\n },\n },\n type: 'colorPreview',\n value: node.value,\n };\n\n // 替换 inlineCode 节点\n parent.children.splice(index, 1, colorNode);\n return index;\n }\n });\n\n // 处理文本节点中的颜色语法(作为备用,处理可能的边缘情况)\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // 查找反引号包围的颜色值\n const colorPattern = /`([^`]+)`/g;\n const text = node.value;\n\n let hasColorMatch = false;\n const newNodes = [];\n let lastIndex = 0;\n let match;\n\n while ((match = colorPattern.exec(text)) !== null) {\n const [fullMatch, colorCandidate] = match;\n const colorValue = validateAndNormalizeColor(colorCandidate);\n\n if (colorValue) {\n hasColorMatch = true;\n const startIndex = match.index;\n\n // 添加匹配前的文本\n if (startIndex > lastIndex) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex, startIndex),\n });\n }\n\n // 添加颜色节点\n newNodes.push({\n children: [{ type: 'text', value: colorCandidate }],\n color: colorValue,\n data: {\n hName: 'code',\n hProperties: {\n 'className': 'color-preview',\n 'data-color': colorValue,\n 'data-original': colorCandidate,\n 'style': `--color-preview-color: ${colorValue}`,\n },\n },\n type: 'colorPreview',\n value: colorCandidate,\n });\n\n lastIndex = startIndex + fullMatch.length;\n }\n }\n\n if (hasColorMatch) {\n // 添加剩余文本\n if (lastIndex < text.length) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex),\n });\n }\n\n // 替换当前节点\n if (newNodes.length > 0 && parent) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1;\n }\n }\n });\n };\n};\n"],"mappings":";;;;;;;;;;;AAkBA,MAAa,eAAe,UAA8B,EAAE,KAAK;CAC/D,MAAM,EAAE,mBAAmB;;;;CAK3B,MAAM,6BAA6B,gBAAuC;EACxE,MAAM,UAAU,YAAY,MAAM;AAGlC,MAAI,kBAAkB,CAAC,eAAe,QAAQ,CAC5C,QAAO;AAKT,
|
|
1
|
+
{"version":3,"file":"remarkColor.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkColor.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\ninterface RemarkColorOptions {\n /**\n * 自定义颜色验证函数\n */\n colorValidator?: (colorString: string) => boolean;\n}\n\n/**\n * Remark plugin to handle color syntax in markdown code spans\n * Supports GitHub-style color visualization for HEX, RGB, and HSL colors\n *\n * @example\n * `#FF0000` -> renders with red color preview\n * `rgb(255, 0, 0)` -> renders with red color preview\n * `hsl(0, 100%, 50%)` -> renders with red color preview\n */\nexport const remarkColor = (options: RemarkColorOptions = {}) => {\n const { colorValidator } = options;\n\n /**\n * 验证并标准化颜色值\n */\n const validateAndNormalizeColor = (colorString: string): string | null => {\n const trimmed = colorString.trim();\n\n // 如果有自定义验证函数,使用它\n if (colorValidator && !colorValidator(trimmed)) {\n return null;\n }\n\n // HEX 颜色: #RRGGBB 或 #RGB\n const hexPattern = /^#([\\da-f]{6}|[\\da-f]{3})$/i;\n if (hexPattern.test(trimmed)) {\n // 标准化为 6 位 HEX\n if (trimmed.length === 4) {\n const [, r, g, b] = trimmed;\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return trimmed.toUpperCase();\n }\n\n // RGB 颜色: rgb(r, g, b)\n const rgbPattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgbMatch = trimmed.match(rgbPattern);\n if (rgbMatch) {\n const [, r, g, b] = rgbMatch;\n const rNum = parseInt(r, 10);\n const gNum = parseInt(g, 10);\n const bNum = parseInt(b, 10);\n\n // 验证 RGB 值范围\n if (rNum >= 0 && rNum <= 255 && gNum >= 0 && gNum <= 255 && bNum >= 0 && bNum <= 255) {\n return `rgb(${rNum}, ${gNum}, ${bNum})`;\n }\n }\n\n // HSL 颜色: hsl(h, s%, l%)\n const hslPattern = /^hsl\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)%\\s*,\\s*(\\d+)%\\s*\\)$/i;\n const hslMatch = trimmed.match(hslPattern);\n if (hslMatch) {\n const [, h, s, l] = hslMatch;\n const hNum = parseInt(h, 10);\n const sNum = parseInt(s, 10);\n const lNum = parseInt(l, 10);\n\n // 验证 HSL 值范围\n if (hNum >= 0 && hNum <= 360 && sNum >= 0 && sNum <= 100 && lNum >= 0 && lNum <= 100) {\n return `hsl(${hNum}, ${sNum}%, ${lNum}%)`;\n }\n }\n\n return null;\n };\n\n return (tree: any) => {\n // 处理 inlineCode 节点(反引号包围的代码)\n visit(tree, 'inlineCode', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n const colorValue = validateAndNormalizeColor(node.value);\n\n if (colorValue) {\n // 创建自定义颜色节点\n const colorNode = {\n children: [{ type: 'text', value: node.value }],\n color: colorValue,\n data: {\n hName: 'code',\n hProperties: {\n 'className': 'color-preview',\n 'data-color': colorValue,\n 'data-original': node.value,\n 'style': `--color-preview-color: ${colorValue}`,\n },\n },\n type: 'colorPreview',\n value: node.value,\n };\n\n // 替换 inlineCode 节点\n parent.children.splice(index, 1, colorNode);\n return index;\n }\n });\n\n // 处理文本节点中的颜色语法(作为备用,处理可能的边缘情况)\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // 查找反引号包围的颜色值\n const colorPattern = /`([^`]+)`/g;\n const text = node.value;\n\n let hasColorMatch = false;\n const newNodes = [];\n let lastIndex = 0;\n let match;\n\n while ((match = colorPattern.exec(text)) !== null) {\n const [fullMatch, colorCandidate] = match;\n const colorValue = validateAndNormalizeColor(colorCandidate);\n\n if (colorValue) {\n hasColorMatch = true;\n const startIndex = match.index;\n\n // 添加匹配前的文本\n if (startIndex > lastIndex) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex, startIndex),\n });\n }\n\n // 添加颜色节点\n newNodes.push({\n children: [{ type: 'text', value: colorCandidate }],\n color: colorValue,\n data: {\n hName: 'code',\n hProperties: {\n 'className': 'color-preview',\n 'data-color': colorValue,\n 'data-original': colorCandidate,\n 'style': `--color-preview-color: ${colorValue}`,\n },\n },\n type: 'colorPreview',\n value: colorCandidate,\n });\n\n lastIndex = startIndex + fullMatch.length;\n }\n }\n\n if (hasColorMatch) {\n // 添加剩余文本\n if (lastIndex < text.length) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex),\n });\n }\n\n // 替换当前节点\n if (newNodes.length > 0 && parent) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1;\n }\n }\n });\n };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remarkGfmPlus.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkGfmPlus.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\ninterface RemarkGfmPlusOptions {\n allowHtmlTags?: string[];\n}\n\nconst DEFAULT_ALLOW_HTML_TAGS = [\n 'sub',\n 'sup',\n 'ins',\n 'kbd',\n 'b',\n 'strong',\n 'i',\n 'em',\n 'mark',\n 'del',\n 'u',\n];\n\nexport const remarkGfmPlus = (options: RemarkGfmPlusOptions = {}) => {\n const { allowHtmlTags = DEFAULT_ALLOW_HTML_TAGS } = options;\n\n return (tree: any) => {\n // 遍历所有父节点,查找分离的HTML标签模式\n visit(tree, (node: any) => {\n if (!node.children || !Array.isArray(node.children)) return;\n\n const children = node.children;\n let i = 0;\n\n while (i < children.length) {\n const currentNode = children[i];\n\n // 查找开始标签\n if (currentNode.type === 'html' && currentNode.value) {\n const tagPattern = `^<(${allowHtmlTags.join('|')})>$`;\n const startTagMatch = currentNode.value.match(new RegExp(tagPattern));\n\n if (startTagMatch) {\n const tagName = startTagMatch[1];\n\n // 查找对应的结束标签\n let endIndex = -1;\n const textNodes: any[] = [];\n\n for (let j = i + 1; j < children.length; j++) {\n const nextNode = children[j];\n\n if (nextNode.type === 'html' && nextNode.value === `</${tagName}>`) {\n endIndex = j;\n break;\n } else if (nextNode.type === 'text') {\n textNodes.push(nextNode);\n } else {\n // 如果遇到其他类型的节点,停止查找\n break;\n }\n }\n\n if (endIndex !== -1) {\n // 收集所有文本内容\n const textContent = textNodes.map((node) => node.value).join('');\n\n // 创建新的自定义节点\n const newNode = {\n children: [{ type: 'text', value: textContent }],\n data: {\n hName: tagName,\n hProperties: {},\n },\n type: tagName,\n };\n\n // 替换从开始标签到结束标签的所有节点\n const removeCount = endIndex - i + 1;\n children.splice(i, removeCount, newNode);\n\n // 继续处理下一个节点\n i++;\n continue;\n }\n }\n }\n\n i++;\n }\n });\n\n // 保留对文本节点中完整HTML标签的处理(作为备用)\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // 处理HTML实体编码的标签\n const encodedTagPattern = `<(${allowHtmlTags.join('|')})>(.*?)<\\\\/\\\\1>`;\n const encodedTagRegex = new RegExp(encodedTagPattern, 'gi');\n const text = node.value;\n\n if (!encodedTagRegex.test(text)) return;\n\n // 重置正则表达式的 lastIndex\n encodedTagRegex.lastIndex = 0;\n\n const newNodes = [];\n let lastIndex = 0;\n let match;\n\n while ((match = encodedTagRegex.exec(text)) !== null) {\n const [fullMatch, tagName, content] = match;\n const startIndex = match.index;\n\n // 添加匹配前的文本\n if (startIndex > lastIndex) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex, startIndex),\n });\n }\n\n // 添加特殊标签节点\n newNodes.push({\n children: [{ type: 'text', value: content }],\n data: {\n hName: tagName,\n hProperties: {},\n },\n type: tagName,\n });\n\n lastIndex = startIndex + fullMatch.length;\n }\n\n // 添加剩余文本\n if (lastIndex < text.length) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex),\n });\n }\n\n // 替换当前节点\n if (newNodes.length > 0 && parent) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1;\n }\n });\n };\n};\n"],"mappings":";;AAMA,MAAM,0BAA0B;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,iBAAiB,UAAgC,EAAE,KAAK;CACnE,MAAM,EAAE,gBAAgB,4BAA4B;AAEpD,SAAQ,SAAc;AAEpB,QAAM,OAAO,SAAc;AACzB,OAAI,CAAC,KAAK,YAAY,CAAC,MAAM,QAAQ,KAAK,SAAS,CAAE;GAErD,MAAM,WAAW,KAAK;GACtB,IAAI,IAAI;AAER,UAAO,IAAI,SAAS,QAAQ;IAC1B,MAAM,cAAc,SAAS;AAG7B,QAAI,YAAY,SAAS,UAAU,YAAY,OAAO;KACpD,MAAM,aAAa,MAAM,cAAc,KAAK,IAAI,CAAC;KACjD,MAAM,gBAAgB,YAAY,MAAM,MAAM,IAAI,OAAO,WAAW,CAAC;AAErE,SAAI,eAAe;MACjB,MAAM,UAAU,cAAc;MAG9B,IAAI,WAAW;MACf,MAAM,YAAmB,EAAE;AAE3B,WAAK,IAAI,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;OAC5C,MAAM,WAAW,SAAS;AAE1B,WAAI,SAAS,SAAS,UAAU,SAAS,UAAU,KAAK,QAAQ,IAAI;AAClE,mBAAW;AACX;kBACS,SAAS,SAAS,OAC3B,WAAU,KAAK,SAAS;WAGxB;;AAIJ,UAAI,aAAa,IAAI;OAKnB,MAAM,UAAU;QACd,UAAU,CAAC;SAAE,MAAM;SAAQ,OAJT,UAAU,KAAK,SAAS,KAAK,MAAM,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"remarkGfmPlus.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkGfmPlus.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\ninterface RemarkGfmPlusOptions {\n allowHtmlTags?: string[];\n}\n\nconst DEFAULT_ALLOW_HTML_TAGS = [\n 'sub',\n 'sup',\n 'ins',\n 'kbd',\n 'b',\n 'strong',\n 'i',\n 'em',\n 'mark',\n 'del',\n 'u',\n];\n\nexport const remarkGfmPlus = (options: RemarkGfmPlusOptions = {}) => {\n const { allowHtmlTags = DEFAULT_ALLOW_HTML_TAGS } = options;\n\n return (tree: any) => {\n // 遍历所有父节点,查找分离的HTML标签模式\n visit(tree, (node: any) => {\n if (!node.children || !Array.isArray(node.children)) return;\n\n const children = node.children;\n let i = 0;\n\n while (i < children.length) {\n const currentNode = children[i];\n\n // 查找开始标签\n if (currentNode.type === 'html' && currentNode.value) {\n const tagPattern = `^<(${allowHtmlTags.join('|')})>$`;\n const startTagMatch = currentNode.value.match(new RegExp(tagPattern));\n\n if (startTagMatch) {\n const tagName = startTagMatch[1];\n\n // 查找对应的结束标签\n let endIndex = -1;\n const textNodes: any[] = [];\n\n for (let j = i + 1; j < children.length; j++) {\n const nextNode = children[j];\n\n if (nextNode.type === 'html' && nextNode.value === `</${tagName}>`) {\n endIndex = j;\n break;\n } else if (nextNode.type === 'text') {\n textNodes.push(nextNode);\n } else {\n // 如果遇到其他类型的节点,停止查找\n break;\n }\n }\n\n if (endIndex !== -1) {\n // 收集所有文本内容\n const textContent = textNodes.map((node) => node.value).join('');\n\n // 创建新的自定义节点\n const newNode = {\n children: [{ type: 'text', value: textContent }],\n data: {\n hName: tagName,\n hProperties: {},\n },\n type: tagName,\n };\n\n // 替换从开始标签到结束标签的所有节点\n const removeCount = endIndex - i + 1;\n children.splice(i, removeCount, newNode);\n\n // 继续处理下一个节点\n i++;\n continue;\n }\n }\n }\n\n i++;\n }\n });\n\n // 保留对文本节点中完整HTML标签的处理(作为备用)\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // 处理HTML实体编码的标签\n const encodedTagPattern = `<(${allowHtmlTags.join('|')})>(.*?)<\\\\/\\\\1>`;\n const encodedTagRegex = new RegExp(encodedTagPattern, 'gi');\n const text = node.value;\n\n if (!encodedTagRegex.test(text)) return;\n\n // 重置正则表达式的 lastIndex\n encodedTagRegex.lastIndex = 0;\n\n const newNodes = [];\n let lastIndex = 0;\n let match;\n\n while ((match = encodedTagRegex.exec(text)) !== null) {\n const [fullMatch, tagName, content] = match;\n const startIndex = match.index;\n\n // 添加匹配前的文本\n if (startIndex > lastIndex) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex, startIndex),\n });\n }\n\n // 添加特殊标签节点\n newNodes.push({\n children: [{ type: 'text', value: content }],\n data: {\n hName: tagName,\n hProperties: {},\n },\n type: tagName,\n });\n\n lastIndex = startIndex + fullMatch.length;\n }\n\n // 添加剩余文本\n if (lastIndex < text.length) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex),\n });\n }\n\n // 替换当前节点\n if (newNodes.length > 0 && parent) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1;\n }\n });\n };\n};\n"],"mappings":";;AAMA,MAAM,0BAA0B;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,iBAAiB,UAAgC,EAAE,KAAK;CACnE,MAAM,EAAE,gBAAgB,4BAA4B;AAEpD,SAAQ,SAAc;AAEpB,QAAM,OAAO,SAAc;AACzB,OAAI,CAAC,KAAK,YAAY,CAAC,MAAM,QAAQ,KAAK,SAAS,CAAE;GAErD,MAAM,WAAW,KAAK;GACtB,IAAI,IAAI;AAER,UAAO,IAAI,SAAS,QAAQ;IAC1B,MAAM,cAAc,SAAS;AAG7B,QAAI,YAAY,SAAS,UAAU,YAAY,OAAO;KACpD,MAAM,aAAa,MAAM,cAAc,KAAK,IAAI,CAAC;KACjD,MAAM,gBAAgB,YAAY,MAAM,MAAM,IAAI,OAAO,WAAW,CAAC;AAErE,SAAI,eAAe;MACjB,MAAM,UAAU,cAAc;MAG9B,IAAI,WAAW;MACf,MAAM,YAAmB,EAAE;AAE3B,WAAK,IAAI,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;OAC5C,MAAM,WAAW,SAAS;AAE1B,WAAI,SAAS,SAAS,UAAU,SAAS,UAAU,KAAK,QAAQ,IAAI;AAClE,mBAAW;AACX;kBACS,SAAS,SAAS,OAC3B,WAAU,KAAK,SAAS;WAGxB;;AAIJ,UAAI,aAAa,IAAI;OAKnB,MAAM,UAAU;QACd,UAAU,CAAC;SAAE,MAAM;SAAQ,OAJT,UAAU,KAAK,SAAS,KAAK,MAAM,CAAC,KAAK,GAId;SAAE,CAAC;QAChD,MAAM;SACJ,OAAO;SACP,aAAa,EAAE;SAChB;QACD,MAAM;QACP;OAGD,MAAM,cAAc,WAAW,IAAI;AACnC,gBAAS,OAAO,GAAG,aAAa,QAAQ;AAGxC;AACA;;;;AAKN;;IAEF;AAGF,QAAM,MAAM,SAAS,MAAM,QAAQ,GAAG,WAAW;AAC/C,OAAI,CAAC,KAAK,SAAS,OAAO,KAAK,UAAU,SAAU;GAGnD,MAAM,oBAAoB,QAAQ,cAAc,KAAK,IAAI,CAAC;GAC1D,MAAM,kBAAkB,IAAI,OAAO,mBAAmB,KAAK;GAC3D,MAAM,OAAO,KAAK;AAElB,OAAI,CAAC,gBAAgB,KAAK,KAAK,CAAE;AAGjC,mBAAgB,YAAY;GAE5B,MAAM,WAAW,EAAE;GACnB,IAAI,YAAY;GAChB,IAAI;AAEJ,WAAQ,QAAQ,gBAAgB,KAAK,KAAK,MAAM,MAAM;IACpD,MAAM,CAAC,WAAW,SAAS,WAAW;IACtC,MAAM,aAAa,MAAM;AAGzB,QAAI,aAAa,UACf,UAAS,KAAK;KACZ,MAAM;KACN,OAAO,KAAK,MAAM,WAAW,WAAW;KACzC,CAAC;AAIJ,aAAS,KAAK;KACZ,UAAU,CAAC;MAAE,MAAM;MAAQ,OAAO;MAAS,CAAC;KAC5C,MAAM;MACJ,OAAO;MACP,aAAa,EAAE;MAChB;KACD,MAAM;KACP,CAAC;AAEF,gBAAY,aAAa,UAAU;;AAIrC,OAAI,YAAY,KAAK,OACnB,UAAS,KAAK;IACZ,MAAM;IACN,OAAO,KAAK,MAAM,UAAU;IAC7B,CAAC;AAIJ,OAAI,SAAS,SAAS,KAAK,QAAQ;AACjC,WAAO,SAAS,OAAO,OAAO,GAAG,GAAG,SAAS;AAC7C,WAAO,QAAQ,SAAS,SAAS;;IAEnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MaterialFileTypeIcon.mjs","names":[],"sources":["../../src/MaterialFileTypeIcon/MaterialFileTypeIcon.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useMemo } from 'react';\n\nimport { useCdnFn } from '@/ConfigProvider';\nimport FileTypeIcon from '@/FileTypeIcon';\nimport { Center } from '@/Flex';\nimport Img from '@/Img';\n\nimport type { MaterialFileTypeIconProps } from './type';\nimport { getIconUrlForDirectoryPath, getIconUrlForFilePath } from './utils';\n\nconst MaterialFileTypeIcon: FC<MaterialFileTypeIconProps> = ({\n fallbackUnknownType = true,\n filename,\n size = 48,\n variant = 'raw',\n type,\n style,\n open,\n ...rest\n}) => {\n const genCdnUrl = useCdnFn();\n const ICONS_URL = genCdnUrl({\n path: 'assets',\n pkg: '@lobehub/assets-fileicon',\n version: '1.0.0',\n });\n\n const iconUrl: string = useMemo(() => {\n return type === 'file'\n ? getIconUrlForFilePath({ fallbackUnknownType, iconsUrl: ICONS_URL, path: filename })\n : getIconUrlForDirectoryPath({\n fallbackUnknownType,\n iconsUrl: ICONS_URL,\n open,\n path: filename,\n });\n }, [ICONS_URL, type, filename, open]);\n\n if (!iconUrl)\n return (\n <FileTypeIcon\n className={rest.className}\n filetype={filename.split('.')[1]}\n size={size}\n type={type}\n variant={'mono'}\n />\n );\n\n if (variant === 'raw')\n return <Img alt={filename} height={size} src={iconUrl} style={style} width={size} {...rest} />;\n\n return (\n <Center\n flex={'none'}\n height={size}\n style={{ position: 'relative', ...style }}\n width={size}\n {...rest}\n >\n <FileTypeIcon size={size} type={variant} variant={'mono'} />\n <Img\n alt={filename}\n height={size / 2}\n src={iconUrl}\n style={{ position: 'absolute', top: size / 3 }}\n width={size / 2}\n {...rest}\n />\n </Center>\n );\n};\n\nMaterialFileTypeIcon.displayName = 'MaterialFileTypeIcon';\n\nexport default MaterialFileTypeIcon;\n"],"mappings":";;;;;;;;;AAYA,MAAM,wBAAuD,EAC3D,sBAAsB,MACtB,UACA,OAAO,IACP,UAAU,OACV,MACA,OACA,MACA,GAAG,WACC;CAEJ,MAAM,YADY,
|
|
1
|
+
{"version":3,"file":"MaterialFileTypeIcon.mjs","names":[],"sources":["../../src/MaterialFileTypeIcon/MaterialFileTypeIcon.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useMemo } from 'react';\n\nimport { useCdnFn } from '@/ConfigProvider';\nimport FileTypeIcon from '@/FileTypeIcon';\nimport { Center } from '@/Flex';\nimport Img from '@/Img';\n\nimport type { MaterialFileTypeIconProps } from './type';\nimport { getIconUrlForDirectoryPath, getIconUrlForFilePath } from './utils';\n\nconst MaterialFileTypeIcon: FC<MaterialFileTypeIconProps> = ({\n fallbackUnknownType = true,\n filename,\n size = 48,\n variant = 'raw',\n type,\n style,\n open,\n ...rest\n}) => {\n const genCdnUrl = useCdnFn();\n const ICONS_URL = genCdnUrl({\n path: 'assets',\n pkg: '@lobehub/assets-fileicon',\n version: '1.0.0',\n });\n\n const iconUrl: string = useMemo(() => {\n return type === 'file'\n ? getIconUrlForFilePath({ fallbackUnknownType, iconsUrl: ICONS_URL, path: filename })\n : getIconUrlForDirectoryPath({\n fallbackUnknownType,\n iconsUrl: ICONS_URL,\n open,\n path: filename,\n });\n }, [ICONS_URL, type, filename, open]);\n\n if (!iconUrl)\n return (\n <FileTypeIcon\n className={rest.className}\n filetype={filename.split('.')[1]}\n size={size}\n type={type}\n variant={'mono'}\n />\n );\n\n if (variant === 'raw')\n return <Img alt={filename} height={size} src={iconUrl} style={style} width={size} {...rest} />;\n\n return (\n <Center\n flex={'none'}\n height={size}\n style={{ position: 'relative', ...style }}\n width={size}\n {...rest}\n >\n <FileTypeIcon size={size} type={variant} variant={'mono'} />\n <Img\n alt={filename}\n height={size / 2}\n src={iconUrl}\n style={{ position: 'absolute', top: size / 3 }}\n width={size / 2}\n {...rest}\n />\n </Center>\n );\n};\n\nMaterialFileTypeIcon.displayName = 'MaterialFileTypeIcon';\n\nexport default MaterialFileTypeIcon;\n"],"mappings":";;;;;;;;;AAYA,MAAM,wBAAuD,EAC3D,sBAAsB,MACtB,UACA,OAAO,IACP,UAAU,OACV,MACA,OACA,MACA,GAAG,WACC;CAEJ,MAAM,YADY,UACS,CAAC;EAC1B,MAAM;EACN,KAAK;EACL,SAAS;EACV,CAAC;CAEF,MAAM,UAAkB,cAAc;AACpC,SAAO,SAAS,SACZ,sBAAsB;GAAE;GAAqB,UAAU;GAAW,MAAM;GAAU,CAAC,GACnF,2BAA2B;GACzB;GACA,UAAU;GACV;GACA,MAAM;GACP,CAAC;IACL;EAAC;EAAW;EAAM;EAAU;EAAK,CAAC;AAErC,KAAI,CAAC,QACH,QACE,oBAAC,cAAD;EACE,WAAW,KAAK;EAChB,UAAU,SAAS,MAAM,IAAI,CAAC;EACxB;EACA;EACN,SAAS;EACT,CAAA;AAGN,KAAI,YAAY,MACd,QAAO,oBAAC,KAAD;EAAK,KAAK;EAAU,QAAQ;EAAM,KAAK;EAAgB;EAAO,OAAO;EAAM,GAAI;EAAQ,CAAA;AAEhG,QACE,qBAAC,QAAD;EACE,MAAM;EACN,QAAQ;EACR,OAAO;GAAE,UAAU;GAAY,GAAG;GAAO;EACzC,OAAO;EACP,GAAI;YALN,CAOE,oBAAC,cAAD;GAAoB;GAAM,MAAM;GAAS,SAAS;GAAU,CAAA,EAC5D,oBAAC,KAAD;GACE,KAAK;GACL,QAAQ,OAAO;GACf,KAAK;GACL,OAAO;IAAE,UAAU;IAAY,KAAK,OAAO;IAAG;GAC9C,OAAO,OAAO;GACd,GAAI;GACJ,CAAA,CACK;;;AAIb,qBAAqB,cAAc"}
|