mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 14:22:51 +00:00
some things working
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
"@expo/vector-icons": "^15.0.2",
|
"@expo/vector-icons": "^15.0.2",
|
||||||
"@fressh/assets": "workspace:*",
|
"@fressh/assets": "workspace:*",
|
||||||
"@fressh/react-native-uniffi-russh": "workspace:*",
|
"@fressh/react-native-uniffi-russh": "workspace:*",
|
||||||
|
"@fressh/react-native-xtermjs-webview": "workspace:*",
|
||||||
"@react-native-segmented-control/segmented-control": "2.5.7",
|
"@react-native-segmented-control/segmented-control": "2.5.7",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-navigation/elements": "^2.6.4",
|
"@react-navigation/elements": "^2.6.4",
|
||||||
@@ -48,13 +49,13 @@
|
|||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
"expo-image": "~3.0.8",
|
"expo-image": "~3.0.8",
|
||||||
"expo-linking": "~8.0.8",
|
"expo-linking": "~8.0.8",
|
||||||
"@fressh/react-native-xtermjs-webview": "workspace:*",
|
|
||||||
"expo-router": "6.0.6",
|
"expo-router": "6.0.6",
|
||||||
"expo-secure-store": "~15.0.7",
|
"expo-secure-store": "~15.0.7",
|
||||||
"expo-splash-screen": "~31.0.10",
|
"expo-splash-screen": "~31.0.10",
|
||||||
"expo-status-bar": "~3.0.8",
|
"expo-status-bar": "~3.0.8",
|
||||||
"expo-symbols": "~1.0.7",
|
"expo-symbols": "~1.0.7",
|
||||||
"expo-system-ui": "~6.0.7",
|
"expo-system-ui": "~6.0.7",
|
||||||
|
"p-queue": "^8.1.1",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-native": "0.81.4",
|
"react-native": "0.81.4",
|
||||||
|
|||||||
@@ -5,22 +5,15 @@ import {
|
|||||||
type XtermWebViewHandle,
|
type XtermWebViewHandle,
|
||||||
} from '@fressh/react-native-xtermjs-webview';
|
} from '@fressh/react-native-xtermjs-webview';
|
||||||
|
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import PQueue from 'p-queue';
|
||||||
import {
|
import React, { useEffect, useRef } from 'react';
|
||||||
Platform,
|
import { Pressable, View } from 'react-native';
|
||||||
Pressable,
|
|
||||||
ScrollView,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
View,
|
|
||||||
} from 'react-native';
|
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import { disconnectSshConnectionAndInvalidateQuery } from '@/lib/query-fns';
|
||||||
import { useTheme } from '@/lib/theme';
|
import { useTheme } from '@/lib/theme';
|
||||||
|
|
||||||
const renderer: 'xtermjs' | 'rn-text' = 'xtermjs';
|
|
||||||
const decoder = new TextDecoder('utf-8');
|
|
||||||
|
|
||||||
export default function TabsShellDetail() {
|
export default function TabsShellDetail() {
|
||||||
return <ShellDetail />;
|
return <ShellDetail />;
|
||||||
}
|
}
|
||||||
@@ -43,29 +36,42 @@ function ShellDetail() {
|
|||||||
? RnRussh.getSshShell(String(connectionId), channelIdNum)
|
? RnRussh.getSshShell(String(connectionId), channelIdNum)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const [shellData, setShellData] = useState('');
|
function sendDataToXterm(data: ArrayBuffer) {
|
||||||
|
try {
|
||||||
|
const bytes = new Uint8Array(data);
|
||||||
|
console.log('sendDataToXterm', new TextDecoder().decode(bytes));
|
||||||
|
xtermWebViewRef.current?.write(bytes);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to decode shell data', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const queueRef = useRef<PQueue>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!connection) return;
|
if (!queueRef.current)
|
||||||
|
queueRef.current = new PQueue({
|
||||||
|
concurrency: 1,
|
||||||
|
intervalCap: 1, // <= one task per interval
|
||||||
|
interval: 100, // <= 100ms between tasks
|
||||||
|
autoStart: false, // <= buffer until we start()
|
||||||
|
});
|
||||||
|
const xtermQueue = queueRef.current;
|
||||||
|
if (!connection || !xtermQueue) return;
|
||||||
const listenerId = connection.addChannelListener((data: ArrayBuffer) => {
|
const listenerId = connection.addChannelListener((data: ArrayBuffer) => {
|
||||||
try {
|
console.log('ssh.onData', new TextDecoder().decode(new Uint8Array(data)));
|
||||||
const bytes = new Uint8Array(data);
|
void xtermQueue.add(() => {
|
||||||
xtermWebViewRef.current?.write(bytes);
|
sendDataToXterm(data);
|
||||||
const chunk = decoder.decode(bytes);
|
});
|
||||||
setShellData((prev) => prev + chunk);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Failed to decode shell data', e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
connection.removeChannelListener(listenerId);
|
connection.removeChannelListener(listenerId);
|
||||||
|
xtermQueue.pause();
|
||||||
|
xtermQueue.clear();
|
||||||
};
|
};
|
||||||
}, [connection]);
|
}, [connection, queueRef]);
|
||||||
|
|
||||||
const scrollViewRef = useRef<ScrollView | null>(null);
|
const queryClient = useQueryClient();
|
||||||
useEffect(() => {
|
|
||||||
scrollViewRef.current?.scrollToEnd({ animated: true });
|
|
||||||
}, [shellData]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1, backgroundColor: theme.colors.background }}>
|
<SafeAreaView style={{ flex: 1, backgroundColor: theme.colors.background }}>
|
||||||
@@ -77,9 +83,15 @@ function ShellDetail() {
|
|||||||
accessibilityLabel="Disconnect"
|
accessibilityLabel="Disconnect"
|
||||||
hitSlop={10}
|
hitSlop={10}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
|
if (!connection) return;
|
||||||
try {
|
try {
|
||||||
await connection?.disconnect();
|
await disconnectSshConnectionAndInvalidateQuery({
|
||||||
} catch {}
|
connectionId: connection.connectionId,
|
||||||
|
queryClient: queryClient,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to disconnect', e);
|
||||||
|
}
|
||||||
router.replace('/shell');
|
router.replace('/shell');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -94,140 +106,30 @@ function ShellDetail() {
|
|||||||
{ backgroundColor: theme.colors.background },
|
{ backgroundColor: theme.colors.background },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ScrollView>
|
<XtermJsWebView
|
||||||
{renderer === 'xtermjs' ? (
|
ref={xtermWebViewRef}
|
||||||
<XtermJsWebView
|
style={{ flex: 1, height: 400 }}
|
||||||
ref={xtermWebViewRef}
|
// textZoom={0}
|
||||||
style={{ flex: 1, height: 400 }}
|
injectedJavaScript={`
|
||||||
// textZoom={0}
|
document.body.style.backgroundColor = '${theme.colors.background}';
|
||||||
// injectedJavaScript={`
|
const termDiv = document.getElementById('terminal');
|
||||||
// setTimeout(() => {
|
window.terminal.options.fontSize = 50;
|
||||||
// document.body.style.backgroundColor = '${theme.colors.background}';
|
setTimeout(() => {
|
||||||
// document.body.style.color = '${theme.colors.textPrimary}';
|
window.fitAddon?.fit();
|
||||||
// document.body.style.fontSize = '80px';
|
}, 1_000);
|
||||||
// const termDiv = document.getElementById('terminal');
|
`}
|
||||||
// termDiv.style.backgroundColor = '${theme.colors.background}';
|
onMessage={(message) => {
|
||||||
// termDiv.style.color = '${theme.colors.textPrimary}';
|
if (message.type === 'initialized') {
|
||||||
// window.terminal.options.fontSize = 50;
|
console.log('xterm.onMessage initialized');
|
||||||
// }, 50);
|
queueRef.current?.start();
|
||||||
// `}
|
return;
|
||||||
onMessage={(event) => {
|
}
|
||||||
console.log('onMessage', event.nativeEvent.data);
|
const data = message.data;
|
||||||
}}
|
console.log('xterm.onMessage', new TextDecoder().decode(data));
|
||||||
/>
|
void shell?.sendData(data.buffer as ArrayBuffer);
|
||||||
) : (
|
}}
|
||||||
<View
|
/>
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#0E172B',
|
|
||||||
borderRadius: 12,
|
|
||||||
height: 400,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: '#2A3655',
|
|
||||||
overflow: 'hidden',
|
|
||||||
marginBottom: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ScrollView
|
|
||||||
ref={scrollViewRef}
|
|
||||||
contentContainerStyle={{
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingTop: 4,
|
|
||||||
paddingBottom: 12,
|
|
||||||
}}
|
|
||||||
keyboardShouldPersistTaps="handled"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
selectable
|
|
||||||
style={{
|
|
||||||
color: '#D1D5DB',
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: 18,
|
|
||||||
fontFamily: Platform.select({
|
|
||||||
ios: 'Menlo',
|
|
||||||
android: 'monospace',
|
|
||||||
default: 'monospace',
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{shellData || 'Connected. Output will appear here...'}
|
|
||||||
</Text>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<CommandInput
|
|
||||||
executeCommand={async (command) => {
|
|
||||||
await shell?.sendData(
|
|
||||||
Uint8Array.from(new TextEncoder().encode(command + '\n'))
|
|
||||||
.buffer,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CommandInput(props: {
|
|
||||||
executeCommand: (command: string) => Promise<void>;
|
|
||||||
}) {
|
|
||||||
const [command, setCommand] = useState('');
|
|
||||||
|
|
||||||
async function handleExecute() {
|
|
||||||
if (!command.trim()) return;
|
|
||||||
await props.executeCommand(command);
|
|
||||||
setCommand('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
||||||
<TextInput
|
|
||||||
testID="command-input"
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#0E172B',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: '#2A3655',
|
|
||||||
borderRadius: 10,
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 12,
|
|
||||||
color: '#E5E7EB',
|
|
||||||
fontSize: 16,
|
|
||||||
fontFamily: Platform.select({
|
|
||||||
ios: 'Menlo',
|
|
||||||
android: 'monospace',
|
|
||||||
default: 'monospace',
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
value={command}
|
|
||||||
onChangeText={setCommand}
|
|
||||||
placeholder="Type a command and press Enter or Execute"
|
|
||||||
placeholderTextColor="#9AA0A6"
|
|
||||||
autoCapitalize="none"
|
|
||||||
autoCorrect={false}
|
|
||||||
returnKeyType="send"
|
|
||||||
onSubmitEditing={handleExecute}
|
|
||||||
/>
|
|
||||||
<Pressable
|
|
||||||
style={[
|
|
||||||
{
|
|
||||||
backgroundColor: '#2563EB',
|
|
||||||
borderRadius: 10,
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
paddingVertical: 12,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
{ marginTop: 8 },
|
|
||||||
]}
|
|
||||||
onPress={handleExecute}
|
|
||||||
testID="execute-button"
|
|
||||||
>
|
|
||||||
<Text style={{ color: '#FFFFFF', fontWeight: '700', fontSize: 14 }}>
|
|
||||||
Execute
|
|
||||||
</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html style="margin: 0; padding: 0; width: 100vw; height: 100vh">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
</head>
|
</head>
|
||||||
<body style="margin: 0; padding: 0; width: 100%; height: 100%">
|
<body style="margin: 0; padding: 0; width: 100vw; height: 100vh">
|
||||||
<div
|
<div
|
||||||
id="terminal"
|
id="terminal"
|
||||||
style="margin: 0; padding: 0; width: 100%; height: 100%"
|
style="margin: 0; padding: 0; width: 100%; height: 100%"
|
||||||
|
|||||||
@@ -17,22 +17,24 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
|
"js-base64": "^3.7.8",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0"
|
"react-dom": "19.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@epic-web/config": "^1.21.3",
|
||||||
"@eslint/js": "^9.35.0",
|
"@eslint/js": "^9.35.0",
|
||||||
"@types/react": "~19.1.12",
|
"@types/react": "~19.1.12",
|
||||||
"@types/react-dom": "^19.1.7",
|
"@types/react-dom": "^19.1.7",
|
||||||
"@vitejs/plugin-react": "^5.0.2",
|
"@vitejs/plugin-react": "^5.0.2",
|
||||||
"eslint": "^9.35.0",
|
"eslint": "^9.35.0",
|
||||||
"prettier": "^3.6.2",
|
|
||||||
"prettier-plugin-organize-imports": "^4.2.0",
|
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
"globals": "^16.4.0",
|
"globals": "^16.4.0",
|
||||||
"@epic-web/config": "^1.21.3",
|
"prettier": "^3.6.2",
|
||||||
|
"prettier-plugin-organize-imports": "^4.2.0",
|
||||||
"typescript": "~5.9.2",
|
"typescript": "~5.9.2",
|
||||||
"typescript-eslint": "^8.44.0",
|
"typescript-eslint": "^8.44.0",
|
||||||
"vite": "6.3.6",
|
"vite": "6.3.6",
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
|
|
||||||
import '@xterm/xterm/css/xterm.css';
|
import '@xterm/xterm/css/xterm.css';
|
||||||
|
|
||||||
const decoder = new TextDecoder('utf-8');
|
|
||||||
|
|
||||||
const terminal = new Terminal();
|
const terminal = new Terminal();
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
terminal.loadAddon(fitAddon);
|
||||||
terminal.open(document.getElementById('terminal')!);
|
terminal.open(document.getElementById('terminal')!);
|
||||||
terminal.write('Hello from Xterm.js!');
|
fitAddon.fit();
|
||||||
window.terminal = terminal;
|
window.terminal = terminal;
|
||||||
|
window.fitAddon = fitAddon;
|
||||||
const postMessage = (arg: string) => {
|
const postMessage = (arg: string) => {
|
||||||
window.ReactNativeWebView?.postMessage?.(arg);
|
window.ReactNativeWebView?.postMessage?.(arg);
|
||||||
};
|
};
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
postMessage('DEBUG: set timeout');
|
postMessage('initialized');
|
||||||
}, 1000);
|
}, 10);
|
||||||
|
|
||||||
|
terminal.onData((data) => {
|
||||||
|
const base64Data = Base64.encode(data);
|
||||||
|
postMessage(base64Data);
|
||||||
|
});
|
||||||
function terminalWriteBase64(base64Data: string) {
|
function terminalWriteBase64(base64Data: string) {
|
||||||
try {
|
try {
|
||||||
postMessage(`DEBUG: terminalWriteBase64 ${base64Data}`);
|
const data = Base64.toUint8Array(base64Data);
|
||||||
const data = new Uint8Array(Buffer.from(base64Data, 'base64'));
|
|
||||||
postMessage(`DEBUG: terminalWriteBase64 decoded ${decoder.decode(data)}`);
|
|
||||||
|
|
||||||
terminal.write(data);
|
terminal.write(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
postMessage(`DEBUG: terminalWriteBase64 error ${e}`);
|
postMessage(`DEBUG: terminalWriteBase64 error ${e}`);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
terminal?: Terminal;
|
terminal?: Terminal;
|
||||||
|
fitAddon?: FitAddon;
|
||||||
terminalWriteBase64?: (data: string) => void;
|
terminalWriteBase64?: (data: string) => void;
|
||||||
ReactNativeWebView?: {
|
ReactNativeWebView?: {
|
||||||
postMessage?: (data: string) => void;
|
postMessage?: (data: string) => void;
|
||||||
|
|||||||
@@ -8,13 +8,19 @@ type StrictOmit<T, K extends keyof T> = Omit<T, K>;
|
|||||||
export type XtermWebViewHandle = {
|
export type XtermWebViewHandle = {
|
||||||
write: (data: Uint8Array) => void;
|
write: (data: Uint8Array) => void;
|
||||||
};
|
};
|
||||||
const decoder = new TextDecoder('utf-8');
|
|
||||||
|
|
||||||
export function XtermJsWebView({
|
export function XtermJsWebView({
|
||||||
ref,
|
ref,
|
||||||
|
onMessage,
|
||||||
...props
|
...props
|
||||||
}: StrictOmit<ComponentProps<typeof WebView>, 'source' | 'originWhitelist'> & {
|
}: StrictOmit<
|
||||||
|
ComponentProps<typeof WebView>,
|
||||||
|
'source' | 'originWhitelist' | 'onMessage'
|
||||||
|
> & {
|
||||||
ref: React.RefObject<XtermWebViewHandle | null>;
|
ref: React.RefObject<XtermWebViewHandle | null>;
|
||||||
|
onMessage?: (
|
||||||
|
data: { type: 'data'; data: Uint8Array } | { type: 'initialized' },
|
||||||
|
) => void;
|
||||||
}) {
|
}) {
|
||||||
const webViewRef = useRef<WebView>(null);
|
const webViewRef = useRef<WebView>(null);
|
||||||
|
|
||||||
@@ -22,17 +28,8 @@ export function XtermJsWebView({
|
|||||||
return {
|
return {
|
||||||
write: (data) => {
|
write: (data) => {
|
||||||
const base64Data = Base64.fromUint8Array(data);
|
const base64Data = Base64.fromUint8Array(data);
|
||||||
console.log('writing rn side', {
|
|
||||||
base64Data,
|
|
||||||
dataLength: data.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
'try to decode',
|
|
||||||
decoder.decode(Base64.toUint8Array(base64Data)),
|
|
||||||
);
|
|
||||||
webViewRef.current?.injectJavaScript(`
|
webViewRef.current?.injectJavaScript(`
|
||||||
window?.terminalWriteBase64('${base64Data}');
|
window?.terminalWriteBase64?.('${base64Data}');
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -43,6 +40,15 @@ export function XtermJsWebView({
|
|||||||
ref={webViewRef}
|
ref={webViewRef}
|
||||||
originWhitelist={['*']}
|
originWhitelist={['*']}
|
||||||
source={{ html: htmlString }}
|
source={{ html: htmlString }}
|
||||||
|
onMessage={(event) => {
|
||||||
|
const message = event.nativeEvent.data;
|
||||||
|
if (message === 'initialized') {
|
||||||
|
onMessage?.({ type: 'initialized' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = Base64.toUint8Array(message);
|
||||||
|
onMessage?.({ type: 'data', data });
|
||||||
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -130,6 +130,9 @@ importers:
|
|||||||
expo-system-ui:
|
expo-system-ui:
|
||||||
specifier: ~6.0.7
|
specifier: ~6.0.7
|
||||||
version: 6.0.7(expo@54.0.8)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
|
version: 6.0.7(expo@54.0.8)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
|
||||||
|
p-queue:
|
||||||
|
specifier: ^8.1.1
|
||||||
|
version: 8.1.1
|
||||||
react:
|
react:
|
||||||
specifier: 19.1.0
|
specifier: 19.1.0
|
||||||
version: 19.1.0
|
version: 19.1.0
|
||||||
@@ -385,9 +388,15 @@ importers:
|
|||||||
|
|
||||||
packages/react-native-xtermjs-webview-internal:
|
packages/react-native-xtermjs-webview-internal:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@xterm/addon-fit':
|
||||||
|
specifier: ^0.10.0
|
||||||
|
version: 0.10.0(@xterm/xterm@5.5.0)
|
||||||
'@xterm/xterm':
|
'@xterm/xterm':
|
||||||
specifier: ^5.5.0
|
specifier: ^5.5.0
|
||||||
version: 5.5.0
|
version: 5.5.0
|
||||||
|
js-base64:
|
||||||
|
specifier: ^3.7.8
|
||||||
|
version: 3.7.8
|
||||||
react:
|
react:
|
||||||
specifier: 19.1.0
|
specifier: 19.1.0
|
||||||
version: 19.1.0
|
version: 19.1.0
|
||||||
@@ -3512,6 +3521,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
|
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
|
'@xterm/addon-fit@0.10.0':
|
||||||
|
resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@xterm/xterm': ^5.0.0
|
||||||
|
|
||||||
'@xterm/xterm@5.5.0':
|
'@xterm/xterm@5.5.0':
|
||||||
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
|
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
|
||||||
|
|
||||||
@@ -13028,6 +13042,10 @@ snapshots:
|
|||||||
|
|
||||||
'@xmldom/xmldom@0.8.11': {}
|
'@xmldom/xmldom@0.8.11': {}
|
||||||
|
|
||||||
|
'@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)':
|
||||||
|
dependencies:
|
||||||
|
'@xterm/xterm': 5.5.0
|
||||||
|
|
||||||
'@xterm/xterm@5.5.0': {}
|
'@xterm/xterm@5.5.0': {}
|
||||||
|
|
||||||
abbrev@3.0.1: {}
|
abbrev@3.0.1: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user