authorgravatar for 73941998+UtkarshUsername@users.noreply.github.comUtkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com> 2026-08-29 04:47:04+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-29 04:47:04+05:30
logac3b2adf96e755580fc09e149b7c7eb057d0b282
treed6b637426f4e34fa8cd170c89bae190739387e40
parente898008959247befda6b5e6c40079cd6dee33cad
signature Signed by PGP key B5690EEEBB952194

fix(web): show the configured stash shortcut (#8437)

Co-authored-by: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com>

3 files changed, 32 insertions(+), 3 deletions(-)

apps/web/src/components/chat/ChatComposer.tsx+8-1
...@@ -90,7 +90,7 @@ import {...@@ -90,7 +90,7 @@ import {
90} from "../../lib/attachmentUploadState";90} from "../../lib/attachmentUploadState";
91import { isCommandPaletteOpen } from "../../commandPaletteBus";91import { isCommandPaletteOpen } from "../../commandPaletteBus";
92import { getTerminalFocusOwner } from "../../lib/terminalFocus";92import { getTerminalFocusOwner } from "../../lib/terminalFocus";
93import { resolveShortcutCommand } from "../../keybindings";93import { resolveShortcutCommand, shortcutLabelForCommand } from "../../keybindings";
94import {94import {
95 type TerminalContextDraft,95 type TerminalContextDraft,
96 type TerminalContextSelection,96 type TerminalContextSelection,
...@@ -3201,6 +3201,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -3201,6 +3201,13 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
3201 <ComposerCommandMenuLayer anchor={composerMenuAnchor}>3201 <ComposerCommandMenuLayer anchor={composerMenuAnchor}>
3202 <ComposerStashMenu3202 <ComposerStashMenu
3203 entries={stashQueue}3203 entries={stashQueue}
3204 stashShortcutLabel={shortcutLabelForCommand(keybindings, "composer.stash", {
3205 context: {
3206 terminalFocus: false,
3207 terminalOpen,
3208 modelPickerOpen: false,
3209 },
3210 })}
3204 onRestore={restoreStashEntry}3211 onRestore={restoreStashEntry}
3205 onDelete={deleteStashEntry}3212 onDelete={deleteStashEntry}
3206 onClose={() => setIsStashMenuOpen(false)}3213 onClose={() => setIsStashMenuOpen(false)}
apps/web/src/components/chat/ComposerStashMenu.test.tsx+18
...@@ -8,6 +8,7 @@ describe("ComposerStashMenu", () => {...@@ -8,6 +8,7 @@ describe("ComposerStashMenu", () => {
8 const markup = renderToStaticMarkup(8 const markup = renderToStaticMarkup(
9 <ComposerStashMenu9 <ComposerStashMenu
10 entries={[]}10 entries={[]}
11 stashShortcutLabel="Ctrl+S"
11 onRestore={() => {}}12 onRestore={() => {}}
12 onDelete={() => {}}13 onDelete={() => {}}
13 onClose={() => {}}14 onClose={() => {}}
...@@ -20,6 +21,22 @@ describe("ComposerStashMenu", () => {...@@ -20,6 +21,22 @@ describe("ComposerStashMenu", () => {
20 expect(markup).toContain('aria-label="Close stash"');21 expect(markup).toContain('aria-label="Close stash"');
21 expect(markup).not.toContain("dropdown-glass");22 expect(markup).not.toContain("dropdown-glass");
22 expect(markup).not.toContain("Stashed prompts");23 expect(markup).not.toContain("Stashed prompts");
24 expect(markup).toContain("Press Ctrl+S with a prompt in the composer to stash it.");
25 });
26
27 it("does not advertise a shortcut when stash is unbound", () => {
28 const markup = renderToStaticMarkup(
29 <ComposerStashMenu
30 entries={[]}
31 stashShortcutLabel={null}
32 onRestore={() => {}}
33 onDelete={() => {}}
34 onClose={() => {}}
35 />,
36 );
37
38 expect(markup).toContain("Nothing stashed yet.");
39 expect(markup).not.toContain("Press");
23 });40 });
2441
25 it("shows saved image thumbnails and incomplete image states", () => {42 it("shows saved image thumbnails and incomplete image states", () => {
...@@ -53,6 +70,7 @@ describe("ComposerStashMenu", () => {...@@ -53,6 +70,7 @@ describe("ComposerStashMenu", () => {
53 pendingImageCount: 1,70 pendingImageCount: 1,
54 },71 },
55 ]}72 ]}
73 stashShortcutLabel="Ctrl+S"
56 onRestore={() => {}}74 onRestore={() => {}}
57 onDelete={() => {}}75 onDelete={() => {}}
58 onClose={() => {}}76 onClose={() => {}}
apps/web/src/components/chat/ComposerStashMenu.tsx+6-2
...@@ -31,11 +31,12 @@ function stashEntrySnippet(entry: PromptStashEntry): string {...@@ -31,11 +31,12 @@ function stashEntrySnippet(entry: PromptStashEntry): string {
31 */31 */
32export const ComposerStashMenu = memo(function ComposerStashMenu(props: {32export const ComposerStashMenu = memo(function ComposerStashMenu(props: {
33 entries: ReadonlyArray<PromptStashEntry>;33 entries: ReadonlyArray<PromptStashEntry>;
34 stashShortcutLabel: string | null;
34 onRestore: (entry: PromptStashEntry) => void;35 onRestore: (entry: PromptStashEntry) => void;
35 onDelete: (entry: PromptStashEntry) => void;36 onDelete: (entry: PromptStashEntry) => void;
36 onClose: () => void;37 onClose: () => void;
37}) {38}) {
38 const { entries, onRestore, onDelete, onClose } = props;39 const { entries, stashShortcutLabel, onRestore, onDelete, onClose } = props;
39 const drawerRef = useRef<HTMLDivElement>(null);40 const drawerRef = useRef<HTMLDivElement>(null);
40 const [highlightedId, setHighlightedId] = useState<string | null>(entries[0]?.id ?? null);41 const [highlightedId, setHighlightedId] = useState<string | null>(entries[0]?.id ?? null);
4142
...@@ -128,7 +129,10 @@ export const ComposerStashMenu = memo(function ComposerStashMenu(props: {...@@ -128,7 +129,10 @@ export const ComposerStashMenu = memo(function ComposerStashMenu(props: {
128 <CommandGroup>129 <CommandGroup>
129 {entries.length === 0 ? (130 {entries.length === 0 ? (
130 <p className="px-3 py-1.5 text-secondary-label text-xs">131 <p className="px-3 py-1.5 text-secondary-label text-xs">
131 Nothing stashed yet. Press ⌘S with a prompt in the composer to stash it.132 Nothing stashed yet.
133 {stashShortcutLabel
134 ? ` Press ${stashShortcutLabel} with a prompt in the composer to stash it.`
135 : null}
132 </p>136 </p>
133 ) : (137 ) : (
134 entries.map((entry) => (138 entries.map((entry) => (