-
-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🐛 fix(hightlight): Fix prompt highlight
- Loading branch information
1 parent
fa10967
commit a2698af
Showing
7 changed files
with
282 additions
and
99 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Icon } from '@lobehub/ui'; | ||
import { useThemeMode } from 'antd-style'; | ||
import { Loader2 } from 'lucide-react'; | ||
import { memo, useEffect } from 'react'; | ||
import { Center } from 'react-layout-kit'; | ||
import { type HighlighterOptions } from 'shiki-es'; | ||
|
||
import { useStyles } from './style'; | ||
import { useHighlight } from './useHighlight'; | ||
|
||
export interface SyntaxHighlighterProps { | ||
children: string; | ||
language: string; | ||
options?: HighlighterOptions; | ||
} | ||
|
||
const SyntaxHighlighter = memo<SyntaxHighlighterProps>(({ children, language, options }) => { | ||
const { styles } = useStyles(); | ||
const { isDarkMode } = useThemeMode(); | ||
const [codeToHtml, isLoading] = useHighlight((s) => [s.codeToHtml, !s.highlighter]); | ||
|
||
useEffect(() => { | ||
useHighlight.getState().initHighlighter(options); | ||
}, [options]); | ||
|
||
return ( | ||
<> | ||
{isLoading ? ( | ||
<code>{children}</code> | ||
) : ( | ||
<div | ||
className={styles.shiki} | ||
dangerouslySetInnerHTML={{ | ||
__html: codeToHtml(children, language, isDarkMode) || '', | ||
}} | ||
/> | ||
)} | ||
|
||
{isLoading && ( | ||
<Center className={styles.loading} gap={8} horizontal> | ||
<Icon icon={Loader2} spin /> | ||
Highlighting... | ||
</Center> | ||
)} | ||
</> | ||
); | ||
}); | ||
|
||
export default SyntaxHighlighter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,63 @@ | ||
import { createStyles } from 'antd-style'; | ||
|
||
export const useStyles = createStyles(({ css, token }) => ({ | ||
container: css` | ||
pointer-events: none; | ||
position: absolute; | ||
overflow-x: hidden; | ||
overflow-y: auto; | ||
padding: 8px; | ||
pre { | ||
font-family: ${token.fontFamilyCode} !important; | ||
font-size: 13px; | ||
line-height: 18.2px; | ||
color: ${token.colorSuccess}; | ||
word-wrap: break-word; | ||
white-space: pre-wrap; | ||
} | ||
`, | ||
})); | ||
export const useStyles = createStyles(({ css, token, cx, stylish, prefixCls }) => { | ||
const prefix = `${prefixCls}-highlighter`; | ||
return { | ||
container: css` | ||
pointer-events: none; | ||
position: absolute; | ||
overflow-x: hidden; | ||
overflow-y: auto; | ||
padding: 8px; | ||
pre { | ||
font-family: ${token.fontFamilyCode} !important; | ||
font-size: 13px !important; | ||
line-height: 1.5 !important; | ||
color: ${token.colorSuccess}; | ||
text-overflow: ellipsis !important; | ||
word-wrap: break-word; | ||
white-space: pre-wrap; | ||
vertical-align: bottom !important; | ||
} | ||
`, | ||
loading: cx( | ||
stylish.blur, | ||
css` | ||
position: absolute; | ||
z-index: 10; | ||
top: 0; | ||
right: 0; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
height: 34px; | ||
padding: 0 8px; | ||
font-family: ${token.fontFamilyCode}; | ||
color: ${token.colorTextTertiary}; | ||
border-radius: ${token.borderRadius}; | ||
`, | ||
), | ||
shiki: cx( | ||
`${prefix}-shiki`, | ||
css` | ||
margin: 0; | ||
padding: 0; | ||
.shiki { | ||
overflow-x: auto; | ||
margin: 0; | ||
padding: 0; | ||
background: none !important; | ||
} | ||
`, | ||
), | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { type Highlighter, type HighlighterOptions, getHighlighter } from 'shiki-es'; | ||
import { shallow } from 'zustand/shallow'; | ||
import { createWithEqualityFn } from 'zustand/traditional'; | ||
|
||
export const languageMap = [] as const; | ||
|
||
/** | ||
* @title 代码高亮的存储对象 | ||
*/ | ||
interface Store { | ||
/** | ||
* @title Convert code to HTML string | ||
* @param text - The code text | ||
* @param language - The language of the code | ||
* @param isDarkMode - Whether it's in dark mode or not | ||
* @returns HTML string | ||
*/ | ||
codeToHtml: (text: string, language: string, isDarkMode: boolean) => string; | ||
/** | ||
* @title Highlighter object | ||
*/ | ||
highlighter?: Highlighter; | ||
/** | ||
* @title Initialize the highlighter object | ||
* @returns Initialization promise object | ||
*/ | ||
initHighlighter: (options?: HighlighterOptions) => Promise<void>; | ||
} | ||
|
||
export const useHighlight = createWithEqualityFn<Store>( | ||
(set, get) => ({ | ||
codeToHtml: (text, language, isDarkMode) => { | ||
const { highlighter } = get(); | ||
|
||
if (!highlighter) return ''; | ||
|
||
try { | ||
return highlighter?.codeToHtml(text, { | ||
lang: language, | ||
theme: isDarkMode ? 'dark' : 'light', | ||
}); | ||
} catch { | ||
return text; | ||
} | ||
}, | ||
highlighter: undefined, | ||
|
||
initHighlighter: async(options) => { | ||
if (!get().highlighter) { | ||
const highlighter = await getHighlighter({ | ||
langs: options?.langs, | ||
themes: options?.themes, | ||
}); | ||
|
||
set({ highlighter }); | ||
} | ||
}, | ||
}), | ||
shallow, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters