mirror of
https://github.com/Termix-SSH/Mobile.git
synced 2026-07-28 05:51:43 +02:00
6c40d2f0cc
* 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>
96 lines
2.5 KiB
TypeScript
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>
|
|
);
|
|
}
|