vzcode 2.15.0 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{bindings_wasm_bg-9w8E-TmY.wasm → bindings_wasm_bg-D9vNLG9F.wasm} +0 -0
- package/dist/assets/buildWorker-DL4wXpRr.js +695 -0
- package/dist/assets/index-BiPmUreW.js +474 -0
- package/dist/assets/{index-DLm5FQ0E.css → index-BvGPtSrr.css} +1 -1
- package/dist/assets/worker-RJ-cjbld.js +327 -0
- package/dist/index.html +2 -2
- package/dist/server/aiChatHandler/chatOperations.js +168 -35
- package/dist/server/aiChatHandler/index.js +11 -30
- package/dist/server/aiChatHandler/llmStreaming.js +105 -118
- package/package.json +11 -11
- package/src/client/AIAssist/AIAssistWidget/index.tsx +12 -1
- package/src/client/App/useShareDB.ts +1 -1
- package/src/client/CodeEditor/index.tsx +9 -10
- package/src/client/VZCodeContext/types.ts +3 -1
- package/src/client/VZCodeContext/useVZCodeState.ts +7 -5
- package/src/client/VZRight.tsx +10 -2
- package/src/client/VZSidebar/AIChat/ChatInput.tsx +1 -7
- package/src/client/VZSidebar/AIChat/DiffView.scss +1 -0
- package/src/client/VZSidebar/AIChat/DiffView.tsx +72 -29
- package/src/client/VZSidebar/AIChat/FileEditingIndicator.tsx +89 -0
- package/src/client/VZSidebar/AIChat/IndividualFileDiff.tsx +86 -0
- package/src/client/VZSidebar/AIChat/JumpToLatestButton.tsx +64 -0
- package/src/client/VZSidebar/AIChat/Message.tsx +124 -56
- package/src/client/VZSidebar/AIChat/MessageList.tsx +155 -114
- package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +4 -118
- package/src/client/VZSidebar/AIChat/index.tsx +59 -19
- package/src/client/VZSidebar/AIChat/styles.scss +190 -43
- package/src/client/VZSidebar/Item.tsx +2 -2
- package/src/client/VZSidebar/Search.tsx +13 -6
- package/src/client/VZSidebar/VisualEditor/VisualEditor.tsx +39 -23
- package/src/client/VZSidebar/VisualEditor/utils.ts +1 -1
- package/src/client/VZSidebar/index.tsx +12 -10
- package/src/client/VZSidebar/useDragAndDrop.tsx +225 -214
- package/src/client/featureFlags.ts +3 -0
- package/src/client/hooks/useAutoScroll.ts +328 -0
- package/src/client/tabsSearchParameters.ts +1 -17
- package/src/client/useFileCRUD.ts +5 -2
- package/src/client/useKeyboardShortcuts.ts +7 -0
- package/src/client/useOpenDirectories.ts +2 -1
- package/src/client/usePrettier/index.ts +1 -1
- package/src/client/useURLSync.ts +1 -0
- package/src/client/utils/scrollUtils.ts +170 -0
- package/src/client/vzReducer/searchReducer.ts +6 -3
- package/src/server/aiChatHandler/chatOperations.ts +224 -43
- package/src/server/aiChatHandler/index.ts +8 -48
- package/src/server/aiChatHandler/llmStreaming.ts +149 -170
- package/src/types.ts +81 -1
- package/dist/assets/buildWorker-Bi6wsfQk.js +0 -695
- package/dist/assets/index-BpUpNto4.js +0 -461
- package/dist/assets/worker-BSzbM0Uo.js +0 -330
- package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +0 -35
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
memo,
|
|
3
|
-
useRef,
|
|
4
|
-
useEffect,
|
|
5
|
-
useCallback,
|
|
6
|
-
useState,
|
|
7
|
-
} from 'react';
|
|
1
|
+
import { memo } from 'react';
|
|
8
2
|
import Markdown from 'react-markdown';
|
|
9
3
|
import remarkGfm from 'remark-gfm';
|
|
10
4
|
|
|
@@ -17,115 +11,6 @@ const ThinkingScratchpadComponent = ({
|
|
|
17
11
|
content,
|
|
18
12
|
isVisible,
|
|
19
13
|
}: ThinkingScratchpadProps) => {
|
|
20
|
-
const contentRef = useRef<HTMLDivElement>(null);
|
|
21
|
-
const [isUserScrolled, setIsUserScrolled] =
|
|
22
|
-
useState(false);
|
|
23
|
-
const [autoScrollEnabled, setAutoScrollEnabled] =
|
|
24
|
-
useState(true);
|
|
25
|
-
const scrollTimeoutRef =
|
|
26
|
-
useRef<ReturnType<typeof setTimeout>>();
|
|
27
|
-
|
|
28
|
-
// Check if the user is scrolled to the bottom
|
|
29
|
-
const isScrolledToBottom = useCallback(() => {
|
|
30
|
-
const container = contentRef.current;
|
|
31
|
-
if (!container) return true;
|
|
32
|
-
|
|
33
|
-
const threshold = 10; // Allow 10px tolerance for "at bottom"
|
|
34
|
-
const { scrollTop, scrollHeight, clientHeight } =
|
|
35
|
-
container;
|
|
36
|
-
return (
|
|
37
|
-
scrollHeight - scrollTop - clientHeight < threshold
|
|
38
|
-
);
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
// Smooth scroll to bottom
|
|
42
|
-
const scrollToBottom = useCallback(() => {
|
|
43
|
-
if (!autoScrollEnabled) return;
|
|
44
|
-
|
|
45
|
-
const container = contentRef.current;
|
|
46
|
-
if (container) {
|
|
47
|
-
container.scrollTo({
|
|
48
|
-
top: container.scrollHeight,
|
|
49
|
-
behavior: 'smooth',
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}, [autoScrollEnabled]);
|
|
53
|
-
|
|
54
|
-
// Handle scroll events to detect user manual scrolling
|
|
55
|
-
const handleScroll = useCallback(() => {
|
|
56
|
-
const container = contentRef.current;
|
|
57
|
-
if (!container) return;
|
|
58
|
-
|
|
59
|
-
const isAtBottom = isScrolledToBottom();
|
|
60
|
-
|
|
61
|
-
// Clear any existing timeout
|
|
62
|
-
if (scrollTimeoutRef.current) {
|
|
63
|
-
clearTimeout(scrollTimeoutRef.current);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// If user scrolled up from bottom, disable auto-scroll
|
|
67
|
-
if (!isAtBottom && !isUserScrolled) {
|
|
68
|
-
setIsUserScrolled(true);
|
|
69
|
-
setAutoScrollEnabled(false);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// If user scrolled back to bottom, re-enable auto-scroll after a brief delay
|
|
73
|
-
if (isAtBottom && isUserScrolled) {
|
|
74
|
-
scrollTimeoutRef.current = setTimeout(() => {
|
|
75
|
-
setIsUserScrolled(false);
|
|
76
|
-
setAutoScrollEnabled(true);
|
|
77
|
-
}, 300); // 300ms delay to prevent flickering
|
|
78
|
-
}
|
|
79
|
-
}, [isUserScrolled, isScrolledToBottom]);
|
|
80
|
-
|
|
81
|
-
// Auto-scroll when content changes, but only if auto-scroll is enabled
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (
|
|
84
|
-
autoScrollEnabled &&
|
|
85
|
-
!isUserScrolled &&
|
|
86
|
-
isVisible &&
|
|
87
|
-
content
|
|
88
|
-
) {
|
|
89
|
-
// Use a short debounce to prevent multiple scroll calls during rapid updates
|
|
90
|
-
if (scrollTimeoutRef.current) {
|
|
91
|
-
clearTimeout(scrollTimeoutRef.current);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
scrollTimeoutRef.current = setTimeout(() => {
|
|
95
|
-
scrollToBottom();
|
|
96
|
-
}, 50); // 50ms debounce
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return () => {
|
|
100
|
-
if (scrollTimeoutRef.current) {
|
|
101
|
-
clearTimeout(scrollTimeoutRef.current);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
}, [
|
|
105
|
-
content,
|
|
106
|
-
autoScrollEnabled,
|
|
107
|
-
isUserScrolled,
|
|
108
|
-
isVisible,
|
|
109
|
-
scrollToBottom,
|
|
110
|
-
]);
|
|
111
|
-
|
|
112
|
-
// Clean up timeout on unmount
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
return () => {
|
|
115
|
-
if (scrollTimeoutRef.current) {
|
|
116
|
-
clearTimeout(scrollTimeoutRef.current);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
}, []);
|
|
120
|
-
|
|
121
|
-
// Reset scroll state when component becomes visible
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
if (isVisible) {
|
|
124
|
-
setIsUserScrolled(false);
|
|
125
|
-
setAutoScrollEnabled(true);
|
|
126
|
-
}
|
|
127
|
-
}, [isVisible]);
|
|
128
|
-
|
|
129
14
|
if (!isVisible || !content) {
|
|
130
15
|
return null;
|
|
131
16
|
}
|
|
@@ -140,8 +25,9 @@ const ThinkingScratchpadComponent = ({
|
|
|
140
25
|
</div>
|
|
141
26
|
<div
|
|
142
27
|
className="thinking-scratchpad-content"
|
|
143
|
-
|
|
144
|
-
|
|
28
|
+
role="log"
|
|
29
|
+
aria-live="polite"
|
|
30
|
+
aria-relevant="additions"
|
|
145
31
|
>
|
|
146
32
|
<Markdown remarkPlugins={[remarkGfm]}>
|
|
147
33
|
{content}
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
import { VZCodeContext } from '../../VZCodeContext';
|
|
8
8
|
import { MessageList } from './MessageList';
|
|
9
9
|
import { ChatInput } from './ChatInput';
|
|
10
|
+
import { ExtendedVizChat } from '../../../types.js';
|
|
11
|
+
import { useAutoScroll } from '../../hooks/useAutoScroll';
|
|
12
|
+
import { JumpToLatestButton } from './JumpToLatestButton';
|
|
10
13
|
import './styles.scss';
|
|
11
14
|
|
|
12
15
|
const DEBUG = false;
|
|
@@ -50,13 +53,11 @@ export const AIChat = () => {
|
|
|
50
53
|
content,
|
|
51
54
|
aiChatMode,
|
|
52
55
|
aiChatMessage,
|
|
53
|
-
isLoading,
|
|
54
56
|
currentChatId,
|
|
55
57
|
selectedChatId,
|
|
56
58
|
setSelectedChatId,
|
|
57
59
|
aiErrorMessage,
|
|
58
60
|
setAIChatMode,
|
|
59
|
-
clearStoredAIPrompt,
|
|
60
61
|
getStoredAIPrompt,
|
|
61
62
|
setAIChatMessage,
|
|
62
63
|
handleSendMessage,
|
|
@@ -64,17 +65,42 @@ export const AIChat = () => {
|
|
|
64
65
|
navigateMessageHistoryUp,
|
|
65
66
|
navigateMessageHistoryDown,
|
|
66
67
|
resetMessageHistoryNavigation,
|
|
68
|
+
enableMinimalEditFlow,
|
|
67
69
|
} = useContext(VZCodeContext);
|
|
68
70
|
|
|
71
|
+
// Use the new simplified auto-scroll hook
|
|
72
|
+
const {
|
|
73
|
+
containerRef: messagesContainerRef,
|
|
74
|
+
showJumpButton,
|
|
75
|
+
onNewEvent,
|
|
76
|
+
onJumpToLatest,
|
|
77
|
+
beforeRender,
|
|
78
|
+
afterRender,
|
|
79
|
+
} = useAutoScroll({ threshold: 24 });
|
|
80
|
+
|
|
69
81
|
// Get the active chat ID and chat data
|
|
70
82
|
// If selectedChatId is set, use it; otherwise, if no chat selected, don't default to currentChatId
|
|
71
83
|
const activeChatId = selectedChatId;
|
|
72
84
|
const currentChat = activeChatId
|
|
73
85
|
? content?.chats?.[activeChatId]
|
|
74
86
|
: null;
|
|
75
|
-
const rawMessages =
|
|
87
|
+
const rawMessages = useMemo(
|
|
88
|
+
() => currentChat?.messages || [],
|
|
89
|
+
[currentChat?.messages],
|
|
90
|
+
);
|
|
76
91
|
const aiStatus = currentChat?.aiStatus;
|
|
77
92
|
const aiScratchpad = currentChat?.aiScratchpad;
|
|
93
|
+
const currentStatus = (currentChat as ExtendedVizChat)
|
|
94
|
+
?.currentStatus;
|
|
95
|
+
|
|
96
|
+
// Debug logging for AI status
|
|
97
|
+
DEBUG && console.log('AIChat: currentChat:', currentChat);
|
|
98
|
+
DEBUG && console.log('AIChat: aiStatus:', aiStatus);
|
|
99
|
+
DEBUG &&
|
|
100
|
+
console.log(
|
|
101
|
+
'AIChat: enableMinimalEditFlow:',
|
|
102
|
+
enableMinimalEditFlow,
|
|
103
|
+
);
|
|
78
104
|
|
|
79
105
|
// Transform messages to ensure they have required id field - memoized to avoid recreation
|
|
80
106
|
const messages = useMemo(
|
|
@@ -166,7 +192,10 @@ export const AIChat = () => {
|
|
|
166
192
|
return (
|
|
167
193
|
<div className="ai-chat-container">
|
|
168
194
|
<div className="ai-chat-content">
|
|
169
|
-
<div
|
|
195
|
+
<div
|
|
196
|
+
className="ai-chat-messages-container"
|
|
197
|
+
ref={messagesContainerRef}
|
|
198
|
+
>
|
|
170
199
|
{isEmptyState ? (
|
|
171
200
|
<div className="ai-chat-empty">
|
|
172
201
|
<div className="ai-chat-empty-icon">✨</div>
|
|
@@ -198,7 +227,7 @@ export const AIChat = () => {
|
|
|
198
227
|
)
|
|
199
228
|
}
|
|
200
229
|
>
|
|
201
|
-
|
|
230
|
+
"Explain how this works"
|
|
202
231
|
</button>
|
|
203
232
|
<button
|
|
204
233
|
className="ai-chat-suggested-prompt"
|
|
@@ -208,8 +237,8 @@ export const AIChat = () => {
|
|
|
208
237
|
)
|
|
209
238
|
}
|
|
210
239
|
>
|
|
211
|
-
|
|
212
|
-
circles are bigger
|
|
240
|
+
"How could I change it so
|
|
241
|
+
that the circles are bigger?"
|
|
213
242
|
</button>
|
|
214
243
|
<button
|
|
215
244
|
className="ai-chat-suggested-prompt"
|
|
@@ -219,7 +248,8 @@ export const AIChat = () => {
|
|
|
219
248
|
)
|
|
220
249
|
}
|
|
221
250
|
>
|
|
222
|
-
|
|
251
|
+
"What does this function
|
|
252
|
+
do?"
|
|
223
253
|
</button>
|
|
224
254
|
<button
|
|
225
255
|
className="ai-chat-suggested-prompt"
|
|
@@ -229,8 +259,8 @@ export const AIChat = () => {
|
|
|
229
259
|
)
|
|
230
260
|
}
|
|
231
261
|
>
|
|
232
|
-
|
|
233
|
-
accessible
|
|
262
|
+
"How can I make this more
|
|
263
|
+
accessible?"
|
|
234
264
|
</button>
|
|
235
265
|
</div>
|
|
236
266
|
</>
|
|
@@ -246,7 +276,8 @@ export const AIChat = () => {
|
|
|
246
276
|
)
|
|
247
277
|
}
|
|
248
278
|
>
|
|
249
|
-
|
|
279
|
+
"Change the circles to
|
|
280
|
+
squares"
|
|
250
281
|
</button>
|
|
251
282
|
<button
|
|
252
283
|
className="ai-chat-suggested-prompt"
|
|
@@ -256,8 +287,8 @@ export const AIChat = () => {
|
|
|
256
287
|
)
|
|
257
288
|
}
|
|
258
289
|
>
|
|
259
|
-
|
|
260
|
-
animation
|
|
290
|
+
"Add a button that toggles
|
|
291
|
+
the animation"
|
|
261
292
|
</button>
|
|
262
293
|
<button
|
|
263
294
|
className="ai-chat-suggested-prompt"
|
|
@@ -267,8 +298,8 @@ export const AIChat = () => {
|
|
|
267
298
|
)
|
|
268
299
|
}
|
|
269
300
|
>
|
|
270
|
-
|
|
271
|
-
responsive
|
|
301
|
+
"Fix the CSS so the layout is
|
|
302
|
+
responsive"
|
|
272
303
|
</button>
|
|
273
304
|
<button
|
|
274
305
|
className="ai-chat-suggested-prompt"
|
|
@@ -278,8 +309,8 @@ export const AIChat = () => {
|
|
|
278
309
|
)
|
|
279
310
|
}
|
|
280
311
|
>
|
|
281
|
-
|
|
282
|
-
async/await
|
|
312
|
+
"Refactor this function to
|
|
313
|
+
use async/await"
|
|
283
314
|
</button>
|
|
284
315
|
</div>
|
|
285
316
|
</>
|
|
@@ -297,9 +328,14 @@ export const AIChat = () => {
|
|
|
297
328
|
) : (
|
|
298
329
|
<MessageList
|
|
299
330
|
messages={messages}
|
|
300
|
-
isLoading={
|
|
331
|
+
isLoading={false}
|
|
301
332
|
chatId={selectedChatId || currentChatId}
|
|
302
333
|
aiScratchpad={aiScratchpad}
|
|
334
|
+
currentStatus={currentStatus}
|
|
335
|
+
onNewEvent={onNewEvent}
|
|
336
|
+
onJumpToLatest={onJumpToLatest}
|
|
337
|
+
beforeRender={beforeRender}
|
|
338
|
+
afterRender={afterRender}
|
|
303
339
|
/>
|
|
304
340
|
)}
|
|
305
341
|
{aiErrorMessage && (
|
|
@@ -321,6 +357,11 @@ export const AIChat = () => {
|
|
|
321
357
|
</div>
|
|
322
358
|
</div>
|
|
323
359
|
)}
|
|
360
|
+
{/* Jump to Latest Button */}
|
|
361
|
+
<JumpToLatestButton
|
|
362
|
+
visible={showJumpButton}
|
|
363
|
+
onClick={onJumpToLatest}
|
|
364
|
+
/>
|
|
324
365
|
</div>
|
|
325
366
|
</div>
|
|
326
367
|
<div className="ai-chat-input-fixed">
|
|
@@ -328,7 +369,6 @@ export const AIChat = () => {
|
|
|
328
369
|
aiChatMessage={aiChatMessage}
|
|
329
370
|
setAIChatMessage={setAIChatMessage}
|
|
330
371
|
onSendMessage={handleSendMessageWithSelection}
|
|
331
|
-
isLoading={isLoading}
|
|
332
372
|
focused={aiChatFocused}
|
|
333
373
|
aiChatMode={aiChatMode}
|
|
334
374
|
setAIChatMode={setAIChatMode}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
overflow: hidden;
|
|
11
11
|
display: flex;
|
|
12
12
|
flex-direction: column;
|
|
13
|
+
position: relative;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
.ai-chat-messages-container {
|
|
@@ -19,6 +20,21 @@
|
|
|
19
20
|
padding-bottom: 0;
|
|
20
21
|
display: flex;
|
|
21
22
|
flex-direction: column;
|
|
23
|
+
scroll-behavior: smooth;
|
|
24
|
+
|
|
25
|
+
&::-webkit-scrollbar {
|
|
26
|
+
width: 6px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&::-webkit-scrollbar-track {
|
|
30
|
+
background: var(--vh-color-neutral-02);
|
|
31
|
+
border-radius: 3px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&::-webkit-scrollbar-thumb {
|
|
35
|
+
background: var(--vh-color-neutral-04);
|
|
36
|
+
border-radius: 3px;
|
|
37
|
+
}
|
|
22
38
|
}
|
|
23
39
|
|
|
24
40
|
.ai-chat-input-fixed {
|
|
@@ -111,6 +127,125 @@
|
|
|
111
127
|
}
|
|
112
128
|
}
|
|
113
129
|
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Streaming Message Components
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
.streaming-message {
|
|
135
|
+
.text-chunk {
|
|
136
|
+
margin-bottom: 1rem;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.file-editing-indicator {
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
margin: 0.25rem 0;
|
|
144
|
+
background-color: var(--vscode-editor-background);
|
|
145
|
+
border: 1px solid var(--vscode-panel-border);
|
|
146
|
+
border-radius: 4px;
|
|
147
|
+
|
|
148
|
+
.file-editing-header {
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 0.5rem;
|
|
152
|
+
width: 100%;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.file-editing-icon {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
justify-content: center;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.file-editing-text {
|
|
162
|
+
color: var(--vscode-foreground);
|
|
163
|
+
font-size: 0.9rem;
|
|
164
|
+
flex: 1;
|
|
165
|
+
|
|
166
|
+
code {
|
|
167
|
+
background-color: var(
|
|
168
|
+
--vscode-textCodeBlock-background
|
|
169
|
+
);
|
|
170
|
+
padding: 0.2rem 0.4rem;
|
|
171
|
+
border-radius: 3px;
|
|
172
|
+
font-family: var(--vscode-editor-font-family);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.file-editing-done-container {
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
justify-content: space-between;
|
|
179
|
+
width: 100%;
|
|
180
|
+
gap: 0.5rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.file-editing-done-status {
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
gap: 8px;
|
|
187
|
+
font-size: 16px;
|
|
188
|
+
font-weight: 500;
|
|
189
|
+
margin-left: 4px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.file-editing-done-widgets {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 0.5rem;
|
|
196
|
+
flex-shrink: 0;
|
|
197
|
+
|
|
198
|
+
// // Override the margin-top for undo-button-container when inside file editing indicator
|
|
199
|
+
// .undo-button-container {
|
|
200
|
+
// margin-top: 0;
|
|
201
|
+
// margin-bottom: 0;
|
|
202
|
+
// }
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.status-indicator {
|
|
208
|
+
display: flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
padding: 0.5rem;
|
|
211
|
+
margin: 0.5rem 0;
|
|
212
|
+
background-color: var(--vscode-notifications-background);
|
|
213
|
+
border-left: 3px solid
|
|
214
|
+
var(--vscode-progressBar-foreground);
|
|
215
|
+
border-radius: 0 4px 4px 0;
|
|
216
|
+
|
|
217
|
+
.status-content {
|
|
218
|
+
display: flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
gap: 0.5rem;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.status-icon {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
justify-content: center;
|
|
227
|
+
|
|
228
|
+
.done-icon {
|
|
229
|
+
font-size: 1rem;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.status-text {
|
|
234
|
+
color: var(--vscode-foreground);
|
|
235
|
+
font-size: 0.9rem;
|
|
236
|
+
font-weight: 500;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@keyframes spin {
|
|
241
|
+
0% {
|
|
242
|
+
transform: rotate(0deg);
|
|
243
|
+
}
|
|
244
|
+
100% {
|
|
245
|
+
transform: rotate(360deg);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
114
249
|
.ai-chat-list {
|
|
115
250
|
margin-top: 20px;
|
|
116
251
|
|
|
@@ -126,24 +261,8 @@
|
|
|
126
261
|
|
|
127
262
|
.ai-chat-messages {
|
|
128
263
|
flex: 1;
|
|
129
|
-
overflow-y: auto;
|
|
130
264
|
margin-bottom: 10px;
|
|
131
265
|
padding-right: 5px;
|
|
132
|
-
scroll-behavior: smooth;
|
|
133
|
-
|
|
134
|
-
&::-webkit-scrollbar {
|
|
135
|
-
width: 6px;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
&::-webkit-scrollbar-track {
|
|
139
|
-
background: var(--vh-color-neutral-02);
|
|
140
|
-
border-radius: 3px;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
&::-webkit-scrollbar-thumb {
|
|
144
|
-
background: var(--vh-color-neutral-04);
|
|
145
|
-
border-radius: 3px;
|
|
146
|
-
}
|
|
147
266
|
}
|
|
148
267
|
|
|
149
268
|
.ai-chat-message {
|
|
@@ -200,33 +319,6 @@
|
|
|
200
319
|
line-height: 1.4;
|
|
201
320
|
word-wrap: break-word;
|
|
202
321
|
|
|
203
|
-
.undo-button-container {
|
|
204
|
-
display: flex;
|
|
205
|
-
justify-content: flex-end;
|
|
206
|
-
gap: 8px;
|
|
207
|
-
margin-top: 16px;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.undo-button {
|
|
211
|
-
background-color: #dc3545;
|
|
212
|
-
color: white;
|
|
213
|
-
border: none;
|
|
214
|
-
border-radius: 4px;
|
|
215
|
-
padding: 4px 8px;
|
|
216
|
-
font-size: 12px;
|
|
217
|
-
cursor: pointer;
|
|
218
|
-
transition: background-color 0.2s;
|
|
219
|
-
|
|
220
|
-
&:hover:not(:disabled) {
|
|
221
|
-
background-color: #c82333;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
&:disabled {
|
|
225
|
-
background-color: #6c757d;
|
|
226
|
-
cursor: not-allowed;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
322
|
// Markdown styling for dark backgrounds
|
|
231
323
|
pre {
|
|
232
324
|
background-color: rgba(0, 0, 0, 0.3);
|
|
@@ -549,6 +641,61 @@
|
|
|
549
641
|
}
|
|
550
642
|
}
|
|
551
643
|
|
|
644
|
+
// Jump to latest button styles
|
|
645
|
+
.jump-to-latest-button {
|
|
646
|
+
position: absolute;
|
|
647
|
+
left: 50%;
|
|
648
|
+
transform: translateX(-50%);
|
|
649
|
+
bottom: 20px; // Position above the input area
|
|
650
|
+
width: 44px;
|
|
651
|
+
height: 44px;
|
|
652
|
+
border-radius: 50%;
|
|
653
|
+
background: var(--vh-color-primary-01);
|
|
654
|
+
border: none;
|
|
655
|
+
color: white;
|
|
656
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
657
|
+
cursor: pointer;
|
|
658
|
+
display: flex;
|
|
659
|
+
align-items: center;
|
|
660
|
+
justify-content: center;
|
|
661
|
+
z-index: 10;
|
|
662
|
+
transition: all 0.2s ease;
|
|
663
|
+
|
|
664
|
+
&:hover {
|
|
665
|
+
background: var(--vh-color-primary-02);
|
|
666
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
667
|
+
transform: translateX(-50%) scale(1.05);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
&:focus {
|
|
671
|
+
outline: 2px solid var(--vh-color-primary-01);
|
|
672
|
+
outline-offset: 2px;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
&:active {
|
|
676
|
+
transform: translateX(-50%) scale(0.95);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.jump-to-latest-icon {
|
|
680
|
+
font-size: 16px;
|
|
681
|
+
font-weight: bold;
|
|
682
|
+
line-height: 1;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Respect prefers-reduced-motion
|
|
686
|
+
@media (prefers-reduced-motion: reduce) {
|
|
687
|
+
transition: none;
|
|
688
|
+
|
|
689
|
+
&:hover {
|
|
690
|
+
transform: translateX(-50%);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
&:active {
|
|
694
|
+
transform: translateX(-50%);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
552
699
|
// Thinking scratchpad styles
|
|
553
700
|
.thinking-scratchpad {
|
|
554
701
|
background: var(--vh-color-neutral-02);
|
|
@@ -140,11 +140,11 @@ export const Item = ({
|
|
|
140
140
|
|
|
141
141
|
const handleMouseEnter = useCallback(() => {
|
|
142
142
|
setIsHovered(true);
|
|
143
|
-
}, []);
|
|
143
|
+
}, [setIsHovered]);
|
|
144
144
|
|
|
145
145
|
const handleMouseLeave = useCallback(() => {
|
|
146
146
|
setIsHovered(false);
|
|
147
|
-
}, []);
|
|
147
|
+
}, [setIsHovered]);
|
|
148
148
|
|
|
149
149
|
const onChange = useCallback(() => {
|
|
150
150
|
setRenameValue(renameInputRef.current.value);
|
|
@@ -98,7 +98,7 @@ export const Search = () => {
|
|
|
98
98
|
} else {
|
|
99
99
|
setIsMounted(true);
|
|
100
100
|
}
|
|
101
|
-
}, [pattern]);
|
|
101
|
+
}, [pattern, isMounted, setSearchResults, shareDBDoc]);
|
|
102
102
|
|
|
103
103
|
const flattenResult = useCallback(
|
|
104
104
|
(fileId: string, file: SearchFile) => {
|
|
@@ -111,19 +111,26 @@ export const Search = () => {
|
|
|
111
111
|
: 'open',
|
|
112
112
|
);
|
|
113
113
|
},
|
|
114
|
-
[
|
|
114
|
+
[
|
|
115
|
+
setSearchFileVisibility,
|
|
116
|
+
shareDBDoc,
|
|
117
|
+
focusedChildIndex,
|
|
118
|
+
],
|
|
115
119
|
);
|
|
116
120
|
|
|
117
|
-
const closeResult = useCallback(
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
const closeResult = useCallback(
|
|
122
|
+
(fileId: VizFileId) => {
|
|
123
|
+
setSearchFileVisibility(shareDBDoc, fileId, 'closed');
|
|
124
|
+
},
|
|
125
|
+
[setSearchFileVisibility, shareDBDoc],
|
|
126
|
+
);
|
|
120
127
|
|
|
121
128
|
const focusFileElement = useCallback(
|
|
122
129
|
(fileId: VizFileId, index: number) => {
|
|
123
130
|
setActiveFileId(fileId);
|
|
124
131
|
setSearchFocusedIndex(index, null);
|
|
125
132
|
},
|
|
126
|
-
[],
|
|
133
|
+
[setActiveFileId, setSearchFocusedIndex],
|
|
127
134
|
);
|
|
128
135
|
|
|
129
136
|
const handleKeyDown = (event) => {
|