This commit is contained in:
EthanShoeDev
2025-09-19 23:41:17 -04:00
parent f0321c48e7
commit e1d8dc76c9
7 changed files with 39 additions and 23 deletions

View File

@@ -94,7 +94,7 @@ if (window.__FRESSH_XTERM_BRIDGE__) {
break;
}
case 'setOptions': {
const newOpts: ITerminalOptions = {
const newOpts: ITerminalOptions & { cols?: never; rows?: never } = {
...term.options,
...msg.opts,
theme: {
@@ -102,6 +102,8 @@ if (window.__FRESSH_XTERM_BRIDGE__) {
...msg.opts.theme,
},
};
delete newOpts.cols;
delete newOpts.rows;
term.options = newOpts;
if (
'theme' in newOpts &&
@@ -109,7 +111,7 @@ if (window.__FRESSH_XTERM_BRIDGE__) {
'background' in newOpts.theme &&
newOpts.theme.background
) {
document.body.style.backgroundColor = 'blue'; // TODO: Just for debugging
document.body.style.backgroundColor = newOpts.theme.background;
}
break;
}

View File

@@ -1,5 +1,6 @@
import { Base64 } from 'js-base64';
type ITerminalOptions = import('@xterm/xterm').ITerminalOptions;
type ITerminalInitOnlyOptions = import('@xterm/xterm').ITerminalInitOnlyOptions;
// Messages posted from the WebView (xterm page) to React Native
export type BridgeInboundMessage =
| { type: 'initialized' }
@@ -12,7 +13,11 @@ export type BridgeOutboundMessage =
| { type: 'writeMany'; chunks: string[] }
| { type: 'resize'; cols: number; rows: number }
| { type: 'fit' }
| { type: 'setOptions'; opts: Partial<ITerminalOptions> }
| {
type: 'setOptions'; opts: Partial<
Omit<ITerminalOptions, keyof ITerminalInitOnlyOptions>
>
}
| { type: 'clear' }
| { type: 'focus' };

View File

@@ -4,6 +4,7 @@ import React, {
useMemo,
useRef,
useCallback,
useState,
} from 'react';
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
import htmlString from '../dist-internal/index.html?raw';
@@ -63,7 +64,7 @@ const defaultWebViewProps: WebViewOptions = {
const defaultXtermOptions: Partial<ITerminalOptions> = {
fontFamily: 'Menlo, ui-monospace, monospace',
fontSize: 80,
fontSize: 20,
cursorBlink: true,
scrollback: 10000,
};
@@ -125,6 +126,7 @@ export function XtermJsWebView({
autoFit = true,
}: XtermJsWebViewProps) {
const webRef = useRef<WebView>(null);
const [initialized, setInitialized] = useState(false);
// ---- RN -> WebView message sender
const sendToWebView = useCallback(
@@ -132,7 +134,7 @@ export function XtermJsWebView({
const webViewRef = webRef.current;
if (!webViewRef) return;
const payload = JSON.stringify(obj);
logger?.log?.(`sending msg to webview: ${payload}`);
logger?.debug?.(`sending msg to webview: ${payload}`);
const js = `window.dispatchEvent(new MessageEvent('message',{data:${payload}})); true;`;
webViewRef.injectJavaScript(js);
},
@@ -211,6 +213,7 @@ export function XtermJsWebView({
const appliedSizeRef = useRef<{ cols: number; rows: number } | null>(null);
useEffect(() => {
if (!initialized) return;
const appliedSize = appliedSizeRef.current;
if (!size) return;
if (appliedSize?.cols === size.cols && appliedSize?.rows === size.rows)
@@ -221,7 +224,7 @@ export function XtermJsWebView({
autoFitFn();
appliedSizeRef.current = size;
}, [size, sendToWebView, logger, autoFitFn]);
}, [size, sendToWebView, logger, autoFitFn, initialized]);
useImperativeHandle(ref, () => ({
write,
@@ -251,13 +254,14 @@ export function XtermJsWebView({
const appliedXtermOptionsRef = useRef<Partial<ITerminalOptions> | null>(null);
useEffect(() => {
if (!initialized) return;
const appliedXtermOptions = appliedXtermOptionsRef.current;
if (xTermOptionsEquals(appliedXtermOptions, mergedXTermOptions)) return;
logger?.log?.(`setting options: `, mergedXTermOptions);
sendToWebView({ type: 'setOptions', opts: mergedXTermOptions });
appliedXtermOptionsRef.current = mergedXTermOptions;
}, [mergedXTermOptions, sendToWebView, logger]);
}, [mergedXTermOptions, sendToWebView, logger, initialized]);
const onMessage = useCallback(
(e: WebViewMessageEvent) => {
@@ -267,6 +271,7 @@ export function XtermJsWebView({
if (msg.type === 'initialized') {
onInitialized?.();
autoFitFn();
setInitialized(true);
return;
}
if (msg.type === 'input') {

View File

@@ -8,12 +8,12 @@
// Special tasks
"build:main": {
"inputs": ["src/**"],
"inputs": ["src/**", "vite.config.ts"],
"dependsOn": ["build:internal"],
"outputs": ["dist/**"],
},
"build:internal": {
"inputs": ["src-internal/**"],
"inputs": ["src-internal/**", "index.html", "vite.config.internal.ts"],
"outputs": ["dist-internal/**"],
},
},