1import { BranchNamingMode, type ServerSettingsPatch } from "@t3tools/contracts";
2import { useRef } from "react";
3import { View } from "react-native";
4
5import { AppText as Text, AppTextInput } from "../../../components/AppText";
6import { SettingsChoiceRow } from "./SettingsChoiceRow";
7import { SettingsSection } from "./SettingsSection";
8
9const MODES = {
10 static: { label: "Static prefix", description: "Add your prefix to the generated branch name." },
11 semantic: {
12 label: "Semantic prefix",
13 description: "Let the model choose feat/, fix/, refactor/, or another prefix.",
14 },
15 custom: {
16 label: "Custom instructions",
17 description: "Generate the complete name with no added prefix or suffix.",
18 },
19} satisfies Record<BranchNamingMode, { label: string; description: string }>;
20
21export function BranchNamingSettings(props: {
22 mode: BranchNamingMode | null;
23 prefix: string | null;
24 instructions: string | null;
25 disabled: boolean;
26 onChange: (patch: ServerSettingsPatch) => void;
27}) {
28 const prefixEdited = useRef(false);
29 const instructionsEdited = useRef(false);
30 return (
31 <SettingsSection
32 title="Worktree branch naming"
33 trailing={
34 props.mode === null ? <Text className="text-xs text-foreground-muted">Mixed</Text> : null
35 }
36 >
37 {BranchNamingMode.literals.map((mode, index) => (
38 <SettingsChoiceRow
39 key={mode}
40 label={MODES[mode].label}
41 description={MODES[mode].description}
42 selected={props.mode === mode}
43 separated={index > 0}
44 disabled={props.disabled}
45 onPress={() => props.onChange({ branchNamingMode: mode })}
46 />
47 ))}
48 {props.mode === "static" ? (
49 <View className="gap-2 px-4 py-3">
50 <Text className="text-sm text-foreground-muted">
51 Use t3 or t3/ for t3/add-search. Leave empty for no prefix.
52 </Text>
53 <AppTextInput
54 key={props.prefix}
55 accessibilityLabel="Branch prefix"
56 onChangeText={() => {
57 prefixEdited.current = true;
58 }}
59 defaultValue={props.prefix ?? ""}
60 placeholder={props.prefix === null ? "Mixed" : "No prefix"}
61 editable={!props.disabled}
62 autoCapitalize="none"
63 autoCorrect={false}
64 className="min-h-10 rounded-xl px-3 py-2 text-base text-foreground"
65 onEndEditing={(event) => {
66 const value = event.nativeEvent.text.trim();
67 if (
68 !props.disabled &&
69 prefixEdited.current &&
70 (props.prefix === null || value !== props.prefix)
71 )
72 props.onChange({ branchNamePrefix: value });
73 prefixEdited.current = false;
74 }}
75 />
76 </View>
77 ) : null}
78 {props.mode === "custom" ? (
79 <View className="gap-2 px-4 py-3">
80 <Text className="text-sm text-foreground-muted">
81 Append instructions to the naming prompt.
82 </Text>
83 <AppTextInput
84 key={props.instructions}
85 accessibilityLabel="Branch naming instructions"
86 onChangeText={() => {
87 instructionsEdited.current = true;
88 }}
89 defaultValue={props.instructions ?? ""}
90 placeholder={
91 props.instructions === null
92 ? "Mixed. Enter instructions for all selected targets."
93 : "Use julius/ followed by the issue ID and a short description."
94 }
95 editable={!props.disabled}
96 multiline
97 autoCapitalize="sentences"
98 className="min-h-24 rounded-xl px-3 py-2 text-base text-foreground"
99 onEndEditing={(event) => {
100 const value = event.nativeEvent.text.trim();
101 if (
102 !props.disabled &&
103 instructionsEdited.current &&
104 (props.instructions === null || value !== props.instructions)
105 )
106 props.onChange({ branchNameInstructions: value });
107 instructionsEdited.current = false;
108 }}
109 />
110 </View>
111 ) : null}
112 </SettingsSection>
113 );
114}