folder增加项目启动器入口,优化窗口跳转

This commit is contained in:
xintaofei
2026-03-27 15:25:55 +08:00
parent 3b080c801b
commit ea77c5b0e8
20 changed files with 81 additions and 39 deletions

View File

@@ -610,13 +610,22 @@ pub async fn open_push_window(
} }
#[tauri::command] #[tauri::command]
pub async fn open_project_boot_window(app: AppHandle) -> Result<(), AppCommandError> { pub async fn open_project_boot_window(
app: AppHandle,
source: Option<String>,
) -> Result<(), AppCommandError> {
if let Some(existing) = app.get_webview_window("project-boot") { if let Some(existing) = app.get_webview_window("project-boot") {
ensure_windows_undecorated(&existing); ensure_windows_undecorated(&existing);
let _ = existing.unminimize(); let _ = existing.unminimize();
existing.set_focus().map_err(|e| { existing.set_focus().map_err(|e| {
AppCommandError::window("Failed to focus project boot window", e.to_string()) AppCommandError::window("Failed to focus project boot window", e.to_string())
})?; })?;
// Close welcome if opened from welcome
if source.as_deref() == Some("welcome") {
if let Some(w) = app.get_webview_window("welcome") {
let _ = w.close();
}
}
return Ok(()); return Ok(());
} }
@@ -633,5 +642,12 @@ pub async fn open_project_boot_window(app: AppHandle) -> Result<(), AppCommandEr
})?; })?;
ensure_windows_undecorated(&window); ensure_windows_undecorated(&window);
// Close welcome if opened from welcome
if source.as_deref() == Some("welcome") {
if let Some(w) = app.get_webview_window("welcome") {
let _ = w.close();
}
}
Ok(()) Ok(())
} }

View File

@@ -140,6 +140,24 @@ pub fn run() {
}); });
} }
if label == "project-boot"
&& matches!(
event,
tauri::WindowEvent::CloseRequested { .. } | tauri::WindowEvent::Destroyed
)
{
let app = window.app_handle();
if !APP_QUITTING.load(Ordering::Relaxed) {
let has_other = app
.webview_windows()
.keys()
.any(|l| *l != label && *l != "settings");
if !has_other {
let _ = windows::open_welcome_window(app);
}
}
}
if let tauri::WindowEvent::CloseRequested { .. } = event { if let tauri::WindowEvent::CloseRequested { .. } = event {
if label.starts_with("folder-") { if label.starts_with("folder-") {
let app = window.app_handle(); let app = window.app_handle();

View File

@@ -1,7 +1,13 @@
"use client" "use client"
import { useState } from "react" import { useState } from "react"
import { ChevronDown, Folder, FolderOpen, GitBranch } from "lucide-react" import {
ChevronDown,
Folder,
FolderOpen,
GitBranch,
Rocket,
} from "lucide-react"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import { import {
DropdownMenu, DropdownMenu,
@@ -16,6 +22,7 @@ import {
listOpenFolders, listOpenFolders,
loadFolderHistory, loadFolderHistory,
openFolderWindow, openFolderWindow,
openProjectBootWindow,
} from "@/lib/api" } from "@/lib/api"
import { openFileDialog } from "@/lib/platform" import { openFileDialog } from "@/lib/platform"
import { useFolderContext } from "@/contexts/folder-context" import { useFolderContext } from "@/contexts/folder-context"
@@ -87,6 +94,10 @@ export function FolderNameDropdown() {
<GitBranch className="h-3.5 w-3.5 shrink-0" /> <GitBranch className="h-3.5 w-3.5 shrink-0" />
{t("cloneRepository")} {t("cloneRepository")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onSelect={() => openProjectBootWindow()}>
<Rocket className="h-3.5 w-3.5 shrink-0" />
{t("projectBoot")}
</DropdownMenuItem>
{openFolders.length > 0 && ( {openFolders.length > 0 && (
<> <>
<DropdownMenuSeparator /> <DropdownMenuSeparator />

View File

@@ -302,9 +302,7 @@ export function FolderTitleBar() {
<span <span
className={cn( className={cn(
"grid transition-[grid-template-columns] duration-300", "grid transition-[grid-template-columns] duration-300",
isActive isActive ? "grid-cols-[1fr]" : "grid-cols-[0fr]"
? "grid-cols-[1fr]"
: "grid-cols-[0fr]"
)} )}
> >
<span <span

View File

@@ -1,12 +1,7 @@
"use client" "use client"
import { useTranslations } from "next-intl" import { useTranslations } from "next-intl"
import { import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@/components/ui/tabs"
import { ShadcnLauncher } from "./shadcn/shadcn-launcher" import { ShadcnLauncher } from "./shadcn/shadcn-launcher"
export function ProjectBootWorkspace() { export function ProjectBootWorkspace() {

View File

@@ -1,12 +1,6 @@
// ── Preset encoding/decoding (matches shadcn v2 format) ───────────── // ── Preset encoding/decoding (matches shadcn v2 format) ─────────────
const PRESET_STYLES = [ const PRESET_STYLES = ["nova", "vega", "maia", "lyra", "mira"] as const
"nova",
"vega",
"maia",
"lyra",
"mira",
] as const
const PRESET_BASE_COLORS = [ const PRESET_BASE_COLORS = [
"neutral", "neutral",
@@ -84,13 +78,7 @@ const PRESET_FONTS = [
const PRESET_FONT_HEADINGS = ["inherit", ...PRESET_FONTS] as const const PRESET_FONT_HEADINGS = ["inherit", ...PRESET_FONTS] as const
const PRESET_RADII = [ const PRESET_RADII = ["default", "none", "small", "medium", "large"] as const
"default",
"none",
"small",
"medium",
"large",
] as const
const PRESET_MENU_ACCENTS = ["subtle", "bold"] as const const PRESET_MENU_ACCENTS = ["subtle", "bold"] as const
@@ -115,8 +103,7 @@ const PRESET_FIELDS_V2 = [
{ key: "fontHeading", values: PRESET_FONT_HEADINGS, bits: 5 }, { key: "fontHeading", values: PRESET_FONT_HEADINGS, bits: 5 },
] as const ] as const
const BASE62 = const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
function toBase62(num: number): string { function toBase62(num: number): string {
if (num === 0) return "0" if (num === 0) return "0"

View File

@@ -14,12 +14,7 @@ import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch" import { Switch } from "@/components/ui/switch"
import { import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@/components/ui/tabs"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { import {
Field, Field,

View File

@@ -52,7 +52,11 @@ const CONFIG_FIELDS: {
options: { value: string; label: string }[] options: { value: string; label: string }[]
}[] = [ }[] = [
{ key: "style", i18nKey: "config.style", options: STYLE_OPTIONS }, { key: "style", i18nKey: "config.style", options: STYLE_OPTIONS },
{ key: "baseColor", i18nKey: "config.baseColor", options: BASE_COLOR_OPTIONS }, {
key: "baseColor",
i18nKey: "config.baseColor",
options: BASE_COLOR_OPTIONS,
},
{ key: "theme", i18nKey: "config.theme", options: THEME_OPTIONS }, { key: "theme", i18nKey: "config.theme", options: THEME_OPTIONS },
{ key: "chartColor", i18nKey: "config.chartColor", options: THEME_OPTIONS }, { key: "chartColor", i18nKey: "config.chartColor", options: THEME_OPTIONS },
{ {
@@ -66,8 +70,16 @@ const CONFIG_FIELDS: {
i18nKey: "config.fontHeading", i18nKey: "config.fontHeading",
options: FONT_HEADING_OPTIONS, options: FONT_HEADING_OPTIONS,
}, },
{ key: "menuAccent", i18nKey: "config.menuAccent", options: MENU_ACCENT_OPTIONS }, {
{ key: "menuColor", i18nKey: "config.menuColor", options: MENU_COLOR_OPTIONS }, key: "menuAccent",
i18nKey: "config.menuAccent",
options: MENU_ACCENT_OPTIONS,
},
{
key: "menuColor",
i18nKey: "config.menuColor",
options: MENU_COLOR_OPTIONS,
},
{ key: "radius", i18nKey: "config.radius", options: RADIUS_OPTIONS }, { key: "radius", i18nKey: "config.radius", options: RADIUS_OPTIONS },
{ key: "template", i18nKey: "config.template", options: TEMPLATE_OPTIONS }, { key: "template", i18nKey: "config.template", options: TEMPLATE_OPTIONS },
] ]

View File

@@ -56,7 +56,7 @@ export function FolderActions() {
className="justify-start gap-2 h-9" className="justify-start gap-2 h-9"
onClick={async () => { onClick={async () => {
try { try {
await openProjectBootWindow() await openProjectBootWindow("welcome")
} catch (err) { } catch (err) {
console.error("[FolderActions] failed to open project boot:", err) console.error("[FolderActions] failed to open project boot:", err)
toast.error(t("toasts.openProjectBootFailed")) toast.error(t("toasts.openProjectBootFailed"))

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "مجلد", "fallbackFolderName": "مجلد",
"openFolder": "فتح مجلد", "openFolder": "فتح مجلد",
"cloneRepository": "استنساخ المستودع", "cloneRepository": "استنساخ المستودع",
"projectBoot": "مُنشئ المشروع",
"opened": "مفتوح", "opened": "مفتوح",
"recentOpen": "المفتوح مؤخرًا" "recentOpen": "المفتوح مؤخرًا"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "Ordner", "fallbackFolderName": "Ordner",
"openFolder": "Ordner öffnen", "openFolder": "Ordner öffnen",
"cloneRepository": "Repository klonen", "cloneRepository": "Repository klonen",
"projectBoot": "Projekt-Boot",
"opened": "Geöffnet", "opened": "Geöffnet",
"recentOpen": "Zuletzt geöffnet" "recentOpen": "Zuletzt geöffnet"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "Folder", "fallbackFolderName": "Folder",
"openFolder": "Open Folder", "openFolder": "Open Folder",
"cloneRepository": "Clone Repository", "cloneRepository": "Clone Repository",
"projectBoot": "Project Boot",
"opened": "Opened", "opened": "Opened",
"recentOpen": "Recently Opened" "recentOpen": "Recently Opened"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "Carpeta", "fallbackFolderName": "Carpeta",
"openFolder": "Abrir carpeta", "openFolder": "Abrir carpeta",
"cloneRepository": "Clonar repositorio", "cloneRepository": "Clonar repositorio",
"projectBoot": "Inicializador de proyecto",
"opened": "Abierto", "opened": "Abierto",
"recentOpen": "Abiertos recientemente" "recentOpen": "Abiertos recientemente"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "Dossier", "fallbackFolderName": "Dossier",
"openFolder": "Ouvrir le dossier", "openFolder": "Ouvrir le dossier",
"cloneRepository": "Cloner le dépôt", "cloneRepository": "Cloner le dépôt",
"projectBoot": "Lanceur de projet",
"opened": "Ouvert", "opened": "Ouvert",
"recentOpen": "Ouvert récemment" "recentOpen": "Ouvert récemment"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "フォルダ", "fallbackFolderName": "フォルダ",
"openFolder": "フォルダを開く", "openFolder": "フォルダを開く",
"cloneRepository": "リポジトリをクローン", "cloneRepository": "リポジトリをクローン",
"projectBoot": "プロジェクトブート",
"opened": "開いているフォルダ", "opened": "開いているフォルダ",
"recentOpen": "最近開いたフォルダ" "recentOpen": "最近開いたフォルダ"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "폴더", "fallbackFolderName": "폴더",
"openFolder": "폴더 열기", "openFolder": "폴더 열기",
"cloneRepository": "리포지토리 클론", "cloneRepository": "리포지토리 클론",
"projectBoot": "프로젝트 부트",
"opened": "열린 항목", "opened": "열린 항목",
"recentOpen": "최근 연 항목" "recentOpen": "최근 연 항목"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "Pasta", "fallbackFolderName": "Pasta",
"openFolder": "Abrir pasta", "openFolder": "Abrir pasta",
"cloneRepository": "Clonar repositório", "cloneRepository": "Clonar repositório",
"projectBoot": "Inicializador de projeto",
"opened": "Aberto", "opened": "Aberto",
"recentOpen": "Abertos recentemente" "recentOpen": "Abertos recentemente"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "文件夹", "fallbackFolderName": "文件夹",
"openFolder": "打开文件夹", "openFolder": "打开文件夹",
"cloneRepository": "克隆仓库", "cloneRepository": "克隆仓库",
"projectBoot": "项目启动器",
"opened": "已打开", "opened": "已打开",
"recentOpen": "最近打开" "recentOpen": "最近打开"
}, },

View File

@@ -856,6 +856,7 @@
"fallbackFolderName": "資料夾", "fallbackFolderName": "資料夾",
"openFolder": "打開資料夾", "openFolder": "打開資料夾",
"cloneRepository": "複製倉庫", "cloneRepository": "複製倉庫",
"projectBoot": "專案啟動器",
"opened": "已打開", "opened": "已打開",
"recentOpen": "最近打開" "recentOpen": "最近打開"
}, },

View File

@@ -952,9 +952,9 @@ export async function openSettingsWindow(
window.open(result.path, `settings-${section ?? "general"}`) window.open(result.path, `settings-${section ?? "general"}`)
} }
export async function openProjectBootWindow(): Promise<void> { export async function openProjectBootWindow(source?: string): Promise<void> {
if (getTransport().isDesktop()) { if (getTransport().isDesktop()) {
return getTransport().call("open_project_boot_window") return getTransport().call("open_project_boot_window", { source })
} }
window.open("/project-boot", "project-boot") window.open("/project-boot", "project-boot")
} }