Skip to content

Commit 2460853

Browse files
committed
Implement tab reordering functionality in the prompt field
- tabs: allow reordering through react-sortable - tabs: dont show a dot when there is just a single tab
1 parent 078c8cc commit 2460853

7 files changed

Lines changed: 141 additions & 2 deletions

File tree

‎apps/editor/src/views/panel/frontend/Main/Main.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ type Props = {
8080
on_tab_change: (index: number) => void
8181
on_new_tab: () => void
8282
on_tab_delete: (index: number) => void
83+
on_tabs_reorder: (new_order: number[]) => void
8384
missing_web_configuration?: boolean
8485
voice_input_push_to_talk: boolean
8586
token_count: number
@@ -730,6 +731,7 @@ export const Main: React.FC<Props> = (props) => {
730731
on_tab_change={props.on_tab_change}
731732
on_new_tab={props.on_new_tab}
732733
on_tab_delete={props.on_tab_delete}
734+
on_tabs_reorder={props.on_tabs_reorder}
733735
voice_input_push_to_talk={props.voice_input_push_to_talk}
734736
/>
735737
)

‎apps/editor/src/views/panel/frontend/Main/MainView/MainView.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ type Props = {
123123
on_tab_change: (index: number) => void
124124
on_new_tab: () => void
125125
on_tab_delete: (index: number) => void
126+
on_tabs_reorder: (new_order: number[]) => void
126127
voice_input_push_to_talk: boolean
127128
}
128129

@@ -425,6 +426,7 @@ export const MainView: React.FC<Props> = (props) => {
425426
on_tab_change={props.on_tab_change}
426427
on_new_tab={props.on_new_tab}
427428
on_tab_delete={props.on_tab_delete}
429+
on_tabs_reorder={props.on_tabs_reorder}
428430
warning={warning}
429431
voice_input_push_to_talk={props.voice_input_push_to_talk}
430432
translations={{

‎apps/editor/src/views/panel/frontend/Panel.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export const Panel = () => {
7979
handle_tab_change,
8080
handle_new_tab,
8181
handle_tab_delete,
82+
handle_tabs_reorder,
8283
voice_input_push_to_talk,
8384
token_count
8485
} = use_panel(vscode)
@@ -416,6 +417,7 @@ export const Panel = () => {
416417
on_tab_change={handle_tab_change}
417418
on_new_tab={handle_new_tab}
418419
on_tab_delete={handle_tab_delete}
420+
on_tabs_reorder={handle_tabs_reorder}
419421
voice_input_push_to_talk={voice_input_push_to_talk}
420422
token_count={token_count}
421423
/>

‎apps/editor/src/views/panel/frontend/hooks/panel/use-instructions.ts‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,49 @@ export const use_instructions = (
196196
}
197197
}
198198

199+
const handle_tabs_reorder = (new_order: number[]) => {
200+
const prompt_type = (
201+
mode == MODE.WEB ? web_prompt_type : api_prompt_type
202+
) as any
203+
const update = (
204+
state: InstructionsState | undefined,
205+
setter: React.Dispatch<
206+
React.SetStateAction<InstructionsState | undefined>
207+
>
208+
) => {
209+
if (!state) return
210+
211+
const new_instructions = new_order.map((i) => state.instructions[i])
212+
const new_active_index = new_order.indexOf(state.active_index)
213+
214+
const new_state = {
215+
instructions: new_instructions,
216+
active_index: new_active_index >= 0 ? new_active_index : 0
217+
}
218+
setter(new_state)
219+
post_message(vscode, {
220+
command: 'SAVE_INSTRUCTIONS',
221+
instruction: new_state,
222+
prompt_type
223+
})
224+
}
225+
226+
if (prompt_type == 'ask-about-files') {
227+
update(ask_about_context_instructions, set_ask_about_context_instructions)
228+
} else if (prompt_type == 'edit-files') {
229+
update(edit_files_instructions, set_edit_files_instructions)
230+
} else if (prompt_type == 'without-files') {
231+
update(no_context_instructions, set_no_context_instructions)
232+
} else if (prompt_type == 'code-at-cursor') {
233+
update(code_at_cursor_instructions, set_code_at_cursor_instructions)
234+
} else if (prompt_type == 'find-relevant-files') {
235+
update(
236+
find_relevant_files_instructions,
237+
set_find_relevant_files_instructions
238+
)
239+
}
240+
}
241+
199242
useEffect(() => {
200243
const handle_message = (event: MessageEvent<BackendMessage>) => {
201244
const message = event.data
@@ -222,6 +265,7 @@ export const use_instructions = (
222265
handle_instructions_change,
223266
handle_tab_change,
224267
handle_new_tab,
225-
handle_tab_delete
268+
handle_tab_delete,
269+
handle_tabs_reorder
226270
}
227271
}

‎apps/editor/src/views/panel/frontend/hooks/panel/use-panel.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export const use_panel = (vscode: any) => {
3636
handle_instructions_change,
3737
handle_tab_change,
3838
handle_new_tab,
39-
handle_tab_delete
39+
handle_tab_delete,
40+
handle_tabs_reorder
4041
} = use_instructions(vscode, mode, web_prompt_type, api_prompt_type)
4142

4243
const [context_size_warning_threshold, set_context_size_warning_threshold] =
@@ -301,6 +302,7 @@ export const use_panel = (vscode: any) => {
301302
handle_tab_change,
302303
handle_new_tab,
303304
handle_tab_delete,
305+
handle_tabs_reorder,
304306
voice_input_push_to_talk,
305307
token_count
306308
}

‎packages/ui/src/components/editor/panel/prompts/PromptField/PromptField.module.scss‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
letter-spacing: normal;
109109
min-height: 48px;
110110

111+
.tabs {
112+
display: none;
113+
}
114+
111115
&:focus {
112116
outline: none;
113117
}

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

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useRef, useEffect, useMemo, useState, useCallback } from 'react'
2+
import { ReactSortable } from 'react-sortablejs'
23
import styles from './PromptField.module.scss'
34
import cn from 'classnames'
45
import { Icon } from '../../../common/Icon'
@@ -76,6 +77,7 @@ export type PromptFieldProps = {
7677
on_tab_change: (index: number) => void
7778
on_new_tab: () => void
7879
on_tab_delete: (index: number) => void
80+
on_tabs_reorder?: (new_order: number[]) => void
7981
warning?: string
8082
voice_input_push_to_talk?: boolean
8183
translations: {
@@ -107,6 +109,23 @@ export const PromptField: React.FC<PromptFieldProps> = (props) => {
107109
'at' | 'hash' | 'curly' | null
108110
>(null)
109111

112+
const [tab_items, set_tab_items] = useState<{ id: string }[]>([])
113+
114+
useEffect(() => {
115+
set_tab_items((prev) => {
116+
if (prev.length === props.tabs_count) return prev
117+
if (prev.length < props.tabs_count) {
118+
return [
119+
...prev,
120+
...Array.from({ length: props.tabs_count - prev.length }).map(() => ({
121+
id: Math.random().toString(36).substring(7)
122+
}))
123+
]
124+
}
125+
return prev.slice(0, props.tabs_count)
126+
})
127+
}, [props.tabs_count])
128+
110129
const invocation_button_ref = useRef<HTMLButtonElement>(null)
111130
const chevron_button_ref = useRef<HTMLButtonElement>(null)
112131
const disconnected_chevron_button_ref = useRef<HTMLButtonElement>(null)
@@ -359,6 +378,70 @@ export const PromptField: React.FC<PromptFieldProps> = (props) => {
359378
onClick={() => input_ref.current?.focus()}
360379
>
361380
<div className={styles['input-wrapper']}>
381+
{props.tabs_count > 1 ? (
382+
<ReactSortable
383+
list={tab_items}
384+
setList={(new_list) => {
385+
const has_changed = new_list.some(
386+
(item, i) => item.id !== tab_items[i]?.id
387+
)
388+
if (has_changed) {
389+
const new_order = new_list.map((item) =>
390+
tab_items.findIndex((t) => t.id === item.id)
391+
)
392+
set_tab_items(new_list)
393+
props.on_tabs_reorder?.(new_order)
394+
}
395+
}}
396+
className={styles.tabs}
397+
style={{
398+
position: 'relative',
399+
zIndex: 2,
400+
margin: '7px 1px 0 5px'
401+
}}
402+
animation={150}
403+
filter={`.${styles['tabs__tab--new']}`}
404+
>
405+
{tab_items.map((item, i) => (
406+
<div
407+
key={item.id}
408+
className={cn(styles.tabs__tab, {
409+
[styles['tabs__tab--active']]: i === props.active_tab_index
410+
})}
411+
onClick={(e) => {
412+
e.stopPropagation()
413+
props.on_tab_change(i)
414+
}}
415+
>
416+
<div className={styles['tabs__tab-icon']} />
417+
</div>
418+
))}
419+
<div
420+
className={cn(styles.tabs__tab, styles['tabs__tab--new'])}
421+
onClick={(e) => {
422+
e.stopPropagation()
423+
props.on_new_tab()
424+
}}
425+
/>
426+
</ReactSortable>
427+
) : props.tabs_count === 1 ? (
428+
<div
429+
className={styles.tabs}
430+
style={{
431+
position: 'relative',
432+
zIndex: 2,
433+
margin: '7px 1px 0 5px'
434+
}}
435+
>
436+
<div
437+
className={cn(styles.tabs__tab, styles['tabs__tab--new'])}
438+
onClick={(e) => {
439+
e.stopPropagation()
440+
props.on_new_tab()
441+
}}
442+
/>
443+
</div>
444+
) : null}
362445
<div
363446
ref={input_ref}
364447
contentEditable={true}

0 commit comments

Comments
 (0)