mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat(web): add built-in Nerd Font for terminal icon support (#122)
* feat(web): add built-in Nerd Font for terminal icon support Co-authored-by: tfq <tfq@gmail.com> Co-authored-by: weishu <twsxtd@gmail.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { Terminal } from '@xterm/xterm'
|
||||
import { FitAddon } from '@xterm/addon-fit'
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links'
|
||||
import { CanvasAddon } from '@xterm/addon-canvas'
|
||||
import '@xterm/xterm/css/xterm.css'
|
||||
import { createFontProvider, type ITerminalFontProvider } from '@/lib/terminalFont'
|
||||
import { ensureBuiltinFontLoaded, getFontProvider } from '@/lib/terminalFont'
|
||||
|
||||
function resolveThemeColors(): { background: string; foreground: string; selectionBackground: string } {
|
||||
const styles = getComputedStyle(document.documentElement)
|
||||
@@ -22,12 +22,6 @@ export function TerminalView(props: {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null)
|
||||
const onMountRef = useRef(props.onMount)
|
||||
const onResizeRef = useRef(props.onResize)
|
||||
const [fontProvider, setFontProvider] = useState<ITerminalFontProvider | null>(null)
|
||||
|
||||
// Initialize font provider
|
||||
useEffect(() => {
|
||||
createFontProvider('default').then(setFontProvider)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
onMountRef.current = props.onMount
|
||||
@@ -39,10 +33,11 @@ export function TerminalView(props: {
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current
|
||||
if (!container || !fontProvider) {
|
||||
return
|
||||
}
|
||||
if (!container) return
|
||||
|
||||
const abortController = new AbortController()
|
||||
|
||||
const fontProvider = getFontProvider()
|
||||
const { background, foreground, selectionBackground } = resolveThemeColors()
|
||||
const terminal = new Terminal({
|
||||
cursorBlink: true,
|
||||
@@ -66,27 +61,62 @@ export function TerminalView(props: {
|
||||
terminal.loadAddon(canvasAddon)
|
||||
terminal.open(container)
|
||||
|
||||
const resizeTerminal = () => {
|
||||
const observer = new ResizeObserver(() => {
|
||||
requestAnimationFrame(() => {
|
||||
fitAddon.fit()
|
||||
onResizeRef.current?.(terminal.cols, terminal.rows)
|
||||
})
|
||||
})
|
||||
observer.observe(container)
|
||||
|
||||
const refreshFont = (forceRemeasure = false) => {
|
||||
if (abortController.signal.aborted) return
|
||||
const nextFamily = fontProvider.getFontFamily()
|
||||
|
||||
if (forceRemeasure && terminal.options.fontFamily === nextFamily) {
|
||||
terminal.options.fontFamily = `${nextFamily}, "__hapi_font_refresh__"`
|
||||
requestAnimationFrame(() => {
|
||||
if (abortController.signal.aborted) return
|
||||
terminal.options.fontFamily = nextFamily
|
||||
if (terminal.rows > 0) {
|
||||
terminal.refresh(0, terminal.rows - 1)
|
||||
}
|
||||
fitAddon.fit()
|
||||
onResizeRef.current?.(terminal.cols, terminal.rows)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
terminal.options.fontFamily = nextFamily
|
||||
if (terminal.rows > 0) {
|
||||
terminal.refresh(0, terminal.rows - 1)
|
||||
}
|
||||
fitAddon.fit()
|
||||
onResizeRef.current?.(terminal.cols, terminal.rows)
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
requestAnimationFrame(resizeTerminal)
|
||||
void ensureBuiltinFontLoaded().then(loaded => {
|
||||
if (!loaded) return
|
||||
refreshFont(true)
|
||||
})
|
||||
observer.observe(container)
|
||||
|
||||
requestAnimationFrame(resizeTerminal)
|
||||
onMountRef.current?.(terminal)
|
||||
|
||||
return () => {
|
||||
// Cleanup on abort
|
||||
abortController.signal.addEventListener('abort', () => {
|
||||
observer.disconnect()
|
||||
fitAddon.dispose()
|
||||
webLinksAddon.dispose()
|
||||
canvasAddon.dispose()
|
||||
terminal.dispose()
|
||||
}
|
||||
}, [fontProvider])
|
||||
})
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
fitAddon.fit()
|
||||
onResizeRef.current?.(terminal.cols, terminal.rows)
|
||||
})
|
||||
onMountRef.current?.(terminal)
|
||||
|
||||
return () => abortController.abort()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user