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
55 changes: 41 additions & 14 deletions packages/app/e2e/app/home.spec.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import { test, expect } from "../fixtures"
import { promptSelector, sessionComposerDockSelector } from "../selectors"

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

const home = page.locator('[data-component="session-new-home"]')
const composer = home.locator(sessionComposerDockSelector)
const firstCard = home.getByRole("button", { name: /Process docs/i })
const workspaceChip = page.getByRole("button", { name: /Switch workspace|切换工作目录/i })
await expect(home).toBeVisible()
await expect(page.getByRole("heading", { name: "What do you want to do?" })).toBeVisible()
await expect(page.locator(sessionComposerDockSelector)).toHaveCount(1)
await expect(composer).toHaveCount(1)
await expect(composer).toHaveCSS("text-align", "left")
await expect(home.locator(promptSelector)).toBeVisible()
await expect(firstCard).toBeVisible()
await expect(page.getByRole("button", { name: /Analyze data/i })).toBeVisible()
await expect(page.getByRole("button", { name: /Start writing/i })).toBeVisible()
await expect(page.getByRole("button", { name: "Right utility panel" })).toBeVisible()
await expect(workspaceChip).toBeVisible()

const cardBox = await firstCard.boundingBox()
const composerBox = await composer.boundingBox()
expect(cardBox).not.toBeNull()
expect(composerBox).not.toBeNull()
expect(cardBox!.y).toBeGreaterThan(composerBox!.y)
// Skill-card shortcuts removed in #603 PR2 — slash commands typed directly in
// the composer remain available for the same productivity skills.
await expect(home.getByRole("button", { name: /Process docs/i })).toHaveCount(0)
await expect(home.getByRole("button", { name: /Analyze data/i })).toHaveCount(0)
await expect(home.getByRole("button", { name: /Start writing/i })).toHaveCount(0)
})

test("@smoke home hero prompt starts a session", async ({ page, project, assistant }) => {
Expand All @@ -43,6 +39,29 @@ test("@smoke home hero prompt starts a session", async ({ page, project, assista
await expect(page.getByText("home hero reply")).toBeVisible()
})

test("@smoke home composer submits a slash-prefixed prompt via the fallback path", async ({
page,
project,
assistant,
}) => {
// Guards the #603 PR2 simplification of submit.ts: removing the `!homeSkill`
// gate must not break the slash-prefix fall-through. A leading `/` that does
// not match a registered command should still fall through to the standard
// prompt path. Using an unregistered command name keeps the test independent
// of which backend slash commands are bundled in the fixture.
await project.open()

const home = page.locator('[data-component="session-new-home"]')
const prompt = home.locator(sessionComposerDockSelector).locator(promptSelector)
await expect(prompt).toBeVisible()
await assistant.reply("slash hero reply")
await page.keyboard.type("/pr2skillcheck verify slash submit")
await page.keyboard.press("Enter")

await expect.poll(() => page.url(), { timeout: 30_000 }).toContain("/session/")
await expect(page.getByText("slash hero reply")).toBeVisible()
})

test("@smoke home composer shows unified single-row bar with brand orange send", async ({ page, project }) => {
await project.open()

Expand All @@ -54,15 +73,23 @@ test("@smoke home composer shows unified single-row bar with brand orange send",
// no DockTray tray surface above the input
await expect(composer.locator('[data-dock-surface="tray"]')).toHaveCount(0)

// brand orange enables only when input has content, type first
const prompt = home.locator(promptSelector)
await prompt.click()
await page.keyboard.type("x")

const send = composer.locator('[data-action="prompt-submit"]')

// send is disabled while the prompt is blank — guards the readiness rule
// that #603 PR2 simplified after removing the selectedSkill bypass.
await expect(send).toBeVisible()
await expect(send).toBeDisabled()

// brand orange enables only when input has content
await prompt.click()
await page.keyboard.type("x")
await expect(send).toBeEnabled()

// clearing the prompt returns send to disabled
await page.keyboard.press("Backspace")
await expect(send).toBeDisabled()

// WorkspaceChip present on home
const workspaceChip = page.getByRole("button", { name: /Switch workspace|切换工作目录/i })
await expect(workspaceChip).toBeVisible()
Expand Down
5 changes: 0 additions & 5 deletions packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import { PromptDragOverlay } from "./prompt-input/drag-overlay"
import { promptPlaceholder } from "./prompt-input/placeholder"
import { promptSendDisabled } from "./prompt-input/readiness"
import { ImagePreview } from "@opencode-ai/ui/image-preview"
import type { PawworkSkillName } from "@/components/session/pawwork-skill-meta"

interface PromptInputProps {
class?: string
Expand All @@ -64,7 +63,6 @@ interface PromptInputProps {
sessionIDControlled?: boolean
actionReady?: () => boolean
abortReady?: () => boolean
selectedSkill?: () => PawworkSkillName | undefined
}

const EXAMPLES = [
Expand Down Expand Up @@ -242,7 +240,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
commentCount: commentCount(),
example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "",
suggest: suggest(),
selectedSkill: props.selectedSkill?.(),
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
})
: language.t("prompt.loading"),
Expand Down Expand Up @@ -458,7 +455,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
resetHistoryNavigation(true)
},
setMode: (mode) => setStore("mode", mode),
selectedSkill: props.selectedSkill,
setPopover: (popover) => setStore("popover", popover),
newSessionWorktree: () => props.newSessionWorktree,
onNewSessionWorktreeReset: props.onNewSessionWorktreeReset,
Expand Down Expand Up @@ -689,7 +685,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
actionReady: actionReady(),
abortReady: abortReady(),
blank: blank(),
selectedSkill: !!props.selectedSkill?.(),
})}
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
Expand Down
33 changes: 0 additions & 33 deletions packages/app/src/components/prompt-input/home-override.test.ts

This file was deleted.

11 changes: 0 additions & 11 deletions packages/app/src/components/prompt-input/home-override.ts

This file was deleted.

66 changes: 0 additions & 66 deletions packages/app/src/components/prompt-input/placeholder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,70 +45,4 @@ describe("promptPlaceholder", () => {
})
expect(value).toBe("prompt.placeholder.simple")
})

test("selected Skill picks the per-Skill key in normal mode", () => {
expect(
promptPlaceholder({
mode: "normal",
commentCount: 0,
example: "example",
suggest: true,
selectedSkill: "document-processing",
t,
}),
).toBe("session.new.placeholder.document")
expect(
promptPlaceholder({
mode: "normal",
commentCount: 0,
example: "example",
suggest: false,
selectedSkill: "data-analysis",
t,
}),
).toBe("session.new.placeholder.analysis")
expect(
promptPlaceholder({
mode: "normal",
commentCount: 0,
example: "example",
suggest: true,
selectedSkill: "writing-assistant",
t,
}),
).toBe("session.new.placeholder.writing")
})

test("selected Skill wins over suggest but loses to shell mode and to comment context", () => {
expect(
promptPlaceholder({
mode: "shell",
commentCount: 0,
example: "example",
suggest: true,
selectedSkill: "document-processing",
t,
}),
).toBe("prompt.placeholder.shell")
expect(
promptPlaceholder({
mode: "normal",
commentCount: 2,
example: "example",
suggest: true,
selectedSkill: "data-analysis",
t,
}),
).toBe("prompt.placeholder.summarizeComments")
expect(
promptPlaceholder({
mode: "normal",
commentCount: 1,
example: "example",
suggest: true,
selectedSkill: "data-analysis",
t,
}),
).toBe("prompt.placeholder.summarizeComment")
})
})
10 changes: 0 additions & 10 deletions packages/app/src/components/prompt-input/placeholder.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import type { PawworkSkillName } from "@/components/session/pawwork-skill-meta"

type PromptPlaceholderInput = {
mode: "normal" | "shell"
commentCount: number
example: string
suggest: boolean
selectedSkill?: PawworkSkillName
t: (key: string, params?: Record<string, string>) => string
}

const SKILL_PLACEHOLDER_KEY: Record<PawworkSkillName, string> = {
"document-processing": "session.new.placeholder.document",
"data-analysis": "session.new.placeholder.analysis",
"writing-assistant": "session.new.placeholder.writing",
}

export function promptPlaceholder(input: PromptPlaceholderInput) {
if (input.mode === "shell") return input.t("prompt.placeholder.shell")
if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments")
if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment")
if (input.selectedSkill) return input.t(SKILL_PLACEHOLDER_KEY[input.selectedSkill])
if (!input.suggest) return input.t("prompt.placeholder.simple")
return input.t("prompt.placeholder.normal", { example: input.example })
}
2 changes: 0 additions & 2 deletions packages/app/src/components/prompt-input/readiness.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ describe("promptSendDisabled", () => {
actionReady: false,
abortReady: true,
blank: true,
selectedSkill: false,
}),
).toBe(false)
})
Expand All @@ -69,7 +68,6 @@ describe("promptSendDisabled", () => {
actionReady: false,
abortReady: true,
blank: false,
selectedSkill: false,
}),
).toBe(true)
})
Expand Down
3 changes: 1 addition & 2 deletions packages/app/src/components/prompt-input/readiness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ export function promptSendDisabled(input: {
actionReady: boolean
abortReady: boolean
blank: boolean
selectedSkill: boolean
}) {
if (input.stopping) return !input.abortReady
return !input.actionReady || (input.blank && !input.selectedSkill)
return !input.actionReady || input.blank
}
Loading
Loading