fix: resolve all QA report issues from 2026-06-05 sweep

Merges 77 QA issue fixes across routing, editor, i18n, pipeline,
and settings. Includes catch-all 404 page, keyboard shortcuts,
export filename, password validation, selection masking, brush flow,
eraser modes, feather control, pipeline persistence, RTL properties,
license info, and i18n for all 21 locales.
This commit is contained in:
SnapOtter
2026-06-06 10:37:36 +08:00
44 changed files with 1127 additions and 210 deletions
@@ -21,10 +21,12 @@ export function CompressControls({ settings: initialSettings, onChange }: Compre
const [targetSizeValue, setTargetSizeValue] = useState("");
const [sizeUnit, setSizeUnit] = useState<SizeUnit>("KB");
const initializedRef = useRef(false);
const prevSettingsKeyRef = useRef<string | null>(null);
useEffect(() => {
if (!initialSettings || initializedRef.current) return;
initializedRef.current = true;
if (!initialSettings) return;
const key = JSON.stringify(initialSettings);
if (prevSettingsKeyRef.current === key) return;
prevSettingsKeyRef.current = key;
if (initialSettings.mode != null) setMode(initialSettings.mode as CompressMode);
if (initialSettings.quality != null) setQuality(Number(initialSettings.quality));
if (initialSettings.targetSizeKb != null)
@@ -129,7 +129,7 @@ function SortableStep({
e.stopPropagation();
onRemove();
}}
title="Remove"
title={t.automate.removeStep}
className="p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive"
>
<X className="h-4 w-4" />
@@ -159,6 +159,7 @@ export function PipelineBuilder({
onUpdateSettings,
onToggleStep,
}: PipelineBuilderProps) {
const { t } = useTranslation();
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
@@ -177,10 +178,8 @@ export function PipelineBuilder({
<div className="p-4 rounded-full bg-muted/50 mb-4">
<FileImage className="h-8 w-8 text-muted-foreground" />
</div>
<h3 className="text-sm font-medium text-foreground mb-1">No steps yet</h3>
<p className="text-sm text-muted-foreground max-w-[240px]">
Click tools from the palette to build your pipeline
</p>
<h3 className="text-sm font-medium text-foreground mb-1">{t.automate.noStepsHeading}</h3>
<p className="text-sm text-muted-foreground max-w-[240px]">{t.automate.addToolsPrompt}</p>
</div>
);
}
@@ -66,12 +66,16 @@ export function ToolPalette({ onAddStep, className }: ToolPaletteProps) {
return (
<div className={cn("flex flex-col h-full", className)}>
<div className="px-3 pt-3 pb-2 shrink-0">
<SearchBar value={search} onChange={setSearch} placeholder="Search tools..." />
<SearchBar
value={search}
onChange={setSearch}
placeholder={t.automate.searchToolsPlaceholder}
/>
</div>
<div className="flex-1 overflow-y-auto px-3 pb-3">
{availableTools.length === 0 ? (
<p className="text-sm text-muted-foreground text-center py-8">No tools found</p>
<p className="text-sm text-muted-foreground text-center py-8">{t.common.noToolsFound}</p>
) : isSearching ? (
<div className="space-y-1">
{availableTools.map((tool) => (