fix: show tool name instead of bundle name on AI feature install prompt

The install prompt and error messages were showing the underlying AI
bundle name (e.g. "Background Removal") instead of the actual tool name
(e.g. "Passport Photo"). Now the UI shows the correct tool name and
description while still installing the correct bundle behind the scenes.

Closes #130
This commit is contained in:
SnapOtter
2026-05-07 20:33:25 +08:00
parent 16af9c573a
commit 797b4577d4
4 changed files with 23 additions and 10 deletions
@@ -47,9 +47,15 @@ interface FeatureInstallPromptProps {
bundle: FeatureBundleState;
isAdmin: boolean;
toolName?: string;
toolDescription?: string;
}
export function FeatureInstallPrompt({ bundle, isAdmin, toolName }: FeatureInstallPromptProps) {
export function FeatureInstallPrompt({
bundle,
isAdmin,
toolName,
toolDescription,
}: FeatureInstallPromptProps) {
const { installBundle, clearError, installing, errors, startTimes, queued } = useFeaturesStore();
const progress = installing[bundle.id] ?? null;
const error = errors[bundle.id] ?? null;
@@ -57,6 +63,7 @@ export function FeatureInstallPrompt({ bundle, isAdmin, toolName }: FeatureInsta
const isQueued = queued.includes(bundle.id);
const startTime = startTimes[bundle.id] ?? null;
const displayName = toolName || bundle.name;
const displayDescription = toolDescription || bundle.description;
const [messageIndex, setMessageIndex] = useState(() =>
Math.floor(Math.random() * PROGRESS_MESSAGES.length),
@@ -103,7 +110,7 @@ export function FeatureInstallPrompt({ bundle, isAdmin, toolName }: FeatureInsta
<Download className="h-16 w-16 text-muted-foreground" />
<div className="space-y-2">
<h2 className="text-xl font-semibold text-foreground">{displayName}</h2>
<p className="text-muted-foreground max-w-md">{bundle.description}</p>
<p className="text-muted-foreground max-w-md">{displayDescription}</p>
<p className="text-sm text-muted-foreground">
This feature requires an additional download (~{bundle.estimatedSize})
</p>
+2 -2
View File
@@ -148,7 +148,7 @@ export function usePipelineProcessor() {
const parsed = parseApiError(body, xhr.status);
if (typeof parsed === "object" && parsed.type === "feature_not_installed") {
setError(
`Feature "${parsed.featureName}" is not installed. Enable it in Settings → AI Features.`,
`The "${parsed.featureName}" feature is not installed. Enable it in Settings → AI Features.`,
);
} else {
setError(parsed as string);
@@ -279,7 +279,7 @@ export function usePipelineProcessor() {
} else {
const parsed = parseApiError(body, response.status);
if (typeof parsed === "object" && parsed.type === "feature_not_installed") {
errorMsg = `Feature "${parsed.featureName}" is not installed. Enable it in Settings → AI Features.`;
errorMsg = `The "${parsed.featureName}" feature is not installed. Enable it in Settings → AI Features.`;
} else {
errorMsg = parsed as string;
}
+6 -5
View File
@@ -1,4 +1,4 @@
import { PYTHON_SIDECAR_TOOLS } from "@snapotter/shared";
import { PYTHON_SIDECAR_TOOLS, TOOLS } from "@snapotter/shared";
import { useCallback, useEffect, useRef, useState } from "react";
import { formatHeaders, parseApiError } from "@/lib/api";
import { generateId } from "@/lib/utils";
@@ -47,6 +47,7 @@ export function useToolProcessor(toolId: string) {
const isAiTool = AI_PYTHON_TOOLS.has(toolId);
const isMediumTool = MEDIUM_TOOLS.has(toolId);
const toolName = TOOLS.find((t) => t.id === toolId)?.name ?? toolId;
const processingTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
// Clean up on unmount
@@ -280,7 +281,7 @@ export function useToolProcessor(toolId: string) {
const parsed = parseApiError(body, xhr.status);
if (typeof parsed === "object" && parsed.type === "feature_not_installed") {
setError(
`Feature "${parsed.featureName}" is not installed. Enable it in Settings → AI Features.`,
`${toolName} requires the "${parsed.featureName}" feature. Enable it in Settings → AI Features.`,
);
} else {
setError(parsed as string);
@@ -324,7 +325,7 @@ export function useToolProcessor(toolId: string) {
});
xhr.send(formData);
},
[toolId, isAiTool, isMediumTool, setProcessing, setError],
[toolId, isAiTool, isMediumTool, setProcessing, setError, toolName],
);
const processAllFiles = useCallback(
@@ -407,7 +408,7 @@ export function useToolProcessor(toolId: string) {
const body = JSON.parse(text);
const parsed = parseApiError(body, response.status);
if (typeof parsed === "object" && parsed.type === "feature_not_installed") {
errorMsg = `Feature "${parsed.featureName}" is not installed. Enable it in Settings → AI Features.`;
errorMsg = `${toolName} requires the "${parsed.featureName}" feature. Enable it in Settings → AI Features.`;
} else {
errorMsg = parsed as string;
}
@@ -465,7 +466,7 @@ export function useToolProcessor(toolId: string) {
setProgress(IDLE_PROGRESS);
}
},
[toolId, processFiles, setProcessing, setError],
[toolId, processFiles, setProcessing, setError, toolName],
);
return {
+6 -1
View File
@@ -292,7 +292,12 @@ export function ToolPage() {
if (isAiTool && !toolInstalled && featureBundle) {
return (
<AppLayout>
<FeatureInstallPrompt bundle={featureBundle} isAdmin={isAdmin} toolName={tool?.name} />
<FeatureInstallPrompt
bundle={featureBundle}
isAdmin={isAdmin}
toolName={tool?.name}
toolDescription={tool?.description}
/>
</AppLayout>
);
}