refactor(desktop): improve shiki highlight component (#3461)

* refactor(desktop): improve shiki highlight component

* update

* update
This commit is contained in:
Konv Suu 2025-04-16 17:33:07 +08:00 committed by GitHub
parent be093e1b75
commit 18de7a8de8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 33 additions and 79 deletions

View File

@ -7,7 +7,7 @@ import { AnimatedCommandButton } from "./base"
export const CopyButton: Component<{
value: string
style?: React.CSSProperties
variant?: "solid" | "outline"
variant?: "solid" | "outline" | "ghost"
}> = ({ value, className, style, variant = "solid" }) => {
const copiedTimerRef = useRef<any>()
const handleCopy = useCallback(() => {

View File

@ -44,7 +44,7 @@ export const AnimatedCommandButton: FC<AnimatedCommandButtonProps & HTMLMotionPr
"center pointer-events-auto flex text-xs",
"rounded-md p-1.5 duration-200",
variant === "solid" || variant === "ghost"
? "border-accent/5 bg-accent/80 border text-white backdrop-blur"
? "border-accent/5 bg-accent/80 text-accent border backdrop-blur"
: "text-accent bg-slate-50/50 dark:bg-slate-900/50",
variant === "ghost" && "bg-theme-item-active hover:bg-theme-item-hover",
className,

View File

@ -15,44 +15,7 @@ const LanguageAlias = {
jsx: "javascriptreact",
md: "markdown",
}
const LanguageToColorMap = {
"c#": "#178600",
"c++": "#f34b7d",
"objective-c": "#438eff",
"objective-c++": "#6866fb",
c: "#555555",
cpp: "#f34b7d",
csharp: "#178600",
css: "#563d7c",
dart: "#00B4AB",
dockerfile: "#384d54",
go: "#00add8",
html: "#e34c26",
java: "#b07219",
javascript: "#f1e05a",
javascriptreact: "#f1e05a",
json: "#292929",
kotlin: "#F18E33",
lisp: "#3fb68b",
lua: "#000080",
markdown: "#083fa1",
matlab: "#e16737",
perl: "#0298c3",
php: "#4f5d95",
python: "#3572a5",
r: "#198ce7",
ruby: "#701516",
rust: "#dea584",
scala: "#c22d40",
shell: "#89e051",
sql: "#e38c00",
swift: "#ffac45",
typescript: "#2b7489",
typescriptreact: "#2b7489",
vimscript: "#199f4b",
vue: "#2c3e50",
yaml: "#cb171e",
}
const languageToIconMap = {
javascriptreact: <UilReact />,
typescriptreact: <UilReact />,
@ -62,15 +25,6 @@ const languageToIconMap = {
css: <MdiLanguageCss3 />,
markdown: <RiMarkdownFill />,
}
export const getLanguageColor = (language?: string) => {
if (!language) return
const alias = LanguageAlias[language]
if (alias) {
return LanguageToColorMap[alias]
}
return LanguageToColorMap[language]
}
export const getLanguageIcon = (language?: string) => {
if (!language) return null

View File

@ -1,4 +1,3 @@
import { useIsDark } from "@follow/hooks"
import { ELECTRON_BUILD } from "@follow/shared/constants"
import { cn } from "@follow/utils/utils"
import { useIsomorphicLayoutEffect } from "foxact/use-isomorphic-layout-effect"
@ -11,11 +10,12 @@ import type {
DynamicImportThemeRegistration,
} from "shiki"
import { useUISettingKey, useUISettingSelector } from "~/atoms/settings/ui"
import { useUISettingKey } from "~/atoms/settings/ui"
import { tipcClient } from "~/lib/client"
import { CopyButton } from "../../button/CopyButton"
import { getLanguageColor, getLanguageIcon } from "../constants"
import { getLanguageIcon } from "../constants"
import { useShikiDefaultTheme } from "./hooks"
import { shiki, shikiTransformers } from "./shared"
import styles from "./shiki.module.css"
@ -72,10 +72,7 @@ export const ShikiHighLighter: FC<ShikiProps> = (props) => {
const [loaded, setLoaded] = useState(false)
const isDark = useIsDark()
const codeThemeLight = useUISettingSelector((s) => overrideTheme || s.codeHighlightThemeLight)
const codeThemeDark = useUISettingSelector((s) => overrideTheme || s.codeHighlightThemeDark)
const codeTheme = isDark ? codeThemeDark : codeThemeLight
const codeTheme = useShikiDefaultTheme(overrideTheme)
useIsomorphicLayoutEffect(() => {
let isMounted = true
@ -188,27 +185,22 @@ const ShikiCode: FC<
return (
<div
className={cn(
"group relative my-4",
"my-4 border",
styles["shiki-wrapper"],
transparent ? styles["transparent"] : null,
className,
)}
>
<div className="flex items-center justify-between border-b p-2">
{language !== "plaintext" && (
<span className="center flex gap-1 text-xs uppercase opacity-80 dark:text-white">
<span className="center [&_svg]:size-4">{getLanguageIcon(language)}</span>
<span>{language}</span>
</span>
)}
<CopyButton variant="outline" value={code} />
</div>
<div dangerouslySetInnerHTML={{ __html: rendered }} data-language={language} />
<CopyButton
variant="outline"
value={code}
style={{
color: getLanguageColor(language),
}}
className={"absolute right-2 top-2 opacity-0 duration-200 group-hover:opacity-100"}
/>
{language !== "plaintext" && (
<span className="center absolute bottom-2 right-2 flex gap-1 text-xs uppercase opacity-80 dark:text-white">
<span className="center [&_svg]:size-4">{getLanguageIcon(language)}</span>
<span>{language}</span>
</span>
)}
</div>
)
}

View File

@ -1,7 +1,11 @@
import { useIsDark } from "@follow/hooks"
export const useShikiDefaultTheme = () => {
const isDark = useIsDark()
import { useUISettingSelector } from "~/atoms/settings/ui"
return isDark ? "github-dark" : "github-light"
export const useShikiDefaultTheme = (overrideTheme?: string) => {
const isDark = useIsDark()
const codeThemeLight = useUISettingSelector((s) => overrideTheme || s.codeHighlightThemeLight)
const codeThemeDark = useUISettingSelector((s) => overrideTheme || s.codeHighlightThemeDark)
return isDark ? codeThemeDark : codeThemeLight
}

View File

@ -66,6 +66,13 @@ export function AddModalContent({
}
}, [details.data])
const codes = [
`FOLLOW_OWNER_USER_ID=${me?.handle || me?.id} # User id or handle of your follow account`,
`FOLLOW_DESCRIPTION=${instance?.description || `${me?.name}'s instance`} # The description of your instance`,
`FOLLOW_PRICE=${instance?.price || 100} # The monthly price of your instance, set to 0 means free.`,
`FOLLOW_USER_LIMIT=${instance?.userLimit || 1000} # The user limit of your instance, set it to 0 or 1 can make your instance private, leaving it empty means no restriction`,
]
return (
<div className="max-w-[550px] space-y-4 lg:min-w-[550px]">
<div className="text-sm">{t("rsshub.addModal.description")}</div>
@ -73,11 +80,8 @@ export function AddModalContent({
<ShikiHighLighter
transparent
theme={shikiTheme}
className="border-border group relative mt-3 cursor-auto select-text whitespace-pre break-words rounded-lg border bg-zinc-100 p-2 text-sm dark:bg-neutral-800 [&_pre]:whitespace-pre [&_pre]:break-words [&_pre]:!p-0"
code={`FOLLOW_OWNER_USER_ID=${me?.handle || me?.id} # User id or handle of your follow account
FOLLOW_DESCRIPTION=${instance?.description || `${me?.name}'s instance`} # The description of your instance
FOLLOW_PRICE=${instance?.price || 100} # The monthly price of your instance, set to 0 means free.
FOLLOW_USER_LIMIT=${instance?.userLimit || 1000} # The user limit of your instance, set it to 0 or 1 can make your instance private, leaving it empty means no restriction`}
className="group mt-3 cursor-auto select-text whitespace-pre break-words rounded-lg bg-zinc-100 text-sm dark:bg-neutral-800 [&_pre]:whitespace-pre [&_pre]:break-words"
code={codes.join("\n")}
language="dotenv"
/>
{details.isLoading ? (