diff --git a/AMICODE-PATCHES.md b/AMICODE-PATCHES.md index e89ce2c289..0834bb663e 100644 --- a/AMICODE-PATCHES.md +++ b/AMICODE-PATCHES.md @@ -225,3 +225,23 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build. - index.css: @font-face for both (JuliaMono full glyph set — Julia Unicode; Racing Sans One latin subset, font-display swap). logo.tsx + wordmark-v2.tsx: font-family 'Racing Sans One' first, weight 700→400. settings.tsx: monoDefault/monoFallback lead with JuliaMono. theme.css: --font-family-mono leads with JuliaMono. Terminal font DELIBERATELY unchanged (JetBrainsMono Nerd Font Mono via separate terminalFallback). - New assets (git-added — build breaks without them): public/assets/RacingSansOne-Regular.woff2 (21 KB) + JuliaMono-Regular.woff2 (946 KB). - Font build sha256: `2a15da111be516516fb1fbd1a4fb5ae02ad9bddd6373ede08cdf7b28c19d163a` (dist/opencode-local + vendored path, write-temp + mv -f swap; SUPERSEDES #13's a73d8583… — same code, fonts now committed). Verify (scratch port 14099): `GET /assets/RacingSansOne-Regular.woff2` → 200 font/woff2 21804 B; `GET /assets/JuliaMono-Regular.woff2` → 200 font/woff2 946516 B; "Racing Sans One" in built css + index/new-session chunks, "JuliaMono" in `index-Dwtxigfs.css`; `GET /amicode/problems` → 200; `GET /` → 200 `Amicode`; ui `bun test src` → 70 pass; typecheck green ui+app (no snapshots assert fonts, per Aaron — confirmed nothing went red). Bonus confirmation: KaTeX\_\* woff2 assets now in dist — the entity view's katex import (#13) pulls its font set into the embed. +9. (home CTA fallback) — amicode(home): "Open chat" works on a fresh profile. + - BUG: `startWithPrompt` (fork wiring for the Meet-Amico card, patch 5ef6b7e0e) dead-ended + silently when the persisted client-side project list was empty (fresh browser profile + against a bare `opencode serve`): the `!project` branch called `openNewSession()`, which + needs the SAME empty `newSessionProject()` and hits `if (!conn || !project) return`. + Primary home CTA did nothing, no error. Hit live 2026-07-08 (web UI on a scratch dir). + - FIX (packages/app/src/pages/home.tsx, `startWithPrompt` only): when no project is + tracked, fall back to the focused server's own working directory — + `focusedSync().data.path.directory` (synced from GET /path; "" until loaded, so the + falsy guard holds) — open+touch it as a project (self-heals the home page), then + `tabs.newDraft` with the prompt preserved. Deliberately NOT `sync.data.project`: + the server's "global" project record has worktree "/". + - Regression spec: packages/app/e2e/regression/home-open-chat-empty-projects.spec.ts — + fresh profile (NO localStorage seed), mocked server, click the CTA (`exact: true` — + the whole card is also a button whose accessible name contains "Open chat"), expect + navigation to `/new-session?draftId=` + the cwd persisted as a tracked project. + Verified failing on the unfixed code, passing with the fix. Playwright note: config + reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=` + if something else (e.g. the harmoniqs website dev server) holds 3000. + - Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail. diff --git a/packages/app/e2e/regression/home-open-chat-empty-projects.spec.ts b/packages/app/e2e/regression/home-open-chat-empty-projects.spec.ts new file mode 100644 index 0000000000..39ee2ca9cc --- /dev/null +++ b/packages/app/e2e/regression/home-open-chat-empty-projects.spec.ts @@ -0,0 +1,33 @@ +import { expect, test } from "@playwright/test" +import { fixture, pageMessages } from "../smoke/session-timeline.fixture" +import { mockOpenCodeServer } from "../utils/mock-server" + +// Regression: on a fresh profile (no tracked projects in localStorage) the +// home "Open chat" CTA dead-ended silently — startWithPrompt fell through to +// openNewSession(), which needs the same newSessionProject() that just came +// back empty. It must instead fall back to the server's own working +// directory (GET /path → .directory) and start a draft there, tracking the +// directory as a project so the rest of the home page works from then on. +test("home 'Open chat' falls back to the server cwd on a fresh profile", async ({ page }) => { + await mockOpenCodeServer(page, { + sessions: [], + provider: fixture.provider, + directory: fixture.directory, + project: fixture.project, + pageMessages, + }) + + // Deliberately NO localStorage seed — an empty tracked-project list is the + // regression condition (contrast: session-list-path-loading.spec.ts seeds it). + await page.goto("/") + // exact: true — the whole Meet-Amico card is also a button whose accessible + // name contains "Open chat"; we want the CTA inside it. + await page.getByRole("button", { name: "Open chat", exact: true }).click() + + // Navigates to a new-session draft instead of doing nothing. + await expect(page).toHaveURL(/\/new-session\?draftId=/) + + // And the server cwd is now a tracked project (the self-healing part). + const persisted = await page.evaluate(() => localStorage.getItem("opencode.global.dat:server") ?? "") + expect(persisted).toContain(fixture.directory) +}) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index aa0d177af6..cb199904d3 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -341,11 +341,23 @@ function HomeDesign() { const [sessionsExpanded, setSessionsExpanded] = createSignal(false) function startWithPrompt(prompt: string) { const project = newSessionProject() - if (!project) { - openNewSession() + if (project) { + tabs.newDraft({ server: server.key, directory: project.worktree }, prompt) return } - tabs.newDraft({ server: server.key, directory: project.worktree }, prompt) + // No tracked projects (fresh profile against a bare `opencode serve`): + // openNewSession() would dead-end silently here — it needs the same + // newSessionProject() that just came back empty. Fall back to the server's + // own working directory (path.directory, synced from GET /path; "" until + // loaded) and start tracking it, so the home CTAs work on first visit. + // Deliberately NOT sync.data.project: its "global" record has worktree "/". + const conn = focusedServer() + const directory = focusedSync().data.path.directory + if (!conn || !directory) return + const ctx = global.createServerCtx(conn) + ctx.projects.open(directory) + ctx.projects.touch(directory) + tabs.newDraft({ server: ServerConnection.key(conn), directory }, prompt) } function setSelection(next: HomeProjectSelection) {