Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions applications/virtual-fly-brain/frontend/src/components/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ErrorDialog from "./ErrorDialog";
import QueryBuilder from "./queryBuilder";
import MediaQuery from 'react-responsive';
import VFBUploader from "./VFBUploader/VFBUploader";
import VFBSnapshot from "./VFBSnapshot/VFBSnapshot";
import { useDispatch, useSelector, useStore } from 'react-redux';
import { widgets } from "./layout/widgets";
import VFBDownloadContents from "./VFBDownloadContents/VFBDownloadContents";
Expand Down Expand Up @@ -251,20 +252,22 @@ const MainLayout = ({ bottomNav, setBottomNav }) => {
{desktopScreen ? (
<>
{tabContent}
{bottomNav === 0 && < VFBUploader open={true} setBottomNav={setBottomNav} />}
{bottomNav === 1 && <VFBDownloadContents open={true} setBottomNav={setBottomNav} />}
{bottomNav === 2 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={0}/>}
{bottomNav === 5 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={1}/>}
{bottomNav === 0 && < VFBSnapshot open={true} setBottomNav={setBottomNav} />}
{bottomNav === 1 && < VFBUploader open={true} setBottomNav={setBottomNav} />}
{bottomNav === 2 && <VFBDownloadContents open={true} setBottomNav={setBottomNav} />}
{bottomNav === 3 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={0}/>}
Comment thread
jrmartin marked this conversation as resolved.
Comment thread
jrmartin marked this conversation as resolved.
{bottomNav === 6 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={1}/>}
</>
) : (
<>
{
bottomNav != 2 && tabContent
bottomNav != 3 && tabContent
}
{bottomNav === 0 && <VFBUploader open={true} setBottomNav={setBottomNav} />}
{bottomNav === 1 && <VFBDownloadContents open={true} setBottomNav={setBottomNav} />}
{bottomNav === 2 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={0}/>}
{bottomNav === 5 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={1}/>}
{bottomNav === 0 && <VFBSnapshot open={true} setBottomNav={setBottomNav} />}
{bottomNav === 1 && <VFBUploader open={true} setBottomNav={setBottomNav} />}
{bottomNav === 2 && <VFBDownloadContents open={true} setBottomNav={setBottomNav} />}
{bottomNav === 3 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={0}/>}
{bottomNav === 6 && <QueryBuilder setBottomNav={setBottomNav} fullWidth={sidebarOpen} tabSelected={1}/>}
</>
)}
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useEffect } from "react";
import * as htmlToImage from "html-to-image";

const VFBSnapshot = ({ open, setBottomNav }) => {
useEffect(() => {
if (!open) return;

// screenshot flash effect
const flash = document.createElement("div");
flash.style.position = "fixed";
flash.style.left = 0;
flash.style.top = 0;
flash.style.width = "100vw";
flash.style.height = "100vh";
flash.style.background = "white";
flash.style.opacity = "0.7";
flash.style.zIndex = "2147483647";
flash.style.pointerEvents = "none";
flash.style.transition = "opacity 100ms";
document.body.appendChild(flash);

setTimeout(() => {
flash.style.opacity = "0";
setTimeout(() => { document.body.removeChild(flash); }, 120);
}, 100);

// Hide scrollbars and remove container max-height/overflow for all scrollable area
const selectors = [
".MuiBox-root",
".MuiPaper-root",
".scrollable",
".vfb-scrollable",
".subheader-content",
".VFBMainPanel",
];
const containers = Array.from(document.querySelectorAll(selectors.join(',')));

// Save previous styles so we can restore exactly
const previousStyles = containers.map(el => ({
el,
overflow: el.style.overflow,
overflowX: el.style.overflowX,
overflowY: el.style.overflowY,
maxHeight: el.style.maxHeight,
maxWidth: el.style.maxWidth,
}));

// Apply snapshot styles (no overflow, no artificial maxHeight/Width)
containers.forEach(el => {
el.style.overflow = "visible";
el.style.overflowX = "visible";
el.style.overflowY = "visible";
el.style.maxHeight = "unset";
el.style.maxWidth = "unset";
});

(async () => {
try {
const el = document.getElementById('root') || document.body;
const dataUrl = await htmlToImage.toPng(el, { cacheBust: true });
const link = document.createElement("a");
link.download = "vfb-snapshot.png";
link.href = dataUrl;
link.click();
} catch (err) {
console.error("[VFBSnapshot] Error capturing snapshot:", err);
}

// Restore overflow/maxHeight styles
previousStyles.forEach(({ el, overflow, overflowX, overflowY, maxHeight, maxWidth }) => {
el.style.overflow = overflow;
el.style.overflowX = overflowX;
el.style.overflowY = overflowY;
el.style.maxHeight = maxHeight;
el.style.maxWidth = maxWidth;
});

setBottomNav(undefined);
})();

return () => {
previousStyles.forEach(({ el, overflow, overflowX, overflowY, maxHeight, maxWidth }) => {
el.style.overflow = overflow;
el.style.overflowX = overflowX;
el.style.overflowY = overflowY;
el.style.maxHeight = maxHeight;
el.style.maxWidth = maxWidth;
});
};

}, [open, setBottomNav]);

return null;
};

export default VFBSnapshot;
18 changes: 18 additions & 0 deletions applications/virtual-fly-brain/frontend/src/icons/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ export const Search = (props) => {
)
};

export const Screenshot = (props) => {
return (
<svg {...props} width={props.size || 21} height={props.size || 20} viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_screenshot)">
<rect x="3.5" y="5" width="14" height="10" rx="2" stroke={props.color || "white"} strokeWidth="1.5" fill="none"/>
<circle cx="10.5" cy="10" r="2.5" stroke={props.color || "white"} strokeWidth="1.5" fill="none"/>
<rect x="7" y="4" width="1.5" height="2" rx=".5" fill={props.color || "white"} opacity="0.7"/>
<rect x="12.5" y="4" width="2" height="1.2" rx=".6" fill={props.color || "white"} opacity="0.7"/>
</g>
<defs>
<clipPath id="clip0_screenshot">
<rect width={props.size || 20} height={props.size || 20} fill={props.color || "white"} transform="translate(0.5)" />
</clipPath>
</defs>
</svg>
)
};

export const Upload = (props) => {
return (
<svg {...props} width={props.size || 21} height={props.size || 20} viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import {
Layers,
Query,
Search,
Upload,
Upload
} from "../../icons";
import Screenshot from '@mui/icons-material/CameraAlt';
import vars from "../../theme/variables";
import MediaQuery from "react-responsive";
import { useDispatch, useSelector } from "react-redux";
Expand All @@ -25,31 +26,36 @@ import { resetLoadingState } from "../../reducers/actions/instances";
const navArr = [
{
id: 0,
icon: Screenshot,
name: "Screenshot",
},
{
id: 1,
icon: Upload,
name: "Upload",
},
{
id: 1,
id: 2,
icon: Download,
name: "Download",
},
{
id: 2,
id: 3,
icon: Query,
name: "Query",
},
{
id: 3,
id: 4,
icon: Layers,
name: "Layer",
},
{
id: 4,
id: 5,
icon: ClearAll,
name: "Clear all",
Comment thread
jrmartin marked this conversation as resolved.
},
{
id: 5,
id: 6,
icon: History,
name: "Recent",
},
Expand Down
Loading