Skip to content

Commit 4f10fb1

Browse files
authored
Merge pull request #582 from emcconnell/feat/image-select
Add Screenshot Capture and Management Features to Chat and Workbench Components
2 parents 54c7e3d + 9b98670 commit 4f10fb1

File tree

6 files changed

+364
-6
lines changed

6 files changed

+364
-6
lines changed

app/commit.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "commit": "eb53146937cbe49a84aaaaa59882df6db4a1e459" }
1+
{ "commit": "eb53146937cbe49a84aaaaa59882df6db4a1e459" }

app/components/chat/BaseChat.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import FilePreview from './FilePreview';
2626
import { ModelSelector } from '~/components/chat/ModelSelector';
2727
import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
2828
import type { IProviderSetting, ProviderInfo } from '~/types/model';
29+
import { ScreenshotStateManager } from './ScreenshotStateManager';
2930

3031
const TEXTAREA_MIN_HEIGHT = 76;
3132

@@ -376,6 +377,16 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
376377
setImageDataList?.(imageDataList.filter((_, i) => i !== index));
377378
}}
378379
/>
380+
<ClientOnly>
381+
{() => (
382+
<ScreenshotStateManager
383+
setUploadedFiles={setUploadedFiles}
384+
setImageDataList={setImageDataList}
385+
uploadedFiles={uploadedFiles}
386+
imageDataList={imageDataList}
387+
/>
388+
)}
389+
</ClientOnly>
379390
<div
380391
className={classNames(
381392
'relative shadow-xs border border-bolt-elements-borderColor backdrop-blur rounded-lg',
@@ -425,15 +436,17 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
425436
}
426437

427438
event.preventDefault();
428-
439+
429440
if (isStreaming) {
430441
handleStop?.();
431442
return;
432443
}
444+
433445
// ignore if using input method engine
434446
if (event.nativeEvent.isComposing) {
435-
return
447+
return;
436448
}
449+
437450
handleSendMessage?.(event);
438451
}
439452
}}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useEffect } from 'react';
2+
3+
interface ScreenshotStateManagerProps {
4+
setUploadedFiles?: (files: File[]) => void;
5+
setImageDataList?: (dataList: string[]) => void;
6+
uploadedFiles: File[];
7+
imageDataList: string[];
8+
}
9+
10+
export const ScreenshotStateManager = ({
11+
setUploadedFiles,
12+
setImageDataList,
13+
uploadedFiles,
14+
imageDataList,
15+
}: ScreenshotStateManagerProps) => {
16+
useEffect(() => {
17+
if (setUploadedFiles && setImageDataList) {
18+
(window as any).__BOLT_SET_UPLOADED_FILES__ = setUploadedFiles;
19+
(window as any).__BOLT_SET_IMAGE_DATA_LIST__ = setImageDataList;
20+
(window as any).__BOLT_UPLOADED_FILES__ = uploadedFiles;
21+
(window as any).__BOLT_IMAGE_DATA_LIST__ = imageDataList;
22+
}
23+
24+
return () => {
25+
delete (window as any).__BOLT_SET_UPLOADED_FILES__;
26+
delete (window as any).__BOLT_SET_IMAGE_DATA_LIST__;
27+
delete (window as any).__BOLT_UPLOADED_FILES__;
28+
delete (window as any).__BOLT_IMAGE_DATA_LIST__;
29+
};
30+
}, [setUploadedFiles, setImageDataList, uploadedFiles, imageDataList]);
31+
32+
return null;
33+
};

app/components/workbench/Preview.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react';
33
import { IconButton } from '~/components/ui/IconButton';
44
import { workbenchStore } from '~/lib/stores/workbench';
55
import { PortDropdown } from './PortDropdown';
6+
import { ScreenshotSelector } from './ScreenshotSelector';
67

78
type ResizeSide = 'left' | 'right' | null;
89

@@ -20,6 +21,7 @@ export const Preview = memo(() => {
2021

2122
const [url, setUrl] = useState('');
2223
const [iframeUrl, setIframeUrl] = useState<string | undefined>();
24+
const [isSelectionMode, setIsSelectionMode] = useState(false);
2325

2426
// Toggle between responsive mode and device mode
2527
const [isDeviceModeOn, setIsDeviceModeOn] = useState(false);
@@ -218,12 +220,17 @@ export const Preview = memo(() => {
218220
)}
219221
<div className="bg-bolt-elements-background-depth-2 p-2 flex items-center gap-1.5">
220222
<IconButton icon="i-ph:arrow-clockwise" onClick={reloadPreview} />
221-
223+
<IconButton
224+
icon="i-ph:selection"
225+
onClick={() => setIsSelectionMode(!isSelectionMode)}
226+
className={isSelectionMode ? 'bg-bolt-elements-background-depth-3' : ''}
227+
/>
222228
<div
223229
className="flex items-center gap-1 flex-grow bg-bolt-elements-preview-addressBar-background border border-bolt-elements-borderColor text-bolt-elements-preview-addressBar-text rounded-full px-3 py-1 text-sm hover:bg-bolt-elements-preview-addressBar-backgroundHover hover:focus-within:bg-bolt-elements-preview-addressBar-backgroundActive focus-within:bg-bolt-elements-preview-addressBar-backgroundActive
224230
focus-within-border-bolt-elements-borderColorActive focus-within:text-bolt-elements-preview-addressBar-textActive"
225231
>
226232
<input
233+
title="URL"
227234
ref={inputRef}
228235
className="w-full bg-transparent outline-none"
229236
type="text"
@@ -281,7 +288,20 @@ export const Preview = memo(() => {
281288
}}
282289
>
283290
{activePreview ? (
284-
<iframe ref={iframeRef} className="border-none w-full h-full bg-white" src={iframeUrl} allowFullScreen />
291+
<>
292+
<iframe
293+
ref={iframeRef}
294+
title="preview"
295+
className="border-none w-full h-full bg-white"
296+
src={iframeUrl}
297+
allowFullScreen
298+
/>
299+
<ScreenshotSelector
300+
isSelectionMode={isSelectionMode}
301+
setIsSelectionMode={setIsSelectionMode}
302+
containerRef={iframeRef}
303+
/>
304+
</>
285305
) : (
286306
<div className="flex w-full h-full justify-center items-center bg-white">No preview available</div>
287307
)}

0 commit comments

Comments
 (0)