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

96 lines
2.5 KiB
TypeScript

import {
Pressable,
type PressableProps,
View,
ActivityIndicator,
} from "react-native";
import { Text } from "./Text";
import { useThemeColor } from "@/app/contexts/ThemeContext";
export type ButtonVariant =
| "default" // solid primary
| "accent" // accent-brand outline (primary action in the web design)
| "outline"
| "ghost"
| "destructive";
export type ButtonSize = "sm" | "default" | "lg" | "icon";
interface ButtonProps extends Omit<PressableProps, "children"> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
className?: string;
textClassName?: string;
children?: React.ReactNode;
/** Leading icon element (already colored, or it inherits via color prop). */
icon?: React.ReactNode;
}
const SIZES: Record<ButtonSize, string> = {
sm: "h-8 px-2.5",
default: "h-10 px-3",
lg: "h-12 px-4",
icon: "h-10 w-10",
};
const VARIANT_CONTAINER: Record<ButtonVariant, string> = {
default: "bg-primary border border-primary",
accent: "bg-accent-brand/10 border border-accent-brand/40",
outline: "bg-transparent border border-border",
ghost: "bg-transparent border border-transparent",
destructive: "bg-transparent border border-destructive/40",
};
const VARIANT_TEXT: Record<ButtonVariant, string> = {
default: "text-primary-foreground",
accent: "text-accent-brand",
outline: "text-foreground",
ghost: "text-foreground",
destructive: "text-destructive",
};
export function Button({
variant = "outline",
size = "default",
loading = false,
disabled,
className,
textClassName,
children,
icon,
...props
}: ButtonProps) {
const accent = useThemeColor()("accent-brand");
const isDisabled = disabled || loading;
return (
<Pressable
disabled={isDisabled}
className={`flex-row items-center justify-center gap-1.5 ${SIZES[size]} ${VARIANT_CONTAINER[variant]} ${isDisabled ? "opacity-50" : "active:opacity-80"} ${className ?? ""}`}
{...props}
>
{loading ? (
<ActivityIndicator
size="small"
color={variant === "default" ? undefined : accent}
/>
) : (
<>
{icon ? <View className="shrink-0">{icon}</View> : null}
{typeof children === "string" ? (
<Text
weight="medium"
className={`text-xs ${VARIANT_TEXT[variant]} ${textClassName ?? ""}`}
>
{children}
</Text>
) : (
children
)}
</>
)}
</Pressable>
);
}