diff --git a/windows/tauri/src/features/file-system/stores/file-system.store.ts b/windows/tauri/src/features/file-system/stores/file-system.store.ts index c051d9418..a154ab221 100644 --- a/windows/tauri/src/features/file-system/stores/file-system.store.ts +++ b/windows/tauri/src/features/file-system/stores/file-system.store.ts @@ -43,6 +43,7 @@ import { restoreProjectUiState, } from "@/features/window/stores/workspace-ui-session"; import { useWorkspaceTabsStore } from "@/features/window/stores/workspace-tabs.store"; +import { getProjectDisplayLabel } from "@/features/window/utils/project-display-label"; import { createAppWindow } from "@/features/window/utils/create-app-window"; import { serializeTerminals } from "@/features/terminal/lib/terminal-session-storage"; import { useTerminalTabsStore } from "@/features/terminal/stores/terminal-tabs.store"; @@ -641,7 +642,7 @@ const openLocalWorkspace = async ( state.isFileTreeLoading = true; }); - const projectName = getFolderName(path); + const folderName = getFolderName(path); const readDirectoryStartedAt = performance.now(); logWorkspaceOpenStep("start", "readDirectoryContents", path); @@ -649,7 +650,7 @@ const openLocalWorkspace = async ( logWorkspaceOpenStep("end", "readDirectoryContents", path, readDirectoryStartedAt); const fileTree = sortFileEntries(entries); - const wrappedFileTree = wrapWithRootFolder(fileTree, path, projectName); + const wrappedFileTree = wrapWithRootFolder(fileTree, path, folderName); if (treeState === "expand-root") { fileTreeStore.getState().actions.setExpandedPaths(new Set([path])); @@ -657,18 +658,20 @@ const openLocalWorkspace = async ( fileTreeStore.getState().actions.collapseAll(); } + const workspaceTab = useWorkspaceTabsStore + .getState() + .projectTabs.find((projectTab) => projectTab.id === workspaceId); + const displayName = workspaceTab ? getProjectDisplayLabel(workspaceTab) : folderName; + const { setRootFolderPath, setProjectName, setActiveProjectId } = projectStore.getState().actions; setRootFolderPath(path); - setProjectName(projectName); + setProjectName(displayName); if (restoreUiState) { restoreProjectUiState(path, workspaceId); } - const workspaceTab = useWorkspaceTabsStore - .getState() - .projectTabs.find((projectTab) => projectTab.id === workspaceId); setActiveProjectId(workspaceId); if (!prewarm) { useRecentFoldersStore.getState().actions.addToRecents(path, { @@ -683,7 +686,7 @@ const openLocalWorkspace = async ( state.isFileTreeLoading = false; state.files = wrappedFileTree; state.rootFolderPath = path; - state.workspaceFolders = [{ path, name: projectName, isPrimary: true }]; + state.workspaceFolders = [{ path, name: folderName, isPrimary: true }]; state.filesVersion++; state.projectFilesCache = undefined; }); @@ -1432,6 +1435,9 @@ const createFileSystemStore = (workspaceId: string): StoreApi { const isRemote = isRemoteProjectPath(project.path); const isActive = project.id === activeProjectId; + const displayName = getProjectDisplayLabel(project); return ( @@ -67,8 +71,8 @@ export function SidebarProjectDots({ )} aria-label={ isActive - ? t("titleProject.currentProject", { project: project.name }) - : t("titleProject.switchToProject", { project: project.name }) + ? t("titleProject.currentProject", { project: displayName }) + : t("titleProject.switchToProject", { project: displayName }) } aria-current={isActive ? "page" : undefined} aria-disabled={isSwitchingProject} @@ -138,6 +142,10 @@ export function SidebarProjectDots({ {t("titleProject.selectIcon")} ) : null} + void promptProjectDisplayAlias(project)}> + + {t("titleProject.setDisplayAlias")} + ({ ok: true as const, data: { document: "{}" }, })); +const cancelCoreOperation = mock(async () => true); -mock.module("@/core/lithe-core-client", () => ({ executeCore })); +mock.module("@/core/lithe-core-client", () => ({ executeCore, cancelCoreOperation })); const { createLaunchPlan, saveRunConfigurationEditorChanges } = await import("./run-core-api"); diff --git a/windows/tauri/src/features/run/api/run-host-api.test.ts b/windows/tauri/src/features/run/api/run-host-api.test.ts index a026a0227..4782e52dc 100644 --- a/windows/tauri/src/features/run/api/run-host-api.test.ts +++ b/windows/tauri/src/features/run/api/run-host-api.test.ts @@ -1,17 +1,22 @@ +import { Channel } from "@tauri-apps/api/core"; import { beforeEach, describe, expect, mock, test } from "bun:test"; const invoke = mock(async () => undefined); -const getCurrentWebviewWindow = mock(() => ({ label: "project-window" })); -mock.module("@/platform/tauri-core", () => ({ invoke })); -mock.module("@tauri-apps/api/webviewWindow", () => ({ getCurrentWebviewWindow })); +mock.module("@/platform/tauri-core", () => ({ + invoke, + Channel, + convertFileSrc: (path: string) => path, +})); +mock.module("../utils/run-window-context", () => ({ + getRunWindowLabel: () => "project-window", +})); const { startRunProcess, stopRunProcess, writeRunStdin } = await import("../api/run-host-api"); describe("run host API window scoping", () => { beforeEach(() => { invoke.mockClear(); - getCurrentWebviewWindow.mockClear(); }); test("startRunProcess includes the current window label", async () => { diff --git a/windows/tauri/src/features/run/hooks/use-run-process-events.test.ts b/windows/tauri/src/features/run/hooks/use-run-process-events.test.ts index 430d4e784..757a4f0b4 100644 --- a/windows/tauri/src/features/run/hooks/use-run-process-events.test.ts +++ b/windows/tauri/src/features/run/hooks/use-run-process-events.test.ts @@ -12,6 +12,8 @@ const appendOutput = mock(() => undefined); const finishProcess = mock(() => undefined); const releaseRunSessionWorkspace = mock(() => undefined); +const actualRunStore = await import("../stores/run.store"); + mock.module("@tauri-apps/api/webviewWindow", () => ({ getCurrentWebviewWindow: () => ({ label: "project-window", @@ -20,6 +22,7 @@ mock.module("@tauri-apps/api/webviewWindow", () => ({ })); mock.module("@tauri-apps/api/event", () => ({ listen: globalListen })); mock.module("../stores/run.store", () => ({ + ...actualRunStore, runStoreForSession: () => ({ getState: () => ({ actions: { appendOutput, finishProcess }, diff --git a/windows/tauri/src/features/run/stores/run.store.test.ts b/windows/tauri/src/features/run/stores/run.store.test.ts index f363151a1..3a315ded7 100644 --- a/windows/tauri/src/features/run/stores/run.store.test.ts +++ b/windows/tauri/src/features/run/stores/run.store.test.ts @@ -1,9 +1,6 @@ -import { describe, expect, mock, test } from "bun:test"; - -mock.module("@/platform/tauri-core", () => ({ - invoke: mock(async () => undefined), -})); +import { describe, expect, test } from "bun:test"; +// Window scoping is mocked in run-host-api.test.ts for the full `bun test src/features/run` suite. const { createRunStore } = await import("./run.store"); const { PRIMARY_SESSION_ID } = await import("../types/run.types"); diff --git a/windows/tauri/src/features/window/components/project-tab-bar.test.ts b/windows/tauri/src/features/window/components/project-tab-bar.test.ts index 4e7bd08a9..be392832c 100644 --- a/windows/tauri/src/features/window/components/project-tab-bar.test.ts +++ b/windows/tauri/src/features/window/components/project-tab-bar.test.ts @@ -14,7 +14,7 @@ test("project tab bar exposes accessible switchable and closable project tabs", expect(source).toContain("event.stopPropagation()"); expect(source).toContain("await closeProject(projectId)"); expect(source).toContain("finally"); - expect(source).toContain('t("titleProject.closeProject", { name: project.name })'); + expect(source).toContain('t("titleProject.closeProject", { name: displayName })'); expect(source).toContain("group-hover:opacity-100"); expect(source).toContain("group-focus-within:opacity-100"); expect(source).toContain( diff --git a/windows/tauri/src/features/window/components/project-tab-bar.tsx b/windows/tauri/src/features/window/components/project-tab-bar.tsx index 74e1cf859..09e15a289 100644 --- a/windows/tauri/src/features/window/components/project-tab-bar.tsx +++ b/windows/tauri/src/features/window/components/project-tab-bar.tsx @@ -5,6 +5,7 @@ import { useWorkspaceTabsStore } from "@/features/window/stores/workspace-tabs.s import { Button } from "@/ui/button"; import { FolderIcon, XIcon as X } from "@/ui/icons"; import { cn } from "@/utils/cn"; +import { getProjectDisplayLabel } from "../utils/project-display-label"; import { getProjectTabBarItems, shouldShowProjectTabBar } from "../utils/project-tab-bar-model"; interface ProjectTabBarProps { @@ -43,7 +44,8 @@ export function ProjectTabBar({ hideWhenSingle = false }: ProjectTabBarProps) { >
{projects.map((project) => { - const closeLabel = t("titleProject.closeProject", { name: project.name }); + const displayName = getProjectDisplayLabel(project); + const closeLabel = t("titleProject.closeProject", { name: displayName }); return (
@@ -71,7 +73,7 @@ export function ProjectTabBar({ hideWhenSingle = false }: ProjectTabBarProps) { )} aria-hidden="true" /> - {project.name} + {displayName} {project.isActive ? (