diff --git a/src-tauri/src/commands/file.rs b/src-tauri/src/commands/file.rs new file mode 100644 index 0000000..13a9dfc --- /dev/null +++ b/src-tauri/src/commands/file.rs @@ -0,0 +1,24 @@ +use tauri::{AppHandle, Manager}; +use tauri_plugin_opener::OpenerExt; + +#[tauri::command] +pub async fn reveal_path(app: AppHandle, path: String) -> Result<(), String> { + app.opener() + .reveal_item_in_dir(path) + .map_err(|error| error.to_string()) +} + +#[tauri::command] +pub async fn open_generated_dir(app: AppHandle) -> Result<(), String> { + let dir = app + .path() + .app_data_dir() + .map_err(|error| error.to_string())? + .join("images") + .join("generated"); + + std::fs::create_dir_all(&dir).map_err(|error| error.to_string())?; + app.opener() + .open_path(dir.to_string_lossy().to_string(), None::<&str>) + .map_err(|error| error.to_string()) +} diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index da13b89..f7ceb0b 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -1,3 +1,4 @@ pub mod dialog; +pub mod file; pub mod generation; pub mod provider; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5692616..b7f163e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -54,6 +54,8 @@ pub fn run() { commands::provider::upsert_provider, commands::provider::delete_provider, commands::dialog::pick_material_images, + commands::file::reveal_path, + commands::file::open_generated_dir, commands::generation::create_generation_task, commands::generation::generate_image, ]) diff --git a/src/main.tsx b/src/main.tsx index 56fae6a..af11b83 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -66,19 +66,23 @@ const defaultProviderForm: ProviderForm = { }; const initialGenerationSteps: GenerationStep[] = [ - { label: '保存 Provider 配置', status: 'pending' }, - { label: '提交生成任务', status: 'pending' }, - { label: '请求并等待图像模型返回', status: 'pending' }, - { label: '保存图片到应用数据文件夹', status: 'pending' }, - { label: '更新本次打开图片列表', status: 'pending' }, + { label: '保存配置', status: 'pending' }, + { label: '提交任务', status: 'pending' }, + { label: '等待模型返回', status: 'pending' }, + { label: '保存到应用文件夹', status: 'pending' }, + { label: '更新结果列表', status: 'pending' }, ]; function App() { const [providers, setProviders] = useState([]); const [providerForm, setProviderForm] = useState(defaultProviderForm); const [prompt, setPrompt] = useState('一只赛博朋克风格的橘猫坐在霓虹灯下'); + const [imageSize, setImageSize] = useState('1024x1024'); + const [imageQuality, setImageQuality] = useState('auto'); const [status, setStatus] = useState('准备就绪'); const [isBusy, setIsBusy] = useState(false); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [previewImage, setPreviewImage] = useState(null); const [sessionImages, setSessionImages] = useState([]); const [materialPaths, setMaterialPaths] = useState([]); const [generationSteps, setGenerationSteps] = useState(initialGenerationSteps); @@ -124,13 +128,11 @@ function App() { async function saveProvider() { setIsBusy(true); - setStatus('正在保存 Provider 配置...'); + setStatus('正在保存配置...'); try { - await invoke('upsert_provider', { - input: providerForm, - }); + await invoke('upsert_provider', { input: providerForm }); await refreshProviders(); - setStatus('Provider 已保存,可以直接生成图片'); + setStatus('配置已保存'); } catch (error) { setStatus(`保存失败:${formatError(error)}`); } finally { @@ -140,14 +142,14 @@ function App() { async function deleteProvider(id: string) { setIsBusy(true); - setStatus('正在删除 Provider...'); + setStatus('正在删除配置...'); try { await invoke('delete_provider', { id }); await refreshProviders(); if (providerForm.id === id) { setProviderForm(defaultProviderForm); } - setStatus('Provider 已删除'); + setStatus('配置已删除'); } catch (error) { setStatus(`删除失败:${formatError(error)}`); } finally { @@ -167,7 +169,7 @@ function App() { image_model: provider.image_model ?? defaultProviderForm.image_model, enabled: provider.enabled, })); - setStatus('已载入 Provider,修改后点击保存即可覆盖当前配置。'); + setStatus('已切换模型配置'); } async function generateImage() { @@ -176,9 +178,7 @@ function App() { setStatus('正在生成图片...'); try { startStep(0); - await invoke('upsert_provider', { - input: providerForm, - }); + await invoke('upsert_provider', { input: providerForm }); startStep(1); await new Promise((resolve) => window.setTimeout(resolve, 120)); startStep(2); @@ -187,8 +187,8 @@ function App() { provider_id: providerForm.id, prompt, model: providerForm.image_model, - size: '1024x1024', - quality: 'auto', + size: imageSize, + quality: imageQuality, image_paths: materialPaths, }, }); @@ -206,7 +206,7 @@ function App() { ...images, ]); setStep(4, 'done'); - setStatus(`生成完成,已保存到应用数据文件夹:${result.asset.file_path}`); + setStatus(`生成完成:${result.asset.file_path}`); } catch (error) { setGenerationSteps((steps) => steps.map((step) => (step.status === 'active' ? { ...step, status: 'error' } : step)), @@ -218,15 +218,15 @@ function App() { } async function pickMaterialImages() { - setStatus('正在打开素材图片选择器...'); + setStatus('正在打开素材选择器...'); try { const paths = await invoke('pick_material_images'); if (paths.length === 0) { - setStatus('未选择素材图片'); + setStatus('未选择素材'); return; } setMaterialPaths((current) => Array.from(new Set([...current, ...paths]))); - setStatus(`已导入 ${paths.length} 张素材图片`); + setStatus(`已导入 ${paths.length} 张素材`); } catch (error) { setStatus(`打开素材选择器失败:${formatError(error)}`); } @@ -236,186 +236,293 @@ function App() { setMaterialPaths((current) => current.filter((item) => item !== path)); } + async function revealImage(path: string) { + try { + await invoke('reveal_path', { path }); + } catch (error) { + setStatus(`打开文件位置失败:${formatError(error)}`); + } + } + + async function openGeneratedDir() { + try { + await invoke('open_generated_dir'); + } catch (error) { + setStatus(`打开保存目录失败:${formatError(error)}`); + } + } + useEffect(() => { refreshProviders().catch(() => setStatus('后端未启动或数据库初始化失败')); }, []); return ( -
-
-

Image Draw AI

-

图片默认保存到应用数据文件夹。

-
- -
-
+
+
+
+
-

Provider

-

模型供应商配置

-
- -
- -
- - -
- - - - - -
- - -
- -
- -
-
- -
-
-
-

Generate

-

生成图片

+

Image Draw AI

+

图片默认保存到应用数据文件夹

-