mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
refactor(desktop): dock terminal under chat and simplify file rail
Keep the right rail focused on file browsing while moving the persistent terminal into the chat column bottom slot, and make terminal colors follow the active light/dark mode instead of a fixed Solarized palette.
This commit is contained in:
parent
02e56da0fc
commit
7521de42f4
8 changed files with 111 additions and 154 deletions
|
|
@ -10,6 +10,7 @@ import type * as React from 'react'
|
|||
import { Suspense, useCallback, useMemo, useRef } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { TerminalSlot } from '@/app/right-sidebar/terminal/persistent'
|
||||
import { Thread } from '@/components/assistant-ui/thread'
|
||||
import { Backdrop } from '@/components/Backdrop'
|
||||
import { PromptOverlays } from '@/components/prompt-overlays'
|
||||
|
|
@ -388,6 +389,9 @@ export function ChatView({
|
|||
<ChatDropOverlay kind={dragKind} />
|
||||
<ChatSwapOverlay profile={gatewaySwapTarget} />
|
||||
</div>
|
||||
<section className="flex h-56 min-h-[10rem] shrink-0 border-t border-(--ui-stroke-secondary) bg-(--ui-editor-surface-background)">
|
||||
<TerminalSlot />
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { setRightSidebarTab } from '@/app/right-sidebar/store'
|
||||
import { setTerminalTakeover } from '@/app/right-sidebar/store'
|
||||
import { PANE_TOGGLE_REVEAL_EVENT } from '@/components/pane-shell'
|
||||
import { matchesQuery } from '@/hooks/use-media-query'
|
||||
import { PROFILE_SLOT_COUNT } from '@/lib/keybinds/actions'
|
||||
|
|
@ -84,9 +84,9 @@ export function useKeybinds(deps: KeybindRuntimeDeps): void {
|
|||
}
|
||||
}
|
||||
|
||||
const showRightSidebarTab = (tab: 'files' | 'terminal') => {
|
||||
const showFiles = () => {
|
||||
setFileBrowserOpen(true)
|
||||
setRightSidebarTab(tab)
|
||||
setTerminalTakeover(false)
|
||||
}
|
||||
|
||||
handlersRef.current = {
|
||||
|
|
@ -128,8 +128,8 @@ export function useKeybinds(deps: KeybindRuntimeDeps): void {
|
|||
toggleFileBrowserOpen()
|
||||
}
|
||||
},
|
||||
'view.showFiles': () => showRightSidebarTab('files'),
|
||||
'view.showTerminal': () => showRightSidebarTab('terminal'),
|
||||
'view.showFiles': showFiles,
|
||||
'view.showTerminal': () => setTerminalTakeover(true),
|
||||
'view.flipPanes': togglePanesFlipped,
|
||||
|
||||
'appearance.toggleMode': () => setMode(resolvedMode === 'dark' ? 'light' : 'dark'),
|
||||
|
|
|
|||
|
|
@ -4,22 +4,20 @@ import type { ReactNode } from 'react'
|
|||
import { ErrorBoundary } from '@/components/error-boundary'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { Loader } from '@/components/ui/loader'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { normalizeOrLocalPreviewTarget } from '@/lib/local-preview'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { $panesFlipped } from '@/store/layout'
|
||||
import { notifyError } from '@/store/notifications'
|
||||
import { setCurrentSessionPreviewTarget } from '@/store/preview'
|
||||
import { $currentBranch, $currentCwd } from '@/store/session'
|
||||
import { $currentCwd } from '@/store/session'
|
||||
|
||||
import { SidebarPanelLabel } from '../shell/sidebar-label'
|
||||
|
||||
import { ProjectTree } from './files/tree'
|
||||
import { useProjectTree } from './files/use-project-tree'
|
||||
import { $rightSidebarTab, $terminalTakeover, type RightSidebarTabId, setRightSidebarTab } from './store'
|
||||
import { TerminalSlot } from './terminal/persistent'
|
||||
|
||||
interface RightSidebarPaneProps {
|
||||
onActivateFile: (path: string) => void
|
||||
|
|
@ -27,24 +25,10 @@ interface RightSidebarPaneProps {
|
|||
onChangeCwd: (path: string) => Promise<void> | void
|
||||
}
|
||||
|
||||
interface RightSidebarTab {
|
||||
icon: string
|
||||
id: RightSidebarTabId
|
||||
labelKey: 'files' | 'terminal'
|
||||
}
|
||||
|
||||
const RIGHT_SIDEBAR_TABS: readonly RightSidebarTab[] = [
|
||||
{ id: 'files', labelKey: 'files', icon: 'list-tree' },
|
||||
{ id: 'terminal', labelKey: 'terminal', icon: 'terminal' }
|
||||
]
|
||||
|
||||
export function RightSidebarPane({ onActivateFile, onActivateFolder, onChangeCwd }: RightSidebarPaneProps) {
|
||||
const { t } = useI18n()
|
||||
const r = t.rightSidebar
|
||||
const activeTab = useStore($rightSidebarTab)
|
||||
const terminalTakeover = useStore($terminalTakeover)
|
||||
const panesFlipped = useStore($panesFlipped)
|
||||
const currentBranch = useStore($currentBranch).trim()
|
||||
const currentCwd = useStore($currentCwd).trim()
|
||||
const hasCwd = currentCwd.length > 0
|
||||
|
||||
|
|
@ -68,7 +52,6 @@ export function RightSidebarPane({ onActivateFile, onActivateFolder, onChangeCwd
|
|||
} = useProjectTree(currentCwd)
|
||||
|
||||
const canCollapse = Object.values(openState).some(Boolean)
|
||||
const effectiveTab: RightSidebarTabId = terminalTakeover ? 'files' : activeTab
|
||||
|
||||
const chooseFolder = async () => {
|
||||
const selected = await window.hermesDesktop?.selectPaths({
|
||||
|
|
@ -97,8 +80,6 @@ export function RightSidebarPane({ onActivateFile, onActivateFolder, onChangeCwd
|
|||
}
|
||||
}
|
||||
|
||||
const tabs = terminalTakeover ? RIGHT_SIDEBAR_TABS.filter(tab => tab.id !== 'terminal') : RIGHT_SIDEBAR_TABS
|
||||
|
||||
return (
|
||||
<aside
|
||||
aria-label={r.aria}
|
||||
|
|
@ -109,85 +90,29 @@ export function RightSidebarPane({ onActivateFile, onActivateFolder, onChangeCwd
|
|||
: 'border-l shadow-[inset_0.0625rem_0_0_color-mix(in_srgb,white_18%,transparent)]'
|
||||
)}
|
||||
>
|
||||
<RightSidebarChrome activeTab={effectiveTab} branch={currentBranch} tabs={tabs} />
|
||||
|
||||
{effectiveTab === 'terminal' ? (
|
||||
<TerminalSlot />
|
||||
) : (
|
||||
<FilesystemTab
|
||||
canCollapse={canCollapse}
|
||||
collapseNonce={collapseNonce}
|
||||
cwd={currentCwd}
|
||||
cwdName={cwdName}
|
||||
data={data}
|
||||
error={rootError}
|
||||
hasCwd={hasCwd}
|
||||
loading={rootLoading}
|
||||
onActivateFile={onActivateFile}
|
||||
onActivateFolder={onActivateFolder}
|
||||
onChangeFolder={chooseFolder}
|
||||
onCollapseAll={collapseAll}
|
||||
onLoadChildren={loadChildren}
|
||||
onNodeOpenChange={setNodeOpen}
|
||||
onPreviewFile={previewFile}
|
||||
onRefresh={() => void refreshRoot()}
|
||||
openState={openState}
|
||||
/>
|
||||
)}
|
||||
<FilesystemTab
|
||||
canCollapse={canCollapse}
|
||||
collapseNonce={collapseNonce}
|
||||
cwd={currentCwd}
|
||||
cwdName={cwdName}
|
||||
data={data}
|
||||
error={rootError}
|
||||
hasCwd={hasCwd}
|
||||
loading={rootLoading}
|
||||
onActivateFile={onActivateFile}
|
||||
onActivateFolder={onActivateFolder}
|
||||
onChangeFolder={chooseFolder}
|
||||
onCollapseAll={collapseAll}
|
||||
onLoadChildren={loadChildren}
|
||||
onNodeOpenChange={setNodeOpen}
|
||||
onPreviewFile={previewFile}
|
||||
onRefresh={() => void refreshRoot()}
|
||||
openState={openState}
|
||||
/>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
||||
function RightSidebarChrome({
|
||||
activeTab,
|
||||
branch,
|
||||
tabs
|
||||
}: {
|
||||
activeTab: RightSidebarTabId
|
||||
branch: string
|
||||
tabs: readonly RightSidebarTab[]
|
||||
}) {
|
||||
const { t } = useI18n()
|
||||
const r = t.rightSidebar
|
||||
|
||||
return (
|
||||
<header className="shrink-0 bg-transparent text-[0.75rem]">
|
||||
<div className="flex items-center gap-2 px-2.5 py-1">
|
||||
<nav aria-label={r.panelsAria} className="flex min-w-0 items-center gap-1">
|
||||
{tabs.map(tab => {
|
||||
const label = r[tab.labelKey]
|
||||
|
||||
return (
|
||||
<Tip key={tab.id} label={label}>
|
||||
<Button
|
||||
aria-label={label}
|
||||
aria-pressed={tab.id === activeTab}
|
||||
className={cn(
|
||||
'text-(--ui-text-tertiary) hover:bg-(--ui-control-hover-background) hover:text-foreground',
|
||||
tab.id === activeTab && 'bg-(--ui-control-active-background) text-foreground'
|
||||
)}
|
||||
onClick={() => setRightSidebarTab(tab.id)}
|
||||
size="icon-xs"
|
||||
variant="ghost"
|
||||
>
|
||||
<Codicon name={tab.icon} size="0.875rem" />
|
||||
</Button>
|
||||
</Tip>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{branch && (
|
||||
<span className="ml-auto flex min-w-0 items-center gap-1 text-[0.6875rem] text-(--ui-text-tertiary)">
|
||||
<Codicon className="shrink-0" name="git-branch" size="0.75rem" />
|
||||
<span className="truncate">{branch}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
interface FilesystemTabProps extends FileTreeBodyProps {
|
||||
canCollapse: boolean
|
||||
cwdName: string
|
||||
|
|
|
|||
|
|
@ -2,14 +2,10 @@ import { atom } from 'nanostores'
|
|||
|
||||
import { persistBoolean, storedBoolean } from '@/lib/storage'
|
||||
|
||||
export type RightSidebarTabId = 'files' | 'git' | 'terminal' | 'web'
|
||||
|
||||
const TAKEOVER_KEY = 'hermes.desktop.terminalTakeover'
|
||||
|
||||
export const $rightSidebarTab = atom<RightSidebarTabId>('files')
|
||||
export const $terminalTakeover = atom(storedBoolean(TAKEOVER_KEY, false))
|
||||
|
||||
$terminalTakeover.subscribe(active => persistBoolean(TAKEOVER_KEY, active))
|
||||
|
||||
export const setRightSidebarTab = (tab: RightSidebarTabId) => $rightSidebarTab.set(tab)
|
||||
export const setTerminalTakeover = (active: boolean) => $terminalTakeover.set(active)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
import '@xterm/xterm/css/xterm.css'
|
||||
|
||||
import { useStore } from '@nanostores/react'
|
||||
import type { CSSProperties } from 'react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { Loader } from '@/components/ui/loader'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { useTheme } from '@/themes/context'
|
||||
|
||||
import { SidebarPanelLabel } from '../../shell/sidebar-label'
|
||||
import { $terminalTakeover, setRightSidebarTab, setTerminalTakeover } from '../store'
|
||||
import { $terminalTakeover, setTerminalTakeover } from '../store'
|
||||
|
||||
import { addSelectionShortcutLabel } from './selection'
|
||||
import { addSelectionShortcutLabel, terminalTheme } from './selection'
|
||||
import { useTerminalSession } from './use-terminal-session'
|
||||
|
||||
interface TerminalTabProps {
|
||||
|
|
@ -21,6 +23,9 @@ interface TerminalTabProps {
|
|||
|
||||
export function TerminalTab({ cwd, onAddSelectionToChat }: TerminalTabProps) {
|
||||
const { t } = useI18n()
|
||||
const { resolvedMode } = useTheme()
|
||||
const theme = terminalTheme(resolvedMode)
|
||||
|
||||
const { addSelectionToChat, hostRef, selection, selectionStyle, shellName, status } = useTerminalSession({
|
||||
cwd,
|
||||
onAddSelectionToChat
|
||||
|
|
@ -30,22 +35,17 @@ export function TerminalTab({ cwd, onAddSelectionToChat }: TerminalTabProps) {
|
|||
const label = takeover ? t.rightSidebar.terminalSplit : t.rightSidebar.terminalFocus
|
||||
|
||||
const toggleTakeover = () => {
|
||||
// Pre-select the Terminal tab so the slot is ready to host us on return.
|
||||
if (takeover) {
|
||||
setRightSidebarTab('terminal')
|
||||
}
|
||||
|
||||
setTerminalTakeover(!takeover)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-0 min-w-0 flex-1 flex-col">
|
||||
<div className="flex h-8 shrink-0 items-center gap-2 px-2.5">
|
||||
<SidebarPanelLabel className="text-white!">{shellName}</SidebarPanelLabel>
|
||||
<SidebarPanelLabel className="text-(--ui-text-secondary)!">{shellName}</SidebarPanelLabel>
|
||||
<Tip label={label}>
|
||||
<Button
|
||||
aria-label={label}
|
||||
className="ml-auto size-6 rounded-md text-white!"
|
||||
className="ml-auto size-6 rounded-md text-(--ui-text-secondary)!"
|
||||
onClick={toggleTakeover}
|
||||
size="icon"
|
||||
type="button"
|
||||
|
|
@ -55,7 +55,7 @@ export function TerminalTab({ cwd, onAddSelectionToChat }: TerminalTabProps) {
|
|||
</Button>
|
||||
</Tip>
|
||||
</div>
|
||||
<div className="relative min-h-0 flex-1 bg-[#002b36] p-2">
|
||||
<div className="relative min-h-0 flex-1 p-2" style={{ backgroundColor: theme.background }}>
|
||||
{status === 'starting' && (
|
||||
<div className="pointer-events-none absolute inset-0 z-10 grid place-items-center">
|
||||
<Loader
|
||||
|
|
@ -84,13 +84,12 @@ export function TerminalTab({ cwd, onAddSelectionToChat }: TerminalTabProps) {
|
|||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{/* Outer div paints the dark inset; inner div is the xterm host so the
|
||||
canvas sizes to the *content* area and p-2 shows as terminal padding.
|
||||
Forcing screen/viewport bg avoids xterm's default black peeking
|
||||
through the unused pixels below the last full row. */}
|
||||
{/* Outer div paints terminal inset; inner div is the xterm host so the
|
||||
canvas sizes to the content area and p-2 stays as terminal padding. */}
|
||||
<div
|
||||
className="h-full min-h-0 overflow-hidden text-(--ui-text-secondary) [&_.xterm]:h-full [&_.xterm-screen]:bg-[#002b36]! [&_.xterm-viewport]:bg-[#002b36]!"
|
||||
className="h-full min-h-0 overflow-hidden text-(--ui-text-secondary) [&_.xterm]:h-full [&_.xterm-screen]:bg-[var(--terminal-bg)]! [&_.xterm-viewport]:bg-[var(--terminal-bg)]!"
|
||||
ref={hostRef}
|
||||
style={{ '--terminal-bg': theme.background } as CSSProperties}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ import { useStore } from '@nanostores/react'
|
|||
import { atom } from 'nanostores'
|
||||
import { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
|
||||
import { TERMINAL_BG } from './selection'
|
||||
import { useTheme } from '@/themes/context'
|
||||
|
||||
import { terminalTheme } from './selection'
|
||||
|
||||
import { TerminalTab } from './index'
|
||||
|
||||
|
|
@ -56,6 +58,8 @@ const sameRect = (a: Rect | null, b: Rect) =>
|
|||
|
||||
export function PersistentTerminal({ cwd, onAddSelectionToChat }: PersistentTerminalProps) {
|
||||
const slot = useStore($slot)
|
||||
const { resolvedMode } = useTheme()
|
||||
const theme = terminalTheme(resolvedMode)
|
||||
const [rect, setRect] = useState<Rect | null>(null)
|
||||
const [ready, setReady] = useState(false)
|
||||
|
||||
|
|
@ -107,7 +111,7 @@ export function PersistentTerminal({ cwd, onAddSelectionToChat }: PersistentTerm
|
|||
visibility: visible ? 'visible' : 'hidden',
|
||||
pointerEvents: visible ? 'auto' : 'none',
|
||||
zIndex: 4,
|
||||
backgroundColor: TERMINAL_BG,
|
||||
backgroundColor: theme.background,
|
||||
contain: 'layout size paint'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +1,55 @@
|
|||
import type { ITheme, Terminal } from '@xterm/xterm'
|
||||
import type { CSSProperties } from 'react'
|
||||
|
||||
// Solarized-derived palette, but with bright ANSI 8–15 promoted to real
|
||||
// accent variants instead of Schoonover's UI grays. Hermes' TUI skins (gold,
|
||||
// crimson, ...) emit bright SGR codes that would otherwise wash out to gray.
|
||||
// We always render the dark canvas — the app's light surfaces can't host the
|
||||
// default skin without dropping below readable contrast.
|
||||
export const TERMINAL_BG = '#002b36'
|
||||
|
||||
const THEME: ITheme = {
|
||||
background: TERMINAL_BG,
|
||||
foreground: '#839496',
|
||||
cursor: '#93a1a1',
|
||||
cursorAccent: TERMINAL_BG,
|
||||
selectionBackground: '#586e7555',
|
||||
black: '#073642',
|
||||
red: '#dc322f',
|
||||
green: '#859900',
|
||||
yellow: '#b58900',
|
||||
blue: '#268bd2',
|
||||
magenta: '#d33682',
|
||||
cyan: '#2aa198',
|
||||
white: '#eee8d5',
|
||||
brightBlack: '#586e75',
|
||||
brightRed: '#f25c54',
|
||||
brightGreen: '#b3d437',
|
||||
brightYellow: '#f7c948',
|
||||
brightBlue: '#5fb3ff',
|
||||
brightMagenta: '#ff6ab4',
|
||||
brightCyan: '#5cd9c8',
|
||||
brightWhite: '#fdf6e3'
|
||||
const DARK_THEME: ITheme = {
|
||||
background: '#0f172a',
|
||||
foreground: '#dbe4ff',
|
||||
cursor: '#f8fafc',
|
||||
cursorAccent: '#0f172a',
|
||||
selectionBackground: '#93c5fd55',
|
||||
black: '#1e293b',
|
||||
red: '#f87171',
|
||||
green: '#4ade80',
|
||||
yellow: '#facc15',
|
||||
blue: '#60a5fa',
|
||||
magenta: '#c084fc',
|
||||
cyan: '#22d3ee',
|
||||
white: '#e2e8f0',
|
||||
brightBlack: '#64748b',
|
||||
brightRed: '#fca5a5',
|
||||
brightGreen: '#86efac',
|
||||
brightYellow: '#fde047',
|
||||
brightBlue: '#93c5fd',
|
||||
brightMagenta: '#d8b4fe',
|
||||
brightCyan: '#67e8f9',
|
||||
brightWhite: '#f8fafc'
|
||||
}
|
||||
|
||||
export const terminalTheme = (): ITheme => THEME
|
||||
const LIGHT_THEME: ITheme = {
|
||||
background: '#f8fafc',
|
||||
foreground: '#1f2937',
|
||||
cursor: '#111827',
|
||||
cursorAccent: '#f8fafc',
|
||||
selectionBackground: '#60a5fa44',
|
||||
black: '#1f2937',
|
||||
red: '#dc2626',
|
||||
green: '#15803d',
|
||||
yellow: '#a16207',
|
||||
blue: '#1d4ed8',
|
||||
magenta: '#9333ea',
|
||||
cyan: '#0e7490',
|
||||
white: '#d1d5db',
|
||||
brightBlack: '#4b5563',
|
||||
brightRed: '#ef4444',
|
||||
brightGreen: '#22c55e',
|
||||
brightYellow: '#eab308',
|
||||
brightBlue: '#3b82f6',
|
||||
brightMagenta: '#a855f7',
|
||||
brightCyan: '#06b6d4',
|
||||
brightWhite: '#111827'
|
||||
}
|
||||
|
||||
export const terminalTheme = (mode: 'light' | 'dark'): ITheme => (mode === 'dark' ? DARK_THEME : LIGHT_THEME)
|
||||
|
||||
export const isMacPlatform = () => navigator.platform.toLowerCase().includes('mac')
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import { Unicode11Addon } from '@xterm/addon-unicode11'
|
|||
import { WebLinksAddon } from '@xterm/addon-web-links'
|
||||
import { WebglAddon } from '@xterm/addon-webgl'
|
||||
import { Terminal } from '@xterm/xterm'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { CSSProperties } from 'react'
|
||||
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { useTheme } from '@/themes/context'
|
||||
|
||||
import { isAddSelectionShortcut, terminalSelectionAnchor, terminalSelectionLabel, terminalTheme } from './selection'
|
||||
|
||||
|
|
@ -184,6 +185,9 @@ function quotePathForShell(path: string, shellName: string): string {
|
|||
}
|
||||
|
||||
export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSessionOptions) {
|
||||
const { resolvedMode } = useTheme()
|
||||
const activeTheme = useMemo(() => terminalTheme(resolvedMode), [resolvedMode])
|
||||
const initialThemeRef = useRef(activeTheme)
|
||||
const hostRef = useRef<HTMLDivElement | null>(null)
|
||||
const termRef = useRef<Terminal | null>(null)
|
||||
const sessionIdRef = useRef<string | null>(null)
|
||||
|
|
@ -266,7 +270,7 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
|||
lineHeight: 1.12,
|
||||
macOptionIsMeta: true,
|
||||
scrollback: 1000,
|
||||
theme: terminalTheme()
|
||||
theme: initialThemeRef.current
|
||||
})
|
||||
|
||||
const fit = new FitAddon()
|
||||
|
|
@ -493,6 +497,14 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
|||
}
|
||||
}, [addSelectionToChat, cwd])
|
||||
|
||||
useEffect(() => {
|
||||
const term = termRef.current
|
||||
|
||||
if (term) {
|
||||
term.options.theme = activeTheme
|
||||
}
|
||||
}, [activeTheme])
|
||||
|
||||
return {
|
||||
addSelectionToChat,
|
||||
hostRef,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue