Skip to content

Commit f3b5c7a

Browse files
committed
Enable expandable text items in the response preview, managing scroll position upon toggle
1 parent f792d19 commit f3b5c7a

5 files changed

Lines changed: 111 additions & 47 deletions

File tree

‎packages/ui/src/components/editor/panel/ResponsePreview/ResponsePreview.module.scss‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.container {
22
display: flex;
33
flex-direction: column;
4-
padding: var(--padding-4px) 0 var(--padding-12px) 0;
4+
padding: var(--padding-4px) 0 var(--padding-8px) 0;
55
gap: var(--padding-8px);
66
}
77

@@ -42,9 +42,7 @@
4242
}
4343

4444
.list {
45-
display: flex;
46-
flex-direction: column;
47-
gap: var(--padding-4px);
45+
display: block;
4846

4947
> div:first-child {
5048
margin-top: 0;
@@ -59,15 +57,10 @@
5957
align-items: center;
6058
justify-content: space-between;
6159
gap: var(--padding-8px);
62-
border: 1px solid var(--cwc-border-color-dimmed);
63-
padding: 0 var(--padding-8px);
64-
margin: 0 var(--padding-12px);
65-
height: 34px;
66-
border-radius: 8px;
67-
60+
padding: 0 var(--padding-12px);
61+
height: var(--list-item-height);
6862
&:hover {
6963
background-color: var(--vscode-list-hoverBackground);
70-
border-color: var(--cwc-border-color);
7164

7265
.list__file__actions {
7366
display: flex;
@@ -76,6 +69,10 @@
7669

7770
&--selected {
7871
background-color: var(--vscode-list-inactiveSelectionBackground);
72+
73+
&:hover {
74+
background-color: var(--vscode-list-inactiveSelectionBackground);
75+
}
7976
}
8077

8178
&__left {

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

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FC, useState } from 'react'
1+
import { FC, useRef, useState } from 'react'
22
import { FileInPreview, ItemInPreview } from '@shared/types/file-in-preview'
33
import cn from 'classnames'
44
import styles from './ResponsePreview.module.scss'
@@ -25,12 +25,50 @@ type Props = {
2525
}
2626

2727
export const ResponsePreview: FC<Props> = (props) => {
28-
const [last_clicked_file_index, set_last_clicked_file_index] = useState(
29-
props.items.findIndex((i) => i.type == 'file')
28+
const [last_clicked_file_index, set_last_clicked_file_index] = useState(0)
29+
const [expanded_text_items, set_expanded_text_items] = useState<Set<number>>(
30+
new Set()
3031
)
32+
const scroll_top_ref = useRef(0)
33+
const scrollable_ref = useRef<any>(null)
34+
35+
const toggle_expanded_text_item = (
36+
index: number,
37+
element: HTMLDivElement
38+
) => {
39+
const scrollable_instance = scrollable_ref.current
40+
if (scrollable_instance) {
41+
const scroll_container = scrollable_instance.getScrollElement()
42+
if (scroll_container) {
43+
const scroll_container_rect = scroll_container.getBoundingClientRect()
44+
const element_rect = element.getBoundingClientRect()
45+
46+
if (element_rect.top < scroll_container_rect.top) {
47+
// Element's top edge is above the scrollable viewport's top edge.
48+
// Restore scroll position to the distance of this top edge to the top.
49+
const element_position_in_scrollable_content =
50+
scroll_container.scrollTop +
51+
element_rect.top -
52+
scroll_container_rect.top -
53+
4
54+
scroll_top_ref.current = element_position_in_scrollable_content
55+
}
56+
}
57+
}
58+
59+
set_expanded_text_items((prev) => {
60+
const new_set = new Set(prev)
61+
if (new_set.has(index)) {
62+
new_set.delete(index)
63+
} else {
64+
new_set.add(index)
65+
}
66+
return new_set
67+
})
68+
}
3169

3270
const files_in_preview = props.items.filter(
33-
(i) => i.type == 'file'
71+
(i) => 'file_path' in i
3472
) as FileInPreview[]
3573
const fallback_count = files_in_preview.filter((f) => f.is_fallback).length
3674

@@ -45,7 +83,14 @@ export const ResponsePreview: FC<Props> = (props) => {
4583
}
4684

4785
return (
48-
<Scrollable>
86+
<Scrollable
87+
ref={scrollable_ref}
88+
initial_scroll_top={scroll_top_ref.current}
89+
scroll_trigger={expanded_text_items.size}
90+
on_scroll={(top) => {
91+
scroll_top_ref.current = top
92+
}}
93+
>
4994
<div className={styles.container}>
5095
{props.raw_instructions && (
5196
<div
@@ -71,7 +116,7 @@ export const ResponsePreview: FC<Props> = (props) => {
71116
)}
72117
<div className={styles.list}>
73118
{props.items.map((item, index) => {
74-
if (item.type == 'file') {
119+
if ('file_path' in item) {
75120
const file = item
76121
const last_slash_index = file.file_path.lastIndexOf('/')
77122
const file_name = file.file_path.substring(last_slash_index + 1)
@@ -138,7 +183,7 @@ export const ResponsePreview: FC<Props> = (props) => {
138183
{(file.is_fallback || file.is_replaced) && (
139184
<IconButton
140185
codicon_icon="sparkle"
141-
title={`Fix the file with Intelligent Update API tool${
186+
title={`Call Intelligent Update API tool${
142187
file.diff_fallback_method == 'recount'
143188
? ' (fallback used: git apply with --recount flag)'
144189
: file.diff_fallback_method ==
@@ -156,6 +201,18 @@ export const ResponsePreview: FC<Props> = (props) => {
156201
}}
157202
/>
158203
)}
204+
<IconButton
205+
codicon_icon="diff-single"
206+
title="Open Changes"
207+
on_click={(e) => {
208+
e.stopPropagation()
209+
set_last_clicked_file_index(index)
210+
props.on_focus_file({
211+
file_path: file.file_path,
212+
workspace_name: file.workspace_name
213+
})
214+
}}
215+
/>
159216
<IconButton
160217
codicon_icon="go-to-file"
161218
title="Go To File"
@@ -188,7 +245,16 @@ export const ResponsePreview: FC<Props> = (props) => {
188245
</div>
189246
)
190247
} else {
191-
return <TextItem key={index} content={item.content} />
248+
return (
249+
<TextItem
250+
key={index}
251+
content={item.content}
252+
is_expanded={expanded_text_items.has(index)}
253+
on_toggle={(element) =>
254+
toggle_expanded_text_item(index, element)
255+
}
256+
/>
257+
)
192258
}
193259
})}
194260
</div>

‎packages/ui/src/components/editor/panel/ResponsePreview/components/TextItem.module.scss‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
}
1818

1919
code {
20-
font-family: monospace;
2120
font-size: var(--font-size-11px);
2221
background-color: var(--vscode-textCodeBlock-background);
2322
padding: 2px 4px;
Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,38 @@
1-
import { FC, useState } from 'react'
1+
import { FC, useRef } from 'react'
22
import ReactMarkdown from 'react-markdown'
33
import styles from './TextItem.module.scss'
4-
import { IconButton } from '../../IconButton/IconButton'
4+
import { IconButton } from '../../IconButton'
55

66
type Props = {
77
content: string
8+
is_expanded: boolean
9+
on_toggle: (element: HTMLDivElement) => void
810
}
911

10-
export const TextItem: FC<Props> = ({ content }) => {
11-
const [is_hidden, set_is_hidden] = useState(true)
12-
13-
if (is_hidden) {
14-
return (
15-
<div className={styles.hidden}>
16-
<div className={styles.line} />
17-
<IconButton
18-
codicon_icon="add"
19-
on_click={() => set_is_hidden(false)}
20-
title="Show text"
21-
/>
22-
<div className={styles.line} />
23-
</div>
24-
)
25-
}
12+
export const TextItem: FC<Props> = ({ content, is_expanded, on_toggle }) => {
13+
const ref = useRef<HTMLDivElement>(null)
2614

2715
return (
28-
<div
29-
className={styles.text}
30-
onClick={() => {
31-
set_is_hidden(true)
32-
}}
33-
>
34-
<ReactMarkdown disallowedElements={['hr']}>{content}</ReactMarkdown>
35-
</div>
16+
<>
17+
{is_expanded ? (
18+
<div
19+
ref={ref}
20+
className={styles.text}
21+
onClick={() => ref.current && on_toggle(ref.current)}
22+
>
23+
<ReactMarkdown disallowedElements={['hr']}>{content}</ReactMarkdown>
24+
</div>
25+
) : (
26+
<div ref={ref} className={styles.hidden}>
27+
<div className={styles.line} />
28+
<IconButton
29+
codicon_icon="add"
30+
on_click={() => ref.current && on_toggle(ref.current)}
31+
title="Show text"
32+
/>
33+
<div className={styles.line} />
34+
</div>
35+
)}
36+
</>
3637
)
3738
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Props = {
1616
max_height?: string
1717
initial_scroll_top?: number
1818
on_scroll?: (top: number) => void
19+
scroll_trigger?: number
1920
}
2021

2122
export const Scrollable = forwardRef<any, Props>((props, ref) => {
@@ -77,7 +78,7 @@ export const Scrollable = forwardRef<any, Props>((props, ref) => {
7778
scroll_element.scrollTop = props.initial_scroll_top
7879
})
7980
}
80-
}, [props.initial_scroll_top])
81+
}, [props.initial_scroll_top, props.scroll_trigger])
8182

8283
return (
8384
<div

0 commit comments

Comments
 (0)