feat: lightweight Docker image without AI/ML tools (:lite tag)

Closes #1
This commit is contained in:
stirling-image
2026-04-05 00:23:21 +08:00
committed by GitHub
parent 51f10abb35
commit 449a2fc319
19 changed files with 1819 additions and 72 deletions
+35 -7
View File
@@ -1,12 +1,14 @@
import { CATEGORIES, TOOLS } from "@stirling-image/shared";
import * as icons from "lucide-react";
import { useCallback } from "react";
import { useCallback, useEffect, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
import { ImageViewer } from "@/components/common/image-viewer";
import { MultiImageViewer } from "@/components/common/multi-image-viewer";
import { AppLayout } from "@/components/layout/app-layout";
import { cn } from "@/lib/utils";
import { useFileStore } from "@/stores/file-store";
import { useSettingsStore } from "@/stores/settings-store";
// Tools shown prominently as "quick actions" at the top
const QUICK_ACTION_IDS = ["resize", "compress", "convert", "remove-background"];
@@ -15,6 +17,13 @@ export function HomePage() {
const { setFiles, files, reset, originalBlobUrl, selectedFileName, selectedFileSize } =
useFileStore();
const navigate = useNavigate();
const { variantUnavailableTools, fetch: fetchSettings } = useSettingsStore();
useEffect(() => {
fetchSettings();
}, [fetchSettings]);
const unavailableSet = useMemo(() => new Set(variantUnavailableTools), [variantUnavailableTools]);
const handleFiles = useCallback(
(newFiles: File[]) => {
@@ -24,8 +33,22 @@ export function HomePage() {
[setFiles, reset],
);
const handleToolClick = (route: string) => {
// Files are already in the store — just navigate
const handleToolClick = (route: string, toolId: string) => {
if (unavailableSet.has(toolId)) {
toast("This tool requires the full image.", {
description:
"Pull stirlingimage/stirling-image:latest for all features including AI tools.",
action: {
label: "Learn more",
onClick: () =>
window.open(
"https://stirling-image.github.io/stirling-image/guide/docker-tags",
"_blank",
),
},
});
return;
}
navigate(route);
};
@@ -80,8 +103,11 @@ export function HomePage() {
<button
key={id}
type="button"
onClick={() => handleToolClick(tool.route)}
className="flex items-center gap-2 p-3 rounded-xl border border-border hover:border-primary hover:bg-primary/5 transition-colors text-left"
onClick={() => handleToolClick(tool.route, tool.id)}
className={cn(
"flex items-center gap-2 p-3 rounded-xl border border-border hover:border-primary hover:bg-primary/5 transition-colors text-left",
unavailableSet.has(id) && "opacity-50",
)}
>
<div className="p-1.5 rounded-lg bg-primary/10 text-primary">
<Icon className="h-4 w-4" />
@@ -122,10 +148,12 @@ export function HomePage() {
<button
key={tool.id}
type="button"
onClick={() => handleToolClick(tool.route)}
onClick={() => handleToolClick(tool.route, tool.id)}
className={cn(
"flex items-center gap-2.5 w-full py-1.5 px-2 rounded-lg text-left transition-colors",
"hover:bg-muted text-foreground",
unavailableSet.has(tool.id)
? "opacity-50 hover:bg-muted/50"
: "hover:bg-muted text-foreground",
)}
>
<Icon className="h-4 w-4 text-muted-foreground shrink-0" />