diff --git a/frontend/app/app.tsx b/frontend/app/app.tsx index f2bb79b09..3859a2168 100644 --- a/frontend/app/app.tsx +++ b/frontend/app/app.tsx @@ -6,11 +6,11 @@ import { ContextMenuModel } from "@/store/contextmenu"; import { atoms, createBlock, + getSettingsPrefixAtom, globalStore, isDev, PLATFORM, removeFlashError, - useSettingsPrefixAtom, } from "@/store/global"; import { appHandleKeyDown } from "@/store/keymodel"; import { getElemAsStr } from "@/util/focusutil"; @@ -123,7 +123,7 @@ async function handleContextMenu(e: React.MouseEvent) { } function AppSettingsUpdater() { - const windowSettingsAtom = useSettingsPrefixAtom("window"); + const windowSettingsAtom = getSettingsPrefixAtom("window"); const windowSettings = useAtomValue(windowSettingsAtom); useEffect(() => { const isTransparentOrBlur = diff --git a/frontend/app/element/termelem/termelem.tsx b/frontend/app/element/termelem/termelem.tsx index 3ee0a8cb6..041e4f6ad 100644 --- a/frontend/app/element/termelem/termelem.tsx +++ b/frontend/app/element/termelem/termelem.tsx @@ -35,7 +35,10 @@ export type TermWrapOptions = { useWebGl?: boolean; useWebLinksAddon?: boolean; useSerializeAddon?: boolean; - termTheme: Atom; + termThemeAtom: Atom; + termFontSize: Atom; + termFontFamily: Atom; + termScrollback: Atom; onOpenLink?: (uri: string) => void; onCwdChange?: (newCwd: string) => void; handleInputData?: (data: string) => void; @@ -212,18 +215,27 @@ export class TermWrap { export const TermElem = (props: { termOpts: TermWrapOptions }) => { const connectElemRef = useRef(null); const termWrapRef = useRef(null); - const termTheme = useAtomValueSafe(props.termOpts.termTheme); + const termTheme = useAtomValueSafe(props.termOpts.termThemeAtom); + const termFontSize = useAtomValueSafe(props.termOpts.termFontSize) ?? 12; + const termFontFamily = useAtomValueSafe(props.termOpts.termFontFamily) ?? "Hack"; + const termScrollback = useAtomValueSafe(props.termOpts.termScrollback) ?? 1000; useEffect(() => { if (termWrapRef.current == null || termTheme == null) { return; } termWrapRef.current.terminal.options.theme = termTheme; - }, [termTheme]); + termWrapRef.current.terminal.options.fontSize = termFontSize; + termWrapRef.current.terminal.options.fontFamily = termFontFamily; + termWrapRef.current.terminal.options.scrollback = termScrollback; + }, [termTheme, termFontSize, termFontFamily, termScrollback]); useEffect(() => { termWrapRef.current = new TermWrap(props.termOpts); if (termTheme != null) { termWrapRef.current.terminal.options.theme = termTheme; } + termWrapRef.current.terminal.options.fontSize = termFontSize; + termWrapRef.current.terminal.options.fontFamily = termFontFamily; + termWrapRef.current.terminal.options.scrollback = termScrollback; termWrapRef.current.initTerminal(connectElemRef.current); return () => { termWrapRef.current.dispose(); diff --git a/frontend/app/store/global.ts b/frontend/app/store/global.ts index 53fe90da3..f5a43b773 100644 --- a/frontend/app/store/global.ts +++ b/frontend/app/store/global.ts @@ -293,7 +293,7 @@ function useSettingsKeyAtom(key: T): SettingsType[ return useAtomValue(getSettingsKeyAtom(key)); } -function useSettingsPrefixAtom(prefix: string): Atom { +function getSettingsPrefixAtom(prefix: string): Atom { // TODO: use a shallow equal here to make this more efficient let settingsPrefixAtom = settingsAtomCache.get(prefix + ":") as Atom; if (settingsPrefixAtom == null) { @@ -638,6 +638,7 @@ export { getObjectId, getOverrideConfigAtom, getSettingsKeyAtom, + getSettingsPrefixAtom, getUserName, globalStore, initGlobal, @@ -663,6 +664,5 @@ export { useBlockMetaKeyAtom, useOverrideConfigAtom, useSettingsKeyAtom, - useSettingsPrefixAtom, WOS, }; diff --git a/frontend/app/view/term/term.tsx b/frontend/app/view/term/term.tsx index 66474b5fb..41954112b 100644 --- a/frontend/app/view/term/term.tsx +++ b/frontend/app/view/term/term.tsx @@ -3,6 +3,7 @@ import { Block, SubBlock } from "@/app/block/block"; import { BlockNodeModel } from "@/app/block/blocktypes"; +import { TermWrapOptions } from "@/app/element/termelem/termelem"; import { getAllGlobalKeyBindings } from "@/app/store/keymodel"; import { waveEventSubscribe } from "@/app/store/wps"; import { RpcApi } from "@/app/store/wshclientapi"; @@ -17,13 +18,14 @@ import { getConnStatusAtom, getOverrideConfigAtom, getSettingsKeyAtom, + getSettingsPrefixAtom, globalStore, useBlockAtom, - useSettingsPrefixAtom, WOS, } from "@/store/global"; import * as services from "@/store/services"; import * as keyutil from "@/util/keyutil"; +import * as TermTypes from "@xterm/xterm"; import clsx from "clsx"; import debug from "debug"; import * as jotai from "jotai"; @@ -64,6 +66,7 @@ class TermViewModel { termThemeNameAtom: jotai.Atom; noPadding: jotai.PrimitiveAtom; endIconButtons: jotai.Atom; + termThemeAtom: jotai.Atom; constructor(blockId: string, nodeModel: BlockNodeModel) { this.viewType = "term"; @@ -187,6 +190,32 @@ class TermViewModel { }, ]; }); + this.termThemeAtom = jotai.atom((get) => { + const fullConfig = get(atoms.fullConfigAtom); + const termThemeName = get(this.termThemeNameAtom); + const [theme, _] = computeTheme(fullConfig, termThemeName); + return theme; + }); + } + + getTermScrollback(): number { + const settingsPrefixAtom = getSettingsPrefixAtom("term"); + const termSettings = globalStore.get(settingsPrefixAtom); + const blockData = globalStore.get(this.blockAtom); + let termScrollback = 1000; + if (termSettings?.["term:scrollback"]) { + termScrollback = Math.floor(termSettings["term:scrollback"]); + } + if (blockData?.meta?.["term:scrollback"]) { + termScrollback = Math.floor(blockData.meta["term:scrollback"]); + } + if (termScrollback < 0) { + termScrollback = 0; + } + if (termScrollback > 10000) { + termScrollback = 10000; + } + return termScrollback; } setTermMode(mode: "term" | "vdom") { @@ -541,7 +570,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => { const spstatusRef = React.useRef(null); model.shellProcStatusRef = spstatusRef; const [blockData] = WOS.useWaveObjectValue(WOS.makeORef("block", blockId)); - const termSettingsAtom = useSettingsPrefixAtom("term"); + const termSettingsAtom = getSettingsPrefixAtom("term"); const termSettings = jotai.useAtomValue(termSettingsAtom); let termMode = blockData?.meta?.["term:mode"] ?? "term"; if (termMode != "term" && termMode != "vdom") { @@ -549,7 +578,6 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => { } const termModeRef = React.useRef(termMode); - const termFontSize = jotai.useAtomValue(model.fontSizeAtom); React.useEffect(() => { const fullConfig = globalStore.get(atoms.fullConfigAtom); @@ -648,6 +676,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => { cols: termRef.current?.terminal.cols ?? 80, blockId: blockId, }; + return (
diff --git a/frontend/app/view/term/termelemwrap.tsx b/frontend/app/view/term/termelemwrap.tsx new file mode 100644 index 000000000..5384fc484 --- /dev/null +++ b/frontend/app/view/term/termelemwrap.tsx @@ -0,0 +1,33 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { getOverrideConfigAtom, getSettingsPrefixAtom, openLink } from "@/app/store/global"; +import { TermViewModel } from "@/app/view/term/term"; +import { TermElem, TermWrapOptions } from "@/element/termelem/termelem"; +import { useAtomValue } from "jotai"; + +export function TermWrapElem({ blockId, model }: { blockId: string; model: TermViewModel }) { + const termFontSize = useAtomValue(model.fontSizeAtom); + const termSettingsAtom = getSettingsPrefixAtom("term"); + const termSettings = useAtomValue(termSettingsAtom); + const termFontSizeAtom = getOverrideConfigAtom(blockId, "term:fontsize"); + const termFontFamilyAtom = getOverrideConfigAtom(blockId, "term:fontfamily"); + + let termOpts: TermWrapOptions = { + xtermOpts: { + drawBoldTextInBrightColors: false, + fontWeight: "normal", + fontWeightBold: "bold", + allowTransparency: true, + }, + useWebGl: !termSettings?.["term:disablewebgl"], + useWebLinksAddon: true, + useSerializeAddon: true, + onOpenLink: openLink, + keydownHandler: model.handleTerminalKeydown.bind(model), + termThemeAtom: model.termThemeAtom, + termFontFamily: termFontFamilyAtom, + termFontSize: termFontSizeAtom, + }; + return ; +}