| author | |
| committer | |
| log | ac3b2adf96e755580fc09e149b7c7eb057d0b282 |
| tree | d6b637426f4e34fa8cd170c89bae190739387e40 |
| parent | e898008959247befda6b5e6c40079cd6dee33cad |
| signature | Signed by PGP key B5690EEEBB952194 |
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"; |
| 91 | import { isCommandPaletteOpen } from "../../commandPaletteBus"; | 91 | import { isCommandPaletteOpen } from "../../commandPaletteBus"; |
| 92 | import { getTerminalFocusOwner } from "../../lib/terminalFocus"; | 92 | import { getTerminalFocusOwner } from "../../lib/terminalFocus"; |
| 93 | import { resolveShortcutCommand } from "../../keybindings"; | 93 | import { resolveShortcutCommand, shortcutLabelForCommand } from "../../keybindings"; |
| 94 | import { | 94 | import { |
| 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 | <ComposerStashMenu | 3202 | <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 | <ComposerStashMenu | 9 | <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 | }); |
| 24 | 41 | ||
| 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 | */ |
| 32 | export const ComposerStashMenu = memo(function ComposerStashMenu(props: { | 32 | export 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); |
| 41 | 42 | ||
| ... | @@ -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) => ( |