diff --git a/.fork/customizations.yaml b/.fork/customizations.yaml index 2bf7078900c..7662f9d5898 100644 --- a/.fork/customizations.yaml +++ b/.fork/customizations.yaml @@ -674,11 +674,13 @@ absolutely positioned overlay outside the editor's scroll box — wraps and paints straight through the 48px box, and stably so (the overlay never changes the observed editor height, so the latch cannot rescue it). A - narrow viewport resolves tall regardless. States the designs do not draw - also resolve tall: an approval request, a pending user-input question, and - the plan follow-up banner all own the box's internals and carry their own - action rows. Attachments deliberately do not force tall — attachment cards - render as a full-width band above the inline row. + narrow viewport resolves tall regardless. On desktop slim the default + empty-state hint is a shorter string that still names @, $, and /; tall + keeps the long form. States the designs do not draw also resolve tall: an + approval request, a pending user-input question, and the plan follow-up + banner all own the box's internals and carry their own action rows. + Attachments deliberately do not force tall — attachment cards render as a + full-width band above the inline row. The control row renders in every density, including collapsed; only its left half is conditional. The run controls are gated on a pending approval — they moved out of the box, which upstream unmounted wholesale during diff --git a/.fork/notes/FORK-CUSTOMIZATION-DECISIONS.md b/.fork/notes/FORK-CUSTOMIZATION-DECISIONS.md index e4f49a8cf39..474c3878027 100644 --- a/.fork/notes/FORK-CUSTOMIZATION-DECISIONS.md +++ b/.fork/notes/FORK-CUSTOMIZATION-DECISIONS.md @@ -103,6 +103,11 @@ Related deep-dives that predate this file and stay where they are: beats them regardless of specificity. - Attachments forcing the tall shell was considered and declined; attachment cards render as a full-width band above the inline row instead. +- Absolutely-positioned content in the prompt column is invisible to the wrap latch by + construction: it never changes the observed editor height. Phone widths already force tall for + that reason; on desktop slim the 64-character default hint wraps under the inline pills. Truncating + it would eat `$use skills` / `/ commands`, which have no other discovery surface — so slim uses a + shorter hint that still names `@`, `$`, and `/`, and tall keeps the long form. - A general weakness worth remembering for future guards: the composer-shell guards assert on the text of the stylesheet rather than on what it matches, so a selector that stops matching (for example, upstream adding a second child inside the frame) keeps every guard green. Selecting diff --git a/apps/web/src/__fork_guards__/forkComposerShell.test.ts b/apps/web/src/__fork_guards__/forkComposerShell.test.ts index 540593c7396..6061771b60e 100644 --- a/apps/web/src/__fork_guards__/forkComposerShell.test.ts +++ b/apps/web/src/__fork_guards__/forkComposerShell.test.ts @@ -303,6 +303,30 @@ describe("fork guard: fork-composer-shell", () => { ).toBe("tall"); }); + it("shortens the empty-state hint in the slim shell instead of truncating it", () => { + // The absolute placeholder is invisible to the wrap latch (empty prompt ⇒ + // latch false; placeholder only exists when empty). Truncating the 64-char + // default hint would eat `$use skills` / `/ commands`, which have no other + // discovery surface — so slim uses a shorter string that still names all + // three affordances, and tall keeps the long one. + expect(chatComposer).toMatch( + /isComposerSlim\s*\?\s*"Ask anything, @tag, \$skills, \/ commands"/u, + ); + expect(chatComposer).toContain( + "Ask anything, @tag files/folders, $use skills, or / for commands", + ); + // Truncation was considered and declined: no ellipsis rule on the + // placeholder sibling, so a sync that reintroduces one is a regression. + const rules = cssRules(theme); + const placeholderTruncate = rules.find( + (rule) => + rule.selector.includes('[data-testid="composer-editor"]') && + rule.selector.includes("~") && + (rule.body.includes("text-overflow") || rule.body.includes("ellipsis")), + ); + expect(placeholderTruncate, "slim placeholder must not truncate").toBeUndefined(); + }); + it("keeps the branch controls reachable while collapsed, and gates modes on approval", () => { // Two upstream behaviours the control row has to preserve. Upstream showed // BranchToolbar while collapsed (gated on showComposerContextStrip alone), diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 1c969213314..14fec2e0b94 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -2769,7 +2769,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) ? "Enable a provider in Settings to send a message" : phase === "disconnected" ? "Ask for follow-up changes or attach images" - : "Ask anything, @tag files/folders, $use skills, or / for commands" + : /* fork:begin fork-composer-shell — see .fork/customizations.yaml#fork-composer-shell */ + // Absolute placeholder is invisible to the wrap latch. Slim shortens + // the hint so @/$/ stay discoverable instead of truncating them away. + isComposerSlim + ? "Ask anything, @tag, $skills, / commands" + : /* fork:end fork-composer-shell */ + "Ask anything, @tag files/folders, $use skills, or / for commands" } disabled={isConnecting || isComposerApprovalState || projectSelectionRequired} />