fix: improve apple intelligence ui and add reusable alert component (#517)

- hide model dropdown when apple intelligence is selected
- move error state into provider section (only shows when unavailable)
- remove redundant apple intelligence infobox
- add reusable Alert component with variants (error, warning, info, success)
- Alert supports contained prop for use inside settings groups
This commit is contained in:
Viren Mohindra 2026-01-10 09:41:10 +05:30 committed by GitHub
parent ae9c33e0c7
commit 5da495157f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 112 additions and 63 deletions

View file

@ -3,13 +3,16 @@ import { useTranslation } from "react-i18next";
import { RefreshCcw } from "lucide-react";
import { commands } from "@/bindings";
import { SettingsGroup } from "../../ui/SettingsGroup";
import { SettingContainer } from "../../ui/SettingContainer";
import { Alert } from "../../ui/Alert";
import {
Dropdown,
SettingContainer,
SettingsGroup,
Textarea,
} from "@/components/ui";
import { Button } from "../../ui/Button";
import { ResetButton } from "../../ui/ResetButton";
import { Input } from "../../ui/Input";
import { Dropdown } from "../../ui/Dropdown";
import { Textarea } from "../../ui/Textarea";
import { ProviderSelect } from "../PostProcessingSettingsApi/ProviderSelect";
import { BaseUrlField } from "../PostProcessingSettingsApi/BaseUrlField";
@ -56,28 +59,12 @@ const PostProcessingSettingsApiComponent: React.FC = () => {
</div>
</SettingContainer>
{state.appleIntelligenceUnavailable ? (
<div className="p-3 bg-red-500/10 border border-red-500/50">
<p className="text-sm text-red-500">
{t("settings.postProcessing.api.appleIntelligence.unavailable")}
</p>
</div>
) : null}
{state.isAppleProvider ? (
<SettingContainer
title={t("settings.postProcessing.api.appleIntelligence.title")}
description={t(
"settings.postProcessing.api.appleIntelligence.description",
)}
descriptionMode="tooltip"
layout="stacked"
grouped={true}
>
<DisabledNotice>
{t("settings.postProcessing.api.appleIntelligence.requirements")}
</DisabledNotice>
</SettingContainer>
state.appleIntelligenceUnavailable ? (
<Alert variant="error" contained>
{t("settings.postProcessing.api.appleIntelligence.unavailable")}
</Alert>
) : null
) : (
<>
{state.selectedProvider?.id === "custom" && (
@ -124,51 +111,49 @@ const PostProcessingSettingsApiComponent: React.FC = () => {
</>
)}
<SettingContainer
title={t("settings.postProcessing.api.model.title")}
description={
state.isAppleProvider
? t("settings.postProcessing.api.model.descriptionApple")
: state.isCustomProvider
{!state.isAppleProvider && (
<SettingContainer
title={t("settings.postProcessing.api.model.title")}
description={
state.isCustomProvider
? t("settings.postProcessing.api.model.descriptionCustom")
: t("settings.postProcessing.api.model.descriptionDefault")
}
descriptionMode="tooltip"
layout="stacked"
grouped={true}
>
<div className="flex items-center gap-2">
<ModelSelect
value={state.model}
options={state.modelOptions}
disabled={state.isModelUpdating}
isLoading={state.isFetchingModels}
placeholder={
state.isAppleProvider
? t("settings.postProcessing.api.model.placeholderApple")
: state.modelOptions.length > 0
}
descriptionMode="tooltip"
layout="stacked"
grouped={true}
>
<div className="flex items-center gap-2">
<ModelSelect
value={state.model}
options={state.modelOptions}
disabled={state.isModelUpdating}
isLoading={state.isFetchingModels}
placeholder={
state.modelOptions.length > 0
? t(
"settings.postProcessing.api.model.placeholderWithOptions",
)
: t("settings.postProcessing.api.model.placeholderNoOptions")
}
onSelect={state.handleModelSelect}
onCreate={state.handleModelCreate}
onBlur={() => {}}
className="flex-1 min-w-[380px]"
/>
<ResetButton
onClick={state.handleRefreshModels}
disabled={state.isFetchingModels || state.isAppleProvider}
ariaLabel={t("settings.postProcessing.api.model.refreshModels")}
className="flex h-10 w-10 items-center justify-center"
>
<RefreshCcw
className={`h-4 w-4 ${state.isFetchingModels ? "animate-spin" : ""}`}
}
onSelect={state.handleModelSelect}
onCreate={state.handleModelCreate}
onBlur={() => {}}
className="flex-1 min-w-[380px]"
/>
</ResetButton>
</div>
</SettingContainer>
<ResetButton
onClick={state.handleRefreshModels}
disabled={state.isFetchingModels}
ariaLabel={t("settings.postProcessing.api.model.refreshModels")}
className="flex h-10 w-10 items-center justify-center"
>
<RefreshCcw
className={`h-4 w-4 ${state.isFetchingModels ? "animate-spin" : ""}`}
/>
</ResetButton>
</div>
</SettingContainer>
)}
</>
);
};

View file

@ -0,0 +1,64 @@
import React from "react";
import { AlertCircle, AlertTriangle, Info, CheckCircle } from "lucide-react";
type AlertVariant = "error" | "warning" | "info" | "success";
interface AlertProps {
variant?: AlertVariant;
/** When true, removes rounded corners for use inside containers */
contained?: boolean;
children: React.ReactNode;
className?: string;
}
const variantStyles: Record<
AlertVariant,
{ container: string; icon: string; text: string }
> = {
error: {
container: "bg-red-500/10",
icon: "text-red-500",
text: "text-red-400",
},
warning: {
container: "bg-yellow-500/10",
icon: "text-yellow-500",
text: "text-yellow-400",
},
info: {
container: "bg-blue-500/10",
icon: "text-blue-500",
text: "text-blue-400",
},
success: {
container: "bg-green-500/10",
icon: "text-green-500",
text: "text-green-400",
},
};
const variantIcons: Record<AlertVariant, React.ElementType> = {
error: AlertCircle,
warning: AlertTriangle,
info: Info,
success: CheckCircle,
};
export const Alert: React.FC<AlertProps> = ({
variant = "error",
contained = false,
children,
className = "",
}) => {
const styles = variantStyles[variant];
const Icon = variantIcons[variant];
return (
<div
className={`flex items-start gap-3 p-4 ${styles.container} ${contained ? "" : "rounded-lg"} ${className}`}
>
<Icon className={`w-5 h-5 shrink-0 mt-0.5 ${styles.icon}`} />
<p className={`text-sm ${styles.text}`}>{children}</p>
</div>
);
};