Files
Luke Gustafson 6c40d2f0cc v1.4.0 (#31)
* fix: register physical Tab key on iPadOS/iOS hardware keyboards (#14)

The native module only registered Shift+Tab but not bare Tab, so iOS
intercepted it for UI focus navigation. Register an unmodified Tab
UIKeyCommand with wantsPriorityOverSystemBehavior and handle both
Tab and Shift+Tab in the same JS branch.

Fixes Termix-SSH/Support#557

* fix: pass jumpHosts, forceKeyboardInteractive, and overrideCredentialUsername to terminal connection (#15)

These fields were defined on SSHHost but omitted from the terminal
hostConfig passthrough. Without jumpHosts the backend cannot route
through jump servers; without forceKeyboardInteractive hosts that
require keyboard-interactive auth fail silently; without
overrideCredentialUsername shared credentials use the wrong username.

Fixes Termix-SSH/Support#422
Fixes Termix-SSH/Support#638

* fix: improve mobile terminal scroll recovery (#17)

* Add mobile remote desktop sessions (#18)

* feat: add mobile remote desktop sessions

* feat: add remote desktop input controls

* feat: polish remote desktop controls

* feat: add mobile terminal font selection (#19)

* feat: add mobile shift tab hotkey (#20)

* feat: initial UI redesign

* feat: revamp server login system

* chore: update app icon/background color

* fix: remove unused fields in the settings tab

* feat: improve the user pin system and update all settings modals to use new ui

* fix: active server not updating unless app restarts

* feat: add version in settings

* feat: improve host page UI (updated host form and added credential management)

* fix: preserve composed iOS terminal input

* ci: restore mobile checks

* feat: rewrite of connection system for all types

* fix: preserve mobile jump hosts (#30)

* fix: guard android user ca trust (#29)

* fix: capture ios hardware tab key (#27)

* fix: reset terminal input after special keys (#28)

* fix: open oidc in system browser (#26)

* fix: preserve websocket auth context (#25)

* fix: keep none-auth terminal prompts alive (#23)

* fix: allow local network SSL in Android app (#21)

* feat: allow oidc passkeys

* feat: bundeled xterm into app and added docker loading screen

* feat: add 2fa, api key, and active sessions to settings and addressed multiple bug fixes and inconsistencies with termix web

* chore: update repo images and readme

* chore: update readme to split table for screenshots

* chore: rename ios to apple in build workflow

* fix: android build failure

* feat: add snippet management and fix several bugs

* fix: web login and oidc incorrect logic

* chore: run linter

---------

Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:49:50 -05:00

79 lines
2.5 KiB
TypeScript

import { Pressable, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import type { BottomTabBarProps } from "@react-navigation/bottom-tabs";
import { Server, SquareTerminal, Settings } from "lucide-react-native";
import { Text } from "@/app/components/ui";
import { useThemeColor } from "@/app/contexts/ThemeContext";
import { useOrientation } from "@/app/utils/orientation";
const ICONS: Record<
string,
(props: { color: string; size: number }) => React.ReactNode
> = {
hosts: (p) => <Server {...p} />,
sessions: (p) => <SquareTerminal {...p} />,
settings: (p) => <Settings {...p} />,
};
const LABELS: Record<string, string> = {
hosts: "Hosts",
sessions: "Sessions",
settings: "Settings",
};
/**
* Bottom tab bar matching the web MobileBottomBar: square, bg-sidebar,
* top border, accent-colored active item, lucide icons, safe-area aware.
*/
export function CustomTabBar({ state, navigation }: BottomTabBarProps) {
const insets = useSafeAreaInsets();
const { isLandscape } = useOrientation();
const resolveColor = useThemeColor();
const accent = resolveColor("accent-brand") ?? "#f59145";
const muted = resolveColor("muted-foreground") ?? "#a4a4a4";
const iconSize = isLandscape ? 18 : 20;
return (
<View
className="flex-row items-stretch border-t border-border bg-sidebar"
style={{ paddingBottom: insets.bottom }}
>
{state.routes.map((route, index) => {
const focused = state.index === index;
const color = focused ? accent : muted;
const icon = ICONS[route.name];
const label = LABELS[route.name] ?? route.name;
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: route.key,
canPreventDefault: true,
});
if (!focused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
return (
<Pressable
key={route.key}
onPress={onPress}
className="flex-1 items-center justify-center gap-0.5"
style={{ minHeight: isLandscape ? 48 : 56, paddingVertical: 6 }}
>
{icon?.({ color, size: iconSize })}
<Text
weight={focused ? "medium" : "regular"}
className="text-[10px]"
style={{ color }}
>
{label}
</Text>
</Pressable>
);
})}
</View>
);
}