Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions packages/app/e2e/app/home.spec.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,6 @@
import { test, expect } from "../fixtures"
import { openSidebar } from "../actions"
import { promptSelector, sessionComposerDockSelector } from "../selectors"

test("@smoke root route renders the no-project empty state", async ({ page, backend }) => {
await page.route(`${backend.url}/path**`, (route) =>
route.fulfill({
contentType: "application/json",
body: JSON.stringify({ home: "", state: "", config: "", worktree: "", directory: "" }),
}),
)
await page.route(`${backend.url}/project**`, (route) =>
route.fulfill({
contentType: "application/json",
body: JSON.stringify([]),
}),
)
await page.addInitScript((url) => {
localStorage.setItem(
"pawwork.global.dat:server",
JSON.stringify({ list: [url], projects: {}, lastProject: {} }),
)
localStorage.setItem("pawwork.settings.dat:defaultServerUrl", url)
localStorage.setItem("pawwork.global.dat:globalSync.project", JSON.stringify({ value: [] }))
}, backend.url)

await page.goto("/")

const home = page.locator('[data-component="session-new-home"]')
const main = page.getByRole("main")
const emptyTitle = page.getByText(/No recent projects|没有最近项目/)
const emptyDescription = page.getByText(/Get started by opening a local project|通过打开本地项目开始使用/)
const openProject = main.getByRole("button", { name: /Open project|打开项目/ })

await expect(home).toHaveCount(0)
await expect(emptyTitle).toBeVisible()
await expect(emptyDescription).toBeVisible()
await expect(openProject).toBeVisible()
await expect(page.locator(sessionComposerDockSelector)).toHaveCount(0)

await openSidebar(page)
await expect(page.getByText(/No projects open|还没有打开项目/)).toBeVisible()
await expect(page.getByText(/Open a project to start using the session-first sidebar\.|打开一个项目后,即可使用以会话为中心的侧边栏。/)).toBeVisible()
await openProject.click()
await expect(page.getByRole("dialog", { name: /Open project|打开项目/ })).toBeVisible()
})

test("@smoke home renders the hero composer and starter cards", async ({ page, project }) => {
await project.open()

Expand Down
18 changes: 18 additions & 0 deletions packages/app/e2e/app/root-redirect.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { test, expect } from "../fixtures"

test("@smoke root route falls back to backend project when local store is empty", async ({ page, project }) => {
await project.open()

await page.evaluate(() => {
const key = "pawwork.global.dat:server"
const raw = localStorage.getItem(key)
if (!raw) return
const parsed = JSON.parse(raw) as { projects?: Record<string, unknown> }
parsed.projects = {}
localStorage.setItem(key, JSON.stringify(parsed))
})

await page.goto("/")

await expect(page).toHaveURL(/\/[A-Za-z0-9_-]+\/session/)
})
27 changes: 23 additions & 4 deletions packages/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import { CommandProvider } from "@/context/command"
import { CommentsProvider } from "@/context/comments"
import { FileProvider } from "@/context/file"
import { GlobalSDKProvider } from "@/context/global-sdk"
import { GlobalSyncProvider } from "@/context/global-sync"
import { GlobalSyncProvider, useGlobalSync } from "@/context/global-sync"
import { HighlightsProvider } from "@/context/highlights"
import { LanguageProvider, type Locale, useLanguage } from "@/context/language"
import { LayoutProvider } from "@/context/layout"
import { LayoutProvider, useLayout } from "@/context/layout"
import { ModelsProvider } from "@/context/models"
import { NotificationProvider } from "@/context/notification"
import { PermissionProvider } from "@/context/permission"
Expand All @@ -49,8 +49,8 @@ import { ErrorPage } from "./pages/error"
import { buildDesktopContext, desktopWindowTitle, type DesktopContext } from "./utils/desktop-context"
import type { RendererDiagnosticInput, RendererDiagnosticsExportResult } from "@/context/platform"
import { useCheckServerHealth } from "./utils/server-health"
import { base64Encode } from "@opencode-ai/util/encode"

const HomeRoute = lazy(() => import("@/pages/home"))
const loadSession = () => import("@/pages/session")
const Session = lazy(loadSession)
const Loading = () => <div class="size-full" />
Expand All @@ -67,6 +67,25 @@ const SessionRoute = () => (

const SessionIndexRoute = () => <Navigate href="session" />

const HomeRedirectRoute = () => {
const layout = useLayout()
const sync = useGlobalSync()
const target = createMemo(() => {
const local = layout.projects.list()[0]?.worktree
if (local) return local
if (!sync.ready) return undefined
return sync.data.project[0]?.worktree
})
return (
<Show
when={target()}
fallback={<div data-component="home-redirect-pending" class="size-full" aria-hidden="true" />}
>
{(directory) => <Navigate href={`/${base64Encode(directory())}/session`} />}
</Show>
)
}

type WebSearchStatus = {
source: "saved" | "env" | "anonymous"
configured: boolean
Expand Down Expand Up @@ -406,7 +425,7 @@ export function AppInterface(props: {
component={props.router ?? Router}
root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>}
>
<Route path="/" component={HomeRoute} />
<Route path="/" component={HomeRedirectRoute} />
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={SessionIndexRoute} />
<Route path="/session/:id?" component={SessionRoute} />
Expand Down
2 changes: 0 additions & 2 deletions packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,6 @@ export const dict = {
"notification.session.error.fallbackDescription": "An error occurred",

"home.recentProjects": "Recent projects",
"home.empty.title": "No recent projects",
"home.empty.description": "Get started by opening a local project",

"session.tab.session": "Session",
"session.tab.review": "Review",
Expand Down
2 changes: 0 additions & 2 deletions packages/app/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,6 @@ export const dict = {
"notification.session.error.fallbackDescription": "发生错误",

"home.recentProjects": "最近项目",
"home.empty.title": "没有最近项目",
"home.empty.description": "通过打开本地项目开始使用",

"session.tab.session": "会话",
"session.tab.review": "审查",
Expand Down
139 changes: 0 additions & 139 deletions packages/app/src/pages/home.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion packages/opencode/test/config/e2e-smoke-tagging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const expectedSmokeTests = [
"packages/app/e2e/app/home.spec.ts:@smoke home hero prompt starts a session",
"packages/app/e2e/app/home.spec.ts:@smoke home renders the hero composer and starter cards",
"packages/app/e2e/app/home.spec.ts:@smoke project home status panel can open the server picker dialog",
"packages/app/e2e/app/home.spec.ts:@smoke root route renders the no-project empty state",
"packages/app/e2e/app/navigation.spec.ts:@smoke project route redirects to /session",
"packages/app/e2e/app/root-redirect.spec.ts:@smoke root route falls back to backend project when local store is empty",
"packages/app/e2e/app/session.spec.ts:@smoke session composer matches home structure without docktray or agent control",
"packages/app/e2e/app/shell-frame.spec.ts:@smoke shell frame exposes stable desktop hooks",
"packages/app/e2e/files/file-tree.spec.ts:@smoke review tab no longer renders the legacy file-tree sub-panel",
Expand Down
Loading