Skip to content

Commit ddb506e

Browse files
committed
Merge branch 'dev'
2 parents 1b59a67 + 0fafe3c commit ddb506e

9 files changed

Lines changed: 198 additions & 125 deletions

File tree

‎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="list-flat"
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

‎packages/vscode/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "gemini-coder",
33
"displayName": "Code Web Chat",
44
"description": "AI coding without agents (CWC)",
5-
"version": "1.519.0",
5+
"version": "1.520.0",
66
"scripts": {
77
"build": "npx vsce package --no-dependencies",
88
"vscode:prepublish": "rimraf out && npm run compile",

‎packages/vscode/src/commands/apply-chat-response-command/utils/clipboard-parser/parsers/multiple-files-parser.ts‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,23 @@ export const parse_multiple_files = (params: {
186186
!potential_path.includes(' ') &&
187187
/[a-zA-Z0-9]/.test(potential_path)
188188
) {
189-
let is_followed_by_code_block = false
190-
for (let j = i + 1; j < lines.length; j++) {
191-
const next_line = lines[j].trim()
192-
if (next_line.startsWith('```')) {
193-
is_followed_by_code_block = true
194-
break
189+
const before = line.substring(0, match.index!)
190+
const after = line.substring(match.index! + match[0].length)
191+
192+
// Avoid matching file paths that are part of a sentence.
193+
if (!(/[a-zA-Z]/.test(before) && /[a-zA-Z]/.test(after))) {
194+
let is_followed_by_code_block = false
195+
for (let j = i + 1; j < lines.length; j++) {
196+
const next_line = lines[j].trim()
197+
if (next_line.startsWith('```')) {
198+
is_followed_by_code_block = true
199+
break
200+
}
195201
}
196-
}
197202

198-
if (is_followed_by_code_block) {
199-
extracted_filename = potential_path
203+
if (is_followed_by_code_block) {
204+
extracted_filename = potential_path
205+
}
200206
}
201207
}
202208
}

‎packages/vscode/src/views/settings/frontend/Home/Home.tsx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,17 @@ export const Home: React.FC<Props> = (props) => {
275275
on_clear_checks_in_workspace_behavior_change={
276276
props.on_clear_checks_in_workspace_behavior_change
277277
}
278+
edit_format_instructions={props.edit_format_instructions}
279+
on_edit_format_instructions_change={
280+
props.on_edit_format_instructions_change
281+
}
278282
on_open_editor_settings={props.on_open_editor_settings}
279283
on_stuck_change={general_on_stuck_change}
280284
/>
281285
<PresetsSection
282286
ref={(el) => (section_refs.current['presets'] = el)}
283287
gemini_user_id={props.gemini_user_id}
284288
on_gemini_user_id_change={props.on_gemini_user_id_change}
285-
edit_format_instructions={props.edit_format_instructions}
286-
on_edit_format_instructions_change={
287-
props.on_edit_format_instructions_change
288-
}
289289
on_stuck_change={presets_on_stuck_change}
290290
/>
291291
<Section

0 commit comments

Comments
 (0)