Skip to content

Commit 00cc8de

Browse files
committed
Implement Alt+X and numeric keyboard shortcuts for invocation count selection and refine DropdownMenu layout
1 parent c5d5a1e commit 00cc8de

4 files changed

Lines changed: 122 additions & 34 deletions

File tree

‎packages/ui/src/components/editor/common/DropdownMenu/DropdownMenu.module.scss‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
z-index: 2;
77
width: max-content;
88
text-align: left;
9-
min-width: max(100%, 80px);
9+
min-width: max(100%, 110px);
1010

1111
&__inner {
1212
background-color: var(--vscode-quickInput-background);
@@ -29,6 +29,18 @@
2929
white-space: nowrap;
3030
border-radius: 4px;
3131

32+
&__left {
33+
display: flex;
34+
align-items: center;
35+
gap: 6px;
36+
overflow: hidden;
37+
flex: 1;
38+
min-width: 0;
39+
> span {
40+
font-size: 14px !important;
41+
}
42+
}
43+
3244
&__label {
3345
overflow: hidden;
3446
text-overflow: ellipsis;

‎packages/ui/src/components/editor/common/DropdownMenu/DropdownMenu.tsx‎

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const DropdownMenu: React.FC<DropdownMenu.Props> = (props) => {
2121
const [is_preselection_respected, set_is_preselection_respected] =
2222
useState<boolean>(true)
2323

24+
const has_any_checked = props.items.some((item) => item.checked)
25+
2426
return (
2527
<div className={styles.menu} style={{ maxWidth: props.max_width }}>
2628
<div className={styles.menu__inner}>
@@ -39,24 +41,29 @@ export const DropdownMenu: React.FC<DropdownMenu.Props> = (props) => {
3941
set_is_preselection_respected(false)
4042
}}
4143
>
42-
<span className={styles.item__label}>
43-
{should_underline ? (
44-
<>
45-
<span className={styles.underlined}>
46-
{item.label.substring(0, 1)}
47-
</span>
48-
{item.label.substring(1)}
49-
</>
50-
) : (
51-
item.label
44+
<div className={styles.item__left}>
45+
{has_any_checked && (
46+
<span
47+
className="codicon codicon-check"
48+
style={{ visibility: item.checked ? 'visible' : 'hidden' }}
49+
/>
5250
)}
53-
</span>
54-
{(item.shortcut || item.checked) && (
55-
<div className={styles.item__right}>
56-
{item.checked && <span className="codicon codicon-check" />}
57-
{item.shortcut && (
58-
<span className={styles.shortcut}>{item.shortcut}</span>
51+
<span className={styles.item__label}>
52+
{should_underline ? (
53+
<>
54+
<span className={styles.underlined}>
55+
{item.label.substring(0, 1)}
56+
</span>
57+
{item.label.substring(1)}
58+
</>
59+
) : (
60+
item.label
5961
)}
62+
</span>
63+
</div>
64+
{item.shortcut && (
65+
<div className={styles.item__right}>
66+
<span className={styles.shortcut}>{item.shortcut}</span>
6067
</div>
6168
)}
6269
</div>

‎packages/ui/src/components/editor/panel/PromptField/PromptField.tsx‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,19 @@ export const PromptField: React.FC<PromptFieldProps> = (props) => {
6767
useState(false)
6868
const [hovered_edit_format, set_hovered_edit_format] =
6969
useState<EditFormat | null>(null)
70+
71+
const toggle_invocation_dropdown = useCallback(() => {
72+
set_is_invocation_dropdown_open((prev) => !prev)
73+
}, [])
74+
7075
const { is_alt_pressed, handle_container_key_down } = use_keyboard_shortcuts({
7176
show_edit_format_selector: props.show_edit_format_selector,
7277
on_edit_format_change: props.on_edit_format_change,
7378
on_search_click: props.on_search_click,
74-
on_copy: props.on_copy
79+
on_copy: props.on_copy,
80+
on_invocation_count_change: props.on_invocation_count_change,
81+
is_invocation_dropdown_open,
82+
on_toggle_invocation_dropdown: toggle_invocation_dropdown
7583
})
7684
const {
7785
is_dropdown_open,
@@ -546,7 +554,7 @@ export const PromptField: React.FC<PromptFieldProps> = (props) => {
546554
set_is_invocation_dropdown_open((prev) => !prev)
547555
close_dropdown()
548556
}}
549-
title="Invocation count"
557+
title={`Invocation count ${is_mac ? '(⌥X 1-5)' : '(Alt+X 1-5)'}`}
550558
>
551559
{props.invocation_count}×
552560
</button>
@@ -555,6 +563,7 @@ export const PromptField: React.FC<PromptFieldProps> = (props) => {
555563
items={[1, 2, 3, 4, 5].map((count) => ({
556564
label: `${count}×`,
557565
checked: count == props.invocation_count,
566+
shortcut: is_mac ? `⌥X ${count}` : `Alt+X ${count}`,
558567
on_click: () => {
559568
props.on_invocation_count_change(count)
560569
set_is_invocation_dropdown_open(false)

‎packages/ui/src/components/editor/panel/PromptField/hooks/use-keyboard-shortcuts.ts‎

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,95 @@ type UseKeyboardShortcutsParams = Pick<
77
| 'on_edit_format_change'
88
| 'on_search_click'
99
| 'on_copy'
10-
>
10+
| 'on_invocation_count_change'
11+
> & {
12+
on_toggle_invocation_dropdown?: () => void
13+
is_invocation_dropdown_open?: boolean
14+
}
1115

1216
export const use_keyboard_shortcuts = (params: UseKeyboardShortcutsParams) => {
1317
const [is_alt_pressed, set_is_alt_pressed] = useState(false)
1418

19+
const {
20+
on_toggle_invocation_dropdown,
21+
is_invocation_dropdown_open,
22+
on_invocation_count_change
23+
} = params
24+
1525
useEffect(() => {
1626
const handle_key_down = (e: KeyboardEvent) => {
1727
if (e.key == 'Alt') set_is_alt_pressed(true)
1828

1929
if (
30+
on_toggle_invocation_dropdown &&
31+
e.code == 'KeyX' &&
2032
e.altKey &&
2133
!e.shiftKey &&
2234
!e.ctrlKey &&
35+
!e.metaKey
36+
) {
37+
e.preventDefault()
38+
on_toggle_invocation_dropdown()
39+
return
40+
}
41+
42+
if (
43+
is_invocation_dropdown_open &&
44+
on_invocation_count_change &&
45+
!e.altKey &&
46+
!e.ctrlKey &&
2347
!e.metaKey &&
24-
params.show_edit_format_selector &&
25-
params.on_edit_format_change
48+
!e.shiftKey
2649
) {
27-
let format: EditFormat | undefined
50+
let count = 0
2851
switch (e.code) {
29-
case 'KeyW':
30-
format = 'whole'
52+
case 'Digit1':
53+
count = 1
54+
break
55+
case 'Digit2':
56+
count = 2
3157
break
32-
case 'KeyT':
33-
format = 'truncated'
58+
case 'Digit3':
59+
count = 3
3460
break
35-
case 'KeyB':
36-
format = 'before-after'
61+
case 'Digit4':
62+
count = 4
3763
break
38-
case 'KeyD':
39-
format = 'diff'
64+
case 'Digit5':
65+
count = 5
4066
break
4167
}
42-
if (format) {
68+
69+
if (count > 0) {
4370
e.preventDefault()
44-
params.on_edit_format_change(format)
71+
on_invocation_count_change(count)
72+
on_toggle_invocation_dropdown?.()
73+
return
74+
}
75+
}
76+
77+
if (e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey) {
78+
if (params.show_edit_format_selector && params.on_edit_format_change) {
79+
let format: EditFormat | undefined
80+
switch (e.code) {
81+
case 'KeyW':
82+
format = 'whole'
83+
break
84+
case 'KeyT':
85+
format = 'truncated'
86+
break
87+
case 'KeyB':
88+
format = 'before-after'
89+
break
90+
case 'KeyD':
91+
format = 'diff'
92+
break
93+
}
94+
if (format) {
95+
e.preventDefault()
96+
params.on_edit_format_change(format)
97+
return
98+
}
4599
}
46100
}
47101
}
@@ -59,7 +113,13 @@ export const use_keyboard_shortcuts = (params: UseKeyboardShortcutsParams) => {
59113
window.removeEventListener('keyup', handle_key_up)
60114
window.removeEventListener('blur', handle_blur)
61115
}
62-
}, [params.show_edit_format_selector, params.on_edit_format_change])
116+
}, [
117+
params.show_edit_format_selector,
118+
params.on_edit_format_change,
119+
on_invocation_count_change,
120+
on_toggle_invocation_dropdown,
121+
is_invocation_dropdown_open
122+
])
63123

64124
const handle_container_key_down = (
65125
e: React.KeyboardEvent<HTMLDivElement>

0 commit comments

Comments
 (0)