mirror of
https://github.com/responsively-org/responsively-app
synced 2024-11-10 14:54:12 +00:00
Merge branch 'main' of https://github.com/WayneRocha/responsively-app
This commit is contained in:
commit
83006cd37d
7 changed files with 147 additions and 8 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ResponsivelyApp",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"description": "A developer-friendly browser for developing responsive web apps",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
|
|
7
desktop-app/src/common/webViewUtils.ts
Normal file
7
desktop-app/src/common/webViewUtils.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export function updateWebViewHeightAndScale(
|
||||
webView: HTMLElement | Electron.WebviewTag,
|
||||
pageHeight: number
|
||||
) {
|
||||
webView.style.height = `${pageHeight}px`;
|
||||
webView.style.transform = `scale(0.1)`;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
/* eslint-disable promise/always-return */
|
||||
import { Device } from 'common/deviceList';
|
||||
import { ipcMain, shell, webContents } from 'electron';
|
||||
import { writeFile, ensureDir } from 'fs-extra';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { homedir } from 'os';
|
||||
|
||||
export interface ScreenshotArgs {
|
||||
webContentsId: number;
|
||||
|
@ -10,17 +11,35 @@ export interface ScreenshotArgs {
|
|||
device: Device;
|
||||
}
|
||||
|
||||
export interface ScreenshotAllArgs {
|
||||
webContentsId: number;
|
||||
device: Device;
|
||||
previousHeight: string;
|
||||
previousTransform: string;
|
||||
pageHeight: number;
|
||||
}
|
||||
|
||||
export interface ScreenshotResult {
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
const captureImage = async (
|
||||
webContentsId: number
|
||||
): Promise<Electron.NativeImage> => {
|
||||
const Image = await webContents.fromId(webContentsId).capturePage();
|
||||
return Image;
|
||||
};
|
||||
|
||||
const quickScreenshot = async (
|
||||
arg: ScreenshotArgs
|
||||
): Promise<ScreenshotResult> => {
|
||||
const { webContentsId } = arg;
|
||||
const image = await webContents.fromId(webContentsId).capturePage();
|
||||
const dir = path.join(os.homedir(), `Desktop/Responsively-Screenshots`);
|
||||
const filePath = path.join(dir, `/screenshot-${Date.now()}.jpeg`);
|
||||
const {
|
||||
webContentsId,
|
||||
device: { name },
|
||||
} = arg;
|
||||
const image = await captureImage(webContentsId);
|
||||
const dir = path.join(homedir(), `Desktop/Responsively-Screenshots`);
|
||||
const filePath = path.join(dir, `/${name}-${Date.now()}.jpeg`);
|
||||
await ensureDir(dir);
|
||||
await writeFile(filePath, image.toJPEG(100));
|
||||
setTimeout(() => shell.showItemInFolder(filePath), 100);
|
||||
|
@ -28,6 +47,19 @@ const quickScreenshot = async (
|
|||
return { done: true };
|
||||
};
|
||||
|
||||
const captureAllDecies = async (
|
||||
args: Array<ScreenshotAllArgs>
|
||||
): Promise<ScreenshotResult> => {
|
||||
const screenShots = args.map((arg) => {
|
||||
const { device, webContentsId } = arg;
|
||||
const screenShotArg: ScreenshotArgs = { device, webContentsId };
|
||||
return quickScreenshot(screenShotArg);
|
||||
});
|
||||
|
||||
await Promise.all(screenShots);
|
||||
return { done: true };
|
||||
};
|
||||
|
||||
export const initScreenshotHandlers = () => {
|
||||
ipcMain.handle(
|
||||
'screenshot',
|
||||
|
@ -35,4 +67,11 @@ export const initScreenshotHandlers = () => {
|
|||
return quickScreenshot(arg);
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
'screenshot:All',
|
||||
async (event, args: Array<ScreenshotAllArgs>) => {
|
||||
return captureAllDecies(args);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
19
desktop-app/src/renderer/components/ModalLoader/index.tsx
Normal file
19
desktop-app/src/renderer/components/ModalLoader/index.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import Modal from '../Modal';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
title: JSX.Element | string;
|
||||
}
|
||||
|
||||
const ModalLoader = ({ isOpen, onClose, title }: Props) => {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} title={title}>
|
||||
<div className="flex flex-col items-center justify-center gap-4">
|
||||
Capturing screen...
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalLoader;
|
|
@ -89,8 +89,7 @@ const Toolbar = ({
|
|||
|
||||
const previousHeight = webviewTag.style.height;
|
||||
const previousTransform = webviewTag.style.transform;
|
||||
webviewTag.style.height = `${pageHeight}px`;
|
||||
webviewTag.style.transform = `scale(0.1)`;
|
||||
updateWebViewHeightAndScale(webviewTag, pageHeight);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
selectIsCapturingScreenshot,
|
||||
selectIsInspecting,
|
||||
selectRotate,
|
||||
setIsCapturingScreenshot,
|
||||
setIsInspecting,
|
||||
setRotate,
|
||||
} from 'renderer/store/features/renderer';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { ScreenshotAllArgs } from 'main/screenshot';
|
||||
import { selectActiveSuite } from 'renderer/store/features/device-manager';
|
||||
import WebPage from 'main/screenshot/webpage';
|
||||
import { getDevicesMap } from 'common/deviceList';
|
||||
import { updateWebViewHeightAndScale } from 'common/webViewUtils';
|
||||
import NavigationControls from './NavigationControls';
|
||||
import Menu from './Menu';
|
||||
import Button from '../Button';
|
||||
import AddressBar from './AddressBar';
|
||||
import ColorSchemeToggle from './ColorSchemeToggle';
|
||||
import ModalLoader from '../ModalLoader';
|
||||
import { PreviewSuiteSelector } from './PreviewSuiteSelector';
|
||||
|
||||
const Divider = () => <div className="h-6 w-px bg-gray-300 dark:bg-gray-700" />;
|
||||
|
@ -18,8 +26,55 @@ const Divider = () => <div className="h-6 w-px bg-gray-300 dark:bg-gray-700" />;
|
|||
const ToolBar = () => {
|
||||
const rotateDevices = useSelector(selectRotate);
|
||||
const isInspecting = useSelector(selectIsInspecting);
|
||||
const isCapturingScreenshot = useSelector(selectIsCapturingScreenshot);
|
||||
const activeSuite = useSelector(selectActiveSuite);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const screenshotCaptureHandler = async () => {
|
||||
dispatch(setIsCapturingScreenshot(true));
|
||||
const webViews: NodeListOf<Electron.WebviewTag> =
|
||||
document.querySelectorAll('webView');
|
||||
const screens: Array<ScreenshotAllArgs> = [];
|
||||
const devices = activeSuite.devices.map((d) => getDevicesMap()[d]);
|
||||
webViews.forEach(async (webview) => {
|
||||
const device = devices.find((d) => d.name === webview.id);
|
||||
const webPage = new WebPage(webview as unknown as Electron.WebContents);
|
||||
const pageHeight = await webPage.getPageHeight();
|
||||
const previousHeight = webview.style.height;
|
||||
const previousTransform = webview.style.transform;
|
||||
updateWebViewHeightAndScale(webview, pageHeight);
|
||||
if (device != null) {
|
||||
screens.push({
|
||||
webContentsId: webview.getWebContentsId(),
|
||||
device,
|
||||
previousHeight,
|
||||
previousTransform,
|
||||
pageHeight,
|
||||
});
|
||||
}
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await window.electron.ipcRenderer.invoke<Array<ScreenshotAllArgs>, any>(
|
||||
'screenshot:All',
|
||||
screens
|
||||
);
|
||||
|
||||
// reset webviews to original size
|
||||
webViews.forEach((webview) => {
|
||||
const screent = screens.find((s) => s.device.name === webview.id);
|
||||
if (screent != null) {
|
||||
webview.style.height = screent.previousHeight;
|
||||
webview.style.transform = screent.previousTransform;
|
||||
}
|
||||
});
|
||||
|
||||
dispatch(setIsCapturingScreenshot(false));
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
// Do nothing. Prevent Dialog from closing.
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<NavigationControls />
|
||||
|
@ -44,10 +99,22 @@ const ToolBar = () => {
|
|||
>
|
||||
<Icon icon="lucide:inspect" />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={screenshotCaptureHandler}
|
||||
isActive={isCapturingScreenshot}
|
||||
title="Screenshot All WebViews"
|
||||
>
|
||||
<Icon icon="lucide:camera" />
|
||||
</Button>
|
||||
<ColorSchemeToggle />
|
||||
<Divider />
|
||||
<PreviewSuiteSelector />
|
||||
<Menu />
|
||||
<ModalLoader
|
||||
isOpen={isCapturingScreenshot}
|
||||
onClose={handleClose}
|
||||
title="Screenshot"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ export interface RendererState {
|
|||
rotate: boolean;
|
||||
isInspecting: boolean | undefined;
|
||||
layout: PreviewLayout;
|
||||
isCapturingScreenshot: boolean;
|
||||
}
|
||||
|
||||
const zoomSteps = [
|
||||
|
@ -30,6 +31,7 @@ const initialState: RendererState = {
|
|||
rotate: false,
|
||||
isInspecting: undefined,
|
||||
layout: window.electron.store.get('ui.previewLayout'),
|
||||
isCapturingScreenshot: false,
|
||||
};
|
||||
|
||||
export const rendererSlice = createSlice({
|
||||
|
@ -67,6 +69,9 @@ export const rendererSlice = createSlice({
|
|||
state.layout = action.payload;
|
||||
window.electron.store.set('ui.previewLayout', action.payload);
|
||||
},
|
||||
setIsCapturingScreenshot: (state, action: PayloadAction<boolean>) => {
|
||||
state.isCapturingScreenshot = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -78,6 +83,7 @@ export const {
|
|||
setRotate,
|
||||
setIsInspecting,
|
||||
setLayout,
|
||||
setIsCapturingScreenshot,
|
||||
} = rendererSlice.actions;
|
||||
|
||||
export const selectZoomFactor = (state: RootState) => state.renderer.zoomFactor;
|
||||
|
@ -86,5 +92,7 @@ export const selectRotate = (state: RootState) => state.renderer.rotate;
|
|||
export const selectIsInspecting = (state: RootState) =>
|
||||
state.renderer.isInspecting;
|
||||
export const selectLayout = (state: RootState) => state.renderer.layout;
|
||||
export const selectIsCapturingScreenshot = (state: RootState) =>
|
||||
state.renderer.isCapturingScreenshot;
|
||||
|
||||
export default rendererSlice.reducer;
|
||||
|
|
Loading…
Reference in a new issue