Keep sheet text inputs native-owned to avoid React Native controlled input flicker during fast typing. Add reset keys for fields that need programmatic clears, and move model, mode, and thinking pickers onto shared combobox rows with fuzzy ranked search and native virtualization.
102 lines
3.2 KiB
TypeScript
102 lines
3.2 KiB
TypeScript
import { useCallback, useMemo } from "react";
|
|
import { Text, View } from "react-native";
|
|
import { StyleSheet } from "react-native-unistyles";
|
|
import { getIsElectronRuntime } from "@/constants/layout";
|
|
import { AdaptiveModalSheet, type SheetHeader } from "@/components/adaptive-modal-sheet";
|
|
import { Shortcut } from "@/components/ui/shortcut";
|
|
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
|
|
import { getShortcutOs } from "@/utils/shortcut-platform";
|
|
import { buildKeyboardShortcutHelpSections } from "@/keyboard/keyboard-shortcuts";
|
|
|
|
const SNAP_POINTS: string[] = ["70%", "92%"];
|
|
const SHORTCUTS_HEADER: SheetHeader = { title: "Shortcuts" };
|
|
|
|
export function KeyboardShortcutsDialog() {
|
|
const open = useKeyboardShortcutsStore((s) => s.shortcutsDialogOpen);
|
|
const setOpen = useKeyboardShortcutsStore((s) => s.setShortcutsDialogOpen);
|
|
|
|
const isMac = getShortcutOs() === "mac";
|
|
const isDesktopApp = getIsElectronRuntime();
|
|
const sections = useMemo(
|
|
() => buildKeyboardShortcutHelpSections({ isMac, isDesktop: isDesktopApp }),
|
|
[isDesktopApp, isMac],
|
|
);
|
|
|
|
const handleClose = useCallback(() => setOpen(false), [setOpen]);
|
|
|
|
return (
|
|
<AdaptiveModalSheet
|
|
header={SHORTCUTS_HEADER}
|
|
visible={open}
|
|
onClose={handleClose}
|
|
testID="keyboard-shortcuts-dialog"
|
|
snapPoints={SNAP_POINTS}
|
|
>
|
|
<View testID="keyboard-shortcuts-dialog-content" style={styles.content}>
|
|
{sections.map((section) => (
|
|
<View key={section.title} style={styles.section}>
|
|
<Text style={styles.sectionTitle}>{section.title}</Text>
|
|
<View style={styles.rows}>
|
|
{section.rows.map((row) => (
|
|
<View key={row.id} style={styles.row}>
|
|
<View style={styles.rowText}>
|
|
<Text style={styles.rowLabel}>{row.label}</Text>
|
|
{row.note ? <Text style={styles.rowNote}>{row.note}</Text> : null}
|
|
</View>
|
|
<Shortcut keys={row.keys} style={styles.rowShortcut} />
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</AdaptiveModalSheet>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create((theme) => ({
|
|
content: {
|
|
gap: theme.spacing[4],
|
|
},
|
|
section: {
|
|
gap: theme.spacing[2],
|
|
},
|
|
sectionTitle: {
|
|
fontSize: theme.fontSize.xs,
|
|
fontWeight: theme.fontWeight.medium,
|
|
color: theme.colors.foregroundMuted,
|
|
},
|
|
rows: {
|
|
borderWidth: theme.borderWidth[1],
|
|
borderColor: theme.colors.surface2,
|
|
borderRadius: theme.borderRadius.lg,
|
|
overflow: "hidden",
|
|
},
|
|
row: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
gap: theme.spacing[3],
|
|
paddingHorizontal: theme.spacing[4],
|
|
paddingVertical: theme.spacing[3],
|
|
borderBottomWidth: theme.borderWidth[1],
|
|
borderBottomColor: theme.colors.surface2,
|
|
},
|
|
rowText: {
|
|
flex: 1,
|
|
minWidth: 0,
|
|
},
|
|
rowLabel: {
|
|
fontSize: theme.fontSize.sm,
|
|
color: theme.colors.foreground,
|
|
},
|
|
rowNote: {
|
|
marginTop: 2,
|
|
fontSize: theme.fontSize.xs,
|
|
color: theme.colors.foregroundMuted,
|
|
},
|
|
rowShortcut: {
|
|
alignSelf: "flex-start",
|
|
},
|
|
}));
|