Update composer placeholder copy

This commit is contained in:
murashit 2026-07-09 09:38:09 +09:00
parent 085af148e6
commit a297859ec1
8 changed files with 28 additions and 32 deletions

View file

@ -41,7 +41,7 @@ interface RuntimeComposerChoicesInput {
}
function composerPlaceholder(threadName: string | null): string {
return threadName ? `Ask Codex to work on “${threadName}”...` : "Ask Codex to work on this task...";
return threadName ? `Ask Codex in “${threadName}”...` : "Ask Codex...";
}
export function ChatPanelComposer({

View file

@ -157,7 +157,7 @@ export function ComposerShell({
<textarea
ref={composerRef}
className="codex-panel-ui__text-input codex-panel__composer-input"
placeholder={sendMode.canInterrupt ? "Add steering message..." : normalPlaceholder}
placeholder={sendMode.canInterrupt ? "Steer the current turn..." : normalPlaceholder}
role="combobox"
aria-autocomplete="list"
aria-expanded={suggestions.length > 0 ? "true" : "false"}

View file

@ -597,17 +597,17 @@ describe("CodexChatView connection lifecycle", () => {
await view.setState({ threadId: "thread-1", threadTitle: "Restored title" }, {} as never);
await view.onOpen();
expect(composerPlaceholder(view)).toBe("Ask Codex to work on this task...");
expect(composerPlaceholder(view)).toBe("Ask Codex...");
host.threadCatalog.apply({ type: "active-list-snapshot-received", threads: [panelThread({ id: "thread-1", name: "Explicit name" })] });
await waitForAsyncWork(() => {
expect(composerPlaceholder(view)).toBe("Ask Codex to work on this task...");
expect(composerPlaceholder(view)).toBe("Ask Codex...");
});
view.surface.applyThreadRenamed("thread-1", "Explicit name");
await waitForAsyncWork(() => {
expect(composerPlaceholder(view)).toBe("Ask Codex to work on this task...");
expect(composerPlaceholder(view)).toBe("Ask Codex...");
});
});
@ -633,7 +633,7 @@ describe("CodexChatView connection lifecycle", () => {
expect(composer.value).toBe("keep this draft");
expect(composer.selectionStart).toBe(5);
expect(composer.selectionEnd).toBe(9);
expect(composer.getAttribute("placeholder")).toBe("Ask Codex to work on “Renamed thread”...");
expect(composer.getAttribute("placeholder")).toBe("Ask Codex in “Renamed thread”...");
});
});

View file

@ -896,7 +896,7 @@ function skill(name: string): SkillMetadata {
function defaultComposerProjection(_model: ChatPanelComposerReadModel) {
return {
placeholder: "Ask Codex to work on this task...",
placeholder: "Ask Codex...",
meta: {
fatal: null,
context: {

View file

@ -443,7 +443,7 @@ function shellParts(
draft: model.draft.value,
busy: false,
canInterrupt: false,
normalPlaceholder: "Ask Codex to work on this task...",
normalPlaceholder: "Ask Codex...",
suggestions: [],
selectedSuggestionIndex: 0,
callbacks: {
@ -519,7 +519,7 @@ function contextProvider(
function composerProjectionFixture(_model: ChatPanelComposerReadModel) {
return {
placeholder: "Ask Codex to work on this task...",
placeholder: "Ask Codex...",
meta: {
fatal: null,
context: {

View file

@ -385,7 +385,7 @@ describe("chat panel surface projections", () => {
const projection = composerProjectionFromState(composerProjectionActionsFixture(), state);
expect(projection).toMatchObject({
placeholder: "Ask Codex to work on this task...",
placeholder: "Ask Codex...",
meta: {
model: "gpt-5.5",
effort: "high",
@ -400,12 +400,8 @@ describe("chat panel surface projections", () => {
activeState = chatStateWith(activeState, { activeThread: { id: "thread-1" } });
activeState = chatStateWith(activeState, { threadList: { listedThreads: [threadFixture("thread-1", "Active")] } });
expect(composerProjectionFromState(composerProjectionActionsFixture(), activeState).placeholder).toBe(
"Ask Codex to work on “Active”...",
);
expect(composerProjectionFromState(composerProjectionActionsFixture(), chatStateFixture()).placeholder).toBe(
"Ask Codex to work on this task...",
);
expect(composerProjectionFromState(composerProjectionActionsFixture(), activeState).placeholder).toBe("Ask Codex in “Active”...");
expect(composerProjectionFromState(composerProjectionActionsFixture(), chatStateFixture()).placeholder).toBe("Ask Codex...");
});
it("projects goal editor and disclosure state before action wiring", () => {

View file

@ -123,7 +123,7 @@ function shellParts(store: ReturnType<typeof createChatStateStore>, toolbarPanel
draft: "",
busy: false,
canInterrupt: false,
normalPlaceholder: "Ask Codex to work on this task...",
normalPlaceholder: "Ask Codex...",
suggestions: [],
selectedSuggestionIndex: 0,
callbacks: {

View file

@ -85,7 +85,7 @@ describe("ComposerShell decisions", () => {
it("renders composer meta as interactive context and runtime text without changing normal text", () => {
const parent = document.createElement("div");
mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, composerCallbacks(), {
mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, composerCallbacks(), {
fatal: null,
context: {
cells: [
@ -150,7 +150,7 @@ describe("ComposerShell decisions", () => {
const selectModel = vi.fn();
const selectEffort = vi.fn();
mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, callbacks, {
mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, callbacks, {
fatal: null,
context: {
cells: [
@ -234,7 +234,7 @@ describe("ComposerShell decisions", () => {
it("hides composer meta fields only after measured overflow", async () => {
const parent = document.createElement("div");
mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, composerCallbacks(), {
mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, composerCallbacks(), {
fatal: null,
context: {
cells: [
@ -277,7 +277,7 @@ describe("ComposerShell decisions", () => {
it("replaces composer meta with fatal status text", () => {
const parent = document.createElement("div");
mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, composerCallbacks(), {
mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, composerCallbacks(), {
fatal: "Codex app-server disconnected",
context: {
cells: [
@ -310,7 +310,7 @@ describe("ComposerShell decisions", () => {
"",
false,
false,
"Ask Codex to work on this task...",
"Ask Codex...",
[{ display: "/help", detail: "Show help", replacement: "/help", start: 0 }],
0,
{
@ -344,13 +344,13 @@ describe("ComposerShell decisions", () => {
"",
false,
false,
"Ask Codex to work on this task...",
"Ask Codex...",
[{ display: "/help", detail: "Show help", replacement: "/help", start: 0 }],
0,
callbacks,
);
mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, callbacks);
mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, callbacks);
const suggestions = parent.querySelector<HTMLElement>(".codex-panel__composer-suggestions");
expect(composer.getAttribute("aria-expanded")).toBe("false");
@ -361,7 +361,7 @@ describe("ComposerShell decisions", () => {
it("reports composer draft changes from the controlled input", () => {
const parent = document.createElement("div");
const callbacks = composerCallbacks();
const { composer } = mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, callbacks);
const { composer } = mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, callbacks);
changeInputValue(composer, "Draft text");
@ -378,7 +378,7 @@ describe("ComposerShell decisions", () => {
configurable: true,
});
try {
const { composer } = mountComposerShell(parent, "view", "", false, false, "Ask Codex to work on this task...", [], 0, callbacks);
const { composer } = mountComposerShell(parent, "view", "", false, false, "Ask Codex...", [], 0, callbacks);
scrollHeight = 120;
changeInputValue(composer, "line one\nline two");
@ -413,7 +413,7 @@ describe("ComposerShell decisions", () => {
draft: "line one\nline two",
busy: false,
canInterrupt: false,
normalPlaceholder: "Ask Codex to work on this task...",
normalPlaceholder: "Ask Codex...",
suggestions: [],
selectedSuggestionIndex: 0,
callbacks,
@ -450,7 +450,7 @@ describe("ComposerShell decisions", () => {
draft: "",
busy: false,
canInterrupt: false,
normalPlaceholder: "Ask Codex to work on this task...",
normalPlaceholder: "Ask Codex...",
suggestions: [],
selectedSuggestionIndex: 0,
callbacks,
@ -530,20 +530,20 @@ describe("ComposerShell decisions", () => {
it("uses the composer action for interrupt only when a running turn has no steering text", () => {
const parent = document.createElement("div");
const callbacks = composerCallbacks();
const { composer } = mountComposerShell(parent, "view", "", true, true, "Ask Codex to work on this task...", [], 0, callbacks);
const { composer } = mountComposerShell(parent, "view", "", true, true, "Ask Codex...", [], 0, callbacks);
let sendButton = parent.querySelector<HTMLButtonElement>(".codex-panel__send");
expect(sendButton?.getAttribute("aria-label")).toBe("Interrupt");
expect(composer.getAttribute("placeholder")).toBe("Add steering message...");
expect(composer.getAttribute("placeholder")).toBe("Steer the current turn...");
expect(composer.getAttribute("aria-label")).toBeNull();
expect(sendButton?.classList.contains("is-interrupt")).toBe(true);
expect(sendButton?.classList.contains("is-steer")).toBe(false);
expect(sendButton?.dataset["icon"]).toBe("square");
mountComposerShell(parent, "view", "adjust course", true, true, "Ask Codex to work on this task...", [], 0, callbacks);
mountComposerShell(parent, "view", "adjust course", true, true, "Ask Codex...", [], 0, callbacks);
sendButton = parent.querySelector<HTMLButtonElement>(".codex-panel__send");
expect(sendButton?.getAttribute("aria-label")).toBe("Steer");
expect(composer.getAttribute("placeholder")).toBe("Add steering message...");
expect(composer.getAttribute("placeholder")).toBe("Steer the current turn...");
expect(sendButton?.classList.contains("is-interrupt")).toBe(false);
expect(sendButton?.classList.contains("is-steer")).toBe(true);
expect(sendButton?.dataset["icon"]).toBe("corner-down-right");