Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Screenshot Capture and Management Features to Chat and Workbench Components #582

Merged
merged 11 commits into from
Dec 14, 2024
2 changes: 1 addition & 1 deletion app/commit.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "commit": "eb53146937cbe49a84aaaaa59882df6db4a1e459" }
{ "commit": "eb53146937cbe49a84aaaaa59882df6db4a1e459" }
17 changes: 15 additions & 2 deletions app/components/chat/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import FilePreview from './FilePreview';
import { ModelSelector } from '~/components/chat/ModelSelector';
import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
import type { IProviderSetting, ProviderInfo } from '~/types/model';
import { ScreenshotStateManager } from './ScreenshotStateManager';

const TEXTAREA_MIN_HEIGHT = 76;

Expand Down Expand Up @@ -376,6 +377,16 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
setImageDataList?.(imageDataList.filter((_, i) => i !== index));
}}
/>
<ClientOnly>
{() => (
<ScreenshotStateManager
setUploadedFiles={setUploadedFiles}
setImageDataList={setImageDataList}
uploadedFiles={uploadedFiles}
imageDataList={imageDataList}
/>
)}
</ClientOnly>
<div
className={classNames(
'relative shadow-xs border border-bolt-elements-borderColor backdrop-blur rounded-lg',
Expand Down Expand Up @@ -425,15 +436,17 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
}

event.preventDefault();

if (isStreaming) {
handleStop?.();
return;
}

// ignore if using input method engine
if (event.nativeEvent.isComposing) {
return
return;
}

handleSendMessage?.(event);
}
}}
Expand Down
33 changes: 33 additions & 0 deletions app/components/chat/ScreenshotStateManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect } from 'react';

interface ScreenshotStateManagerProps {
setUploadedFiles?: (files: File[]) => void;
setImageDataList?: (dataList: string[]) => void;
uploadedFiles: File[];
imageDataList: string[];
}

export const ScreenshotStateManager = ({
setUploadedFiles,
setImageDataList,
uploadedFiles,
imageDataList,
}: ScreenshotStateManagerProps) => {
useEffect(() => {
if (setUploadedFiles && setImageDataList) {
(window as any).__BOLT_SET_UPLOADED_FILES__ = setUploadedFiles;
(window as any).__BOLT_SET_IMAGE_DATA_LIST__ = setImageDataList;
(window as any).__BOLT_UPLOADED_FILES__ = uploadedFiles;
(window as any).__BOLT_IMAGE_DATA_LIST__ = imageDataList;
}

return () => {
delete (window as any).__BOLT_SET_UPLOADED_FILES__;
delete (window as any).__BOLT_SET_IMAGE_DATA_LIST__;
delete (window as any).__BOLT_UPLOADED_FILES__;
delete (window as any).__BOLT_IMAGE_DATA_LIST__;
};
}, [setUploadedFiles, setImageDataList, uploadedFiles, imageDataList]);

return null;
};
24 changes: 22 additions & 2 deletions app/components/workbench/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { IconButton } from '~/components/ui/IconButton';
import { workbenchStore } from '~/lib/stores/workbench';
import { PortDropdown } from './PortDropdown';
import { ScreenshotSelector } from './ScreenshotSelector';

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

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

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

// Toggle between responsive mode and device mode
const [isDeviceModeOn, setIsDeviceModeOn] = useState(false);
Expand Down Expand Up @@ -218,12 +220,17 @@ export const Preview = memo(() => {
)}
<div className="bg-bolt-elements-background-depth-2 p-2 flex items-center gap-1.5">
<IconButton icon="i-ph:arrow-clockwise" onClick={reloadPreview} />

<IconButton
icon="i-ph:selection"
onClick={() => setIsSelectionMode(!isSelectionMode)}
className={isSelectionMode ? 'bg-bolt-elements-background-depth-3' : ''}
/>
<div
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
focus-within-border-bolt-elements-borderColorActive focus-within:text-bolt-elements-preview-addressBar-textActive"
>
<input
title="URL"
ref={inputRef}
className="w-full bg-transparent outline-none"
type="text"
Expand Down Expand Up @@ -281,7 +288,20 @@ export const Preview = memo(() => {
}}
>
{activePreview ? (
<iframe ref={iframeRef} className="border-none w-full h-full bg-white" src={iframeUrl} allowFullScreen />
<>
<iframe
ref={iframeRef}
title="preview"
className="border-none w-full h-full bg-white"
src={iframeUrl}
allowFullScreen
/>
<ScreenshotSelector
isSelectionMode={isSelectionMode}
setIsSelectionMode={setIsSelectionMode}
containerRef={iframeRef}
/>
</>
) : (
<div className="flex w-full h-full justify-center items-center bg-white">No preview available</div>
)}
Expand Down
Loading
Loading