import { useAtomValue } from "@effect/atom-react";import { useRef } from "react";import type { SourceControlWritingStyleMode } from "@t3tools/contracts";import { DEFAULT_UNIFIED_SETTINGS } from "@t3tools/contracts/settings";import { createModelSelection } from "@t3tools/shared/model";import { resolveSourceControlWriterModelSelection } from "@t3tools/shared/serverSettings"; import { usePrimarySettings, useUpdatePrimarySettings } from "../../hooks/useSettings";import { applyProviderInstanceSettings, deriveProviderInstanceEntries, sortProviderInstanceEntries,} from "../../providerInstances";import { getCustomModelOptionsByInstance, resolveAppModelSelectionState,} from "../../modelSelection";import { primaryServerProvidersAtom } from "../../state/server";import { ProviderModelPicker } from "../chat/ProviderModelPicker";import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";import { Switch } from "../ui/switch";import { Textarea } from "../ui/textarea";import { SettingResetButton, SettingsRow, SettingsSection } from "./settingsLayout"; const MODE_OPTIONS: Record<SourceControlWritingStyleMode, { label: string; description: string }> = { repo_conventions: { label: "Repository conventions", description: "In each project, matches recent change descriptions and change request titles.", }, conventional_commits: { label: "Conventional Commits", description: "Uses Conventional Commit prefixes for change descriptions; change request titles and descriptions stay concise.", }, custom: { label: "Custom instructions", description: "Applies your instructions to change descriptions and change request titles and descriptions in every project.", }, }; export function SourceControlWritingSettingsSection() { const settings = usePrimarySettings(); const updateSettings = useUpdatePrimarySettings(); const serverProviders = useAtomValue(primaryServerProvidersAtom); const customInstructionsRef = useRef<HTMLTextAreaElement>(null); const style = settings.sourceControlWritingStyle; const defaults = DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle; const isSourceControlWritingStyleDirty = style.mode !== defaults.mode || style.customInstructions !== defaults.customInstructions; const defaultModelSelection = resolveAppModelSelectionState(settings, serverProviders); const usesDedicatedModel = settings.sourceControlWriterModelSelection !== null; const resolvedSourceControlWriterSelection = resolveSourceControlWriterModelSelection( settings, serverProviders, ); const activeSelection = resolvedSourceControlWriterSelection === settings.textGenerationModelSelection ? defaultModelSelection : resolvedSourceControlWriterSelection; const instanceEntries = sortProviderInstanceEntries( applyProviderInstanceSettings(deriveProviderInstanceEntries(serverProviders), settings), ); const modelOptionsByInstance = getCustomModelOptionsByInstance( settings, serverProviders, activeSelection.instanceId, activeSelection.model, ); return ( <SettingsSection title="Text generation"> <SettingsRow title="Source control writing style" description={MODE_OPTIONS[style.mode].description} resetAction={ isSourceControlWritingStyleDirty ? ( <SettingResetButton label="source control writing style" onClick={() => updateSettings({ sourceControlWritingStyle: { mode: defaults.mode, customInstructions: defaults.customInstructions, }, }) } /> ) : null } control={ <Select value={style.mode} onValueChange={(value) => { const customInstructions = customInstructionsRef.current?.value.trim(); updateSettings({ sourceControlWritingStyle: { mode: value as SourceControlWritingStyleMode, ...(customInstructions !== undefined ? { customInstructions } : {}), }, }); }} > <SelectTrigger className="w-full sm:w-56" aria-label="Source control writing style"> <SelectValue>{MODE_OPTIONS[style.mode].label}</SelectValue> </SelectTrigger> <SelectPopup align="end" alignItemWithTrigger={false}> {(Object.keys(MODE_OPTIONS) as SourceControlWritingStyleMode[]).map((mode) => ( <SelectItem key={mode} hideIndicator value={mode}> {MODE_OPTIONS[mode].label} </SelectItem> ))} </SelectPopup> </Select> } > {style.mode === "custom" ? ( <div className="mt-3 max-w-2xl pb-3.5"> <Textarea key={style.customInstructions} ref={customInstructionsRef} defaultValue={style.customInstructions} onBlur={(event) => { const customInstructions = event.target.value.trim(); if (customInstructions !== style.customInstructions) { updateSettings({ sourceControlWritingStyle: { customInstructions } }); } }} rows={4} placeholder="Keep titles concise. Use short bullet points in descriptions." aria-label="Custom source control writing instructions" /> </div> ) : null} </SettingsRow> <SettingsRow title="Follow change request templates" description="Structures change request descriptions using the current repository's template when one is available." resetAction={ style.followChangeRequestTemplates !== defaults.followChangeRequestTemplates ? ( <SettingResetButton label="change request templates" onClick={() => updateSettings({ sourceControlWritingStyle: { followChangeRequestTemplates: defaults.followChangeRequestTemplates, }, }) } /> ) : null } control={ <Switch checked={style.followChangeRequestTemplates} onCheckedChange={(checked) => updateSettings({ sourceControlWritingStyle: { followChangeRequestTemplates: Boolean(checked), }, }) } aria-label="Follow change request templates" /> } /> <SettingsRow title="Source control writer model" description="Optional model override for change descriptions, change request titles and descriptions, and branch or bookmark names. Off uses the global text generation model." control={ <div className="flex flex-wrap items-center justify-end gap-2"> {usesDedicatedModel ? ( <ProviderModelPicker activeInstanceId={activeSelection.instanceId} model={activeSelection.model} lockedProvider={null} instanceEntries={instanceEntries} modelOptionsByInstance={modelOptionsByInstance} triggerVariant="outline" triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground" triggerAriaLabel="Source control writer model" onInstanceModelChange={(instanceId, model) => { updateSettings({ sourceControlWriterModelSelection: createModelSelection(instanceId, model), }); }} /> ) : null} <Switch checked={usesDedicatedModel} onCheckedChange={(checked) => updateSettings({ sourceControlWriterModelSelection: checked ? createModelSelection( defaultModelSelection.instanceId, defaultModelSelection.model, defaultModelSelection.options, ) : null, }) } aria-label="Use a separate source control writer model" /> </div> } /> </SettingsSection> );}
import { useAtomValue } from "@effect/atom-react";
import { useRef } from "react";
import type { SourceControlWritingStyleMode } from "@t3tools/contracts";
import { DEFAULT_UNIFIED_SETTINGS } from "@t3tools/contracts/settings";
import { createModelSelection } from "@t3tools/shared/model";
import { resolveSourceControlWriterModelSelection } from "@t3tools/shared/serverSettings";
import { usePrimarySettings, useUpdatePrimarySettings } from "../../hooks/useSettings";
import {
applyProviderInstanceSettings,
deriveProviderInstanceEntries,
sortProviderInstanceEntries,
} from "../../providerInstances";
import {
getCustomModelOptionsByInstance,
resolveAppModelSelectionState,
} from "../../modelSelection";
import { primaryServerProvidersAtom } from "../../state/server";
import { ProviderModelPicker } from "../chat/ProviderModelPicker";
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";
import { Switch } from "../ui/switch";
import { Textarea } from "../ui/textarea";
import { SettingResetButton, SettingsRow, SettingsSection } from "./settingsLayout";
const MODE_OPTIONS: Record<SourceControlWritingStyleMode, { label: string; description: string }> =
{
repo_conventions: {
label: "Repository conventions",
description: "In each project, matches recent change descriptions and change request titles.",
},
conventional_commits: {
label: "Conventional Commits",
description:
"Uses Conventional Commit prefixes for change descriptions; change request titles and descriptions stay concise.",
},
custom: {
label: "Custom instructions",
description:
"Applies your instructions to change descriptions and change request titles and descriptions in every project.",
},
};
export function SourceControlWritingSettingsSection() {
const settings = usePrimarySettings();
const updateSettings = useUpdatePrimarySettings();
const serverProviders = useAtomValue(primaryServerProvidersAtom);
const customInstructionsRef = useRef<HTMLTextAreaElement>(null);
const style = settings.sourceControlWritingStyle;
const defaults = DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle;
const isSourceControlWritingStyleDirty =
style.mode !== defaults.mode || style.customInstructions !== defaults.customInstructions;
const defaultModelSelection = resolveAppModelSelectionState(settings, serverProviders);
const usesDedicatedModel = settings.sourceControlWriterModelSelection !== null;
const resolvedSourceControlWriterSelection = resolveSourceControlWriterModelSelection(
settings,
serverProviders,
);
const activeSelection =
resolvedSourceControlWriterSelection === settings.textGenerationModelSelection
? defaultModelSelection
: resolvedSourceControlWriterSelection;
const instanceEntries = sortProviderInstanceEntries(
applyProviderInstanceSettings(deriveProviderInstanceEntries(serverProviders), settings),
);
const modelOptionsByInstance = getCustomModelOptionsByInstance(
settings,
serverProviders,
activeSelection.instanceId,
activeSelection.model,
);
return (
<SettingsSection title="Text generation">
<SettingsRow
title="Source control writing style"
description={MODE_OPTIONS[style.mode].description}
resetAction={
isSourceControlWritingStyleDirty ? (
<SettingResetButton
label="source control writing style"
onClick={() =>
updateSettings({
sourceControlWritingStyle: {
mode: defaults.mode,
customInstructions: defaults.customInstructions,
},
})
}
/>
) : null
}
control={
<Select
value={style.mode}
onValueChange={(value) => {
const customInstructions = customInstructionsRef.current?.value.trim();
updateSettings({
sourceControlWritingStyle: {
mode: value as SourceControlWritingStyleMode,
...(customInstructions !== undefined ? { customInstructions } : {}),
},
});
}}
>
<SelectTrigger className="w-full sm:w-56" aria-label="Source control writing style">
<SelectValue>{MODE_OPTIONS[style.mode].label}</SelectValue>
</SelectTrigger>
<SelectPopup align="end" alignItemWithTrigger={false}>
{(Object.keys(MODE_OPTIONS) as SourceControlWritingStyleMode[]).map((mode) => (
<SelectItem key={mode} hideIndicator value={mode}>
{MODE_OPTIONS[mode].label}
</SelectItem>
))}
</SelectPopup>
</Select>
}
>
{style.mode === "custom" ? (
<div className="mt-3 max-w-2xl pb-3.5">
<Textarea
key={style.customInstructions}
ref={customInstructionsRef}
defaultValue={style.customInstructions}
onBlur={(event) => {
const customInstructions = event.target.value.trim();
if (customInstructions !== style.customInstructions) {
updateSettings({ sourceControlWritingStyle: { customInstructions } });
}
}}
rows={4}
placeholder="Keep titles concise. Use short bullet points in descriptions."
aria-label="Custom source control writing instructions"
/>
</div>
) : null}
</SettingsRow>
<SettingsRow
title="Follow change request templates"
description="Structures change request descriptions using the current repository's template when one is available."
resetAction={
style.followChangeRequestTemplates !== defaults.followChangeRequestTemplates ? (
<SettingResetButton
label="change request templates"
onClick={() =>
updateSettings({
sourceControlWritingStyle: {
followChangeRequestTemplates: defaults.followChangeRequestTemplates,
},
})
}
/>
) : null
}
control={
<Switch
checked={style.followChangeRequestTemplates}
onCheckedChange={(checked) =>
updateSettings({
sourceControlWritingStyle: {
followChangeRequestTemplates: Boolean(checked),
},
})
}
aria-label="Follow change request templates"
/>
}
/>
<SettingsRow
title="Source control writer model"
description="Optional model override for change descriptions, change request titles and descriptions, and branch or bookmark names. Off uses the global text generation model."
control={
<div className="flex flex-wrap items-center justify-end gap-2">
{usesDedicatedModel ? (
<ProviderModelPicker
activeInstanceId={activeSelection.instanceId}
model={activeSelection.model}
lockedProvider={null}
instanceEntries={instanceEntries}
modelOptionsByInstance={modelOptionsByInstance}
triggerVariant="outline"
triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"
triggerAriaLabel="Source control writer model"
onInstanceModelChange={(instanceId, model) => {
updateSettings({
sourceControlWriterModelSelection: createModelSelection(instanceId, model),
});
}}
/>
) : null}
<Switch
checked={usesDedicatedModel}
onCheckedChange={(checked) =>
updateSettings({
sourceControlWriterModelSelection: checked
? createModelSelection(
defaultModelSelection.instanceId,
defaultModelSelection.model,
defaultModelSelection.options,
)
: null,
})
}
aria-label="Use a separate source control writer model"
/>
</div>
}
/>
</SettingsSection>
);
}