Files
kandev/apps/web/components/settings/task-behavior-settings.tsx
T
2026-09-22 21:24:43 +01:00

148 lines
6.8 KiB
TypeScript

"use client";
import { useTranslation } from "react-i18next";
import { useEffect, useRef, useState } from "react";
import { AgentGeneratedTaskTitleSettings } from "@/components/settings/agent-generated-task-title-settings";
import { AnchoredPromptBarSettings } from "@/components/settings/anchored-prompt-bar-settings";
import { ArchiveConfirmationSettings } from "@/components/settings/archive-confirmation-settings";
import { CreationAutoFocusSettings } from "@/components/settings/creation-auto-focus-settings";
import { MCPTaskAgentProfileDefaultSettings } from "@/components/settings/mcp-task-agent-profile-default-settings";
import { PreventAutoStartAgentSettings } from "@/components/settings/prevent-auto-start-agent-settings";
import { SettingsGroup } from "@/components/settings/settings-group";
import { SettingsTarget } from "@/components/settings/settings-target";
import { SleepInhibitionSettings } from "@/components/settings/sleep-inhibition-settings";
import { TodoListPanelSettings } from "@/components/settings/todo-list-panel-settings";
import { UnreadDividerSettings } from "@/components/settings/unread-divider-settings";
import {
MessageQueueSettingsContent,
useMessageQueueSettingsDraft,
} from "@/components/settings/system/message-queue-settings";
import { SessionCapacitySettingsContent } from "@/components/settings/system/session-capacity-settings";
import { useSessionCapacitySettings } from "@/components/settings/system/use-session-capacity-settings";
import { GENERAL_SETTINGS_TARGETS } from "@/lib/settings-discovery/catalog/preferences";
import { SettingsPageHeader } from "./settings-typography";
import { SettingsTabs, SettingsTabsList, SettingsTabsPanel } from "./settings-tabs";
import { useSettingsTab } from "@/hooks/domains/settings/use-settings-tab";
import { useSettingsSaveCoordinator } from "./settings-save-provider";
import { UnsavedChangesBadge } from "./unsaved-indicator";
import {
TASK_BEHAVIOR_TABS,
TASK_BEHAVIOR_TARGET_TABS,
taskBehaviorTab,
firstNewAttentionTab,
} from "./task-behavior-tabs";
function useTabAttention(value: string, selectTab: (tab: string) => void) {
const { contributorStates } = useSettingsSaveCoordinator();
const previousAttention = useRef<string[]>([]);
const attention = contributorStates.filter((state) => state.invalid || state.saveFailed);
const attentionKey = attention
.map((state) => `${state.id}:${state.invalid}:${state.saveFailed}`)
.join("|");
useEffect(() => {
const tokens = attentionKey ? attentionKey.split("|") : [];
const newTabs = tokens
.filter((token) => !previousAttention.current.includes(token))
.map((token) => taskBehaviorTab(token.split(":")[0]))
.filter((tab): tab is NonNullable<typeof tab> => !!tab);
previousAttention.current = tokens;
const next = firstNewAttentionTab(newTabs, []);
if (next && next !== value) selectTab(next);
}, [attentionKey, selectTab, value]);
return { contributorStates, attention };
}
export function TaskBehaviorSettings() {
const { t } = useTranslation();
const queueState = useMessageQueueSettingsDraft();
const sessionState = useSessionCapacitySettings();
const [sleepAttention, setSleepAttention] = useState(false);
const { value, selectTab } = useSettingsTab({
tabs: TASK_BEHAVIOR_TABS,
defaultTab: "tasks",
targetToTab: TASK_BEHAVIOR_TARGET_TABS,
});
const { contributorStates, attention } = useTabAttention(value, selectTab);
const runtimeLoadError = queueState.loadFailed || sessionState.loadFailed || sleepAttention;
const tabLabels = {
tasks: t("settings:taskBehaviorTabTasks"),
conversation: t("settings:taskBehaviorTabConversation"),
runtime: t("settings:taskBehaviorTabRuntime"),
};
const tabs = TASK_BEHAVIOR_TABS.map((id) => ({
id,
ariaLabel: tabLabels[id],
label: (
<span className="flex items-center gap-2">
{tabLabels[id]}
{contributorStates.some((state) => state.isDirty && taskBehaviorTab(state.id) === id) && (
<UnsavedChangesBadge />
)}
{(attention.some((state) => taskBehaviorTab(state.id) === id) ||
(id === "runtime" && runtimeLoadError)) && (
<span role="status" className="text-xs text-destructive">
{t("settings:settingsNeedAttention")}
</span>
)}
</span>
),
}));
return (
<div className="space-y-6" data-testid="task-behavior-settings">
<SettingsTabs tabs={tabs} value={value} onValueChange={selectTab}>
<SettingsPageHeader
title={t("settings:taskBehavior")}
tabs={<SettingsTabsList ariaLabel={t("settings:taskBehavior")} />}
/>
<SettingsTabsPanel value="tasks" className="space-y-6 pt-4">
<SettingsGroup
title={t("settings:taskBehaviorCreating")}
titleTestId="task-behavior-creating-title"
data-testid="task-behavior-group"
>
<CreationAutoFocusSettings presentation="row" />
<AgentGeneratedTaskTitleSettings presentation="row" />
<MCPTaskAgentProfileDefaultSettings presentation="row" />
<PreventAutoStartAgentSettings presentation="row" />
</SettingsGroup>
<SettingsGroup
title={t("settings:taskBehaviorArchiving")}
titleTestId="task-behavior-archiving-title"
data-testid="task-behavior-group"
>
<ArchiveConfirmationSettings presentation="row" />
</SettingsGroup>
</SettingsTabsPanel>
<SettingsTabsPanel value="conversation" className="pt-4">
<SettingsGroup
title={t("settings:taskBehaviorConversation")}
titleTestId="task-behavior-conversation-title"
data-testid="task-behavior-group"
>
<UnreadDividerSettings presentation="row" />
<AnchoredPromptBarSettings presentation="row" />
<TodoListPanelSettings presentation="row" />
</SettingsGroup>
</SettingsTabsPanel>
<SettingsTabsPanel value="runtime" className="pt-4">
<SettingsGroup
title={t("settings:taskBehaviorRuntime")}
description={t("settings:taskBehaviorRuntimeScopeShort")}
titleTestId="task-behavior-runtime-title"
isDirty={queueState.isDirty || sessionState.isDirty}
data-testid="task-behavior-runtime"
>
<SettingsTarget targetId={GENERAL_SETTINGS_TARGETS.sessionCapacity}>
<SessionCapacitySettingsContent state={sessionState} withinGroup />
</SettingsTarget>
<SettingsTarget targetId={GENERAL_SETTINGS_TARGETS.messageQueue}>
<MessageQueueSettingsContent state={queueState} withinGroup />
</SettingsTarget>
<SleepInhibitionSettings withinGroup onAttentionChange={setSleepAttention} />
</SettingsGroup>
</SettingsTabsPanel>
</SettingsTabs>
</div>
);
}