fix: include logo in screenshot, fix screenshots on small/zoomed windows (#579)

* fix: include logo in screenshot

* fix: screenshots on small/zoomed windows, screenshots with no async/other

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
This commit is contained in:
Samuel Gunter
2025-04-05 11:21:37 -05:00
committed by GitHub
parent 70d4fecad6
commit 76b6aa7c15
3 changed files with 27 additions and 10 deletions

View File

@@ -33,6 +33,8 @@ import CalendarFooter from './CalendarFooter';
*/
export default function Calendar(): ReactNode {
const { courseCells, activeSchedule } = useFlattenedCourseSchedule();
const asyncCourseCells = courseCells.filter(block => block.async);
const displayBottomBar = asyncCourseCells && asyncCourseCells.length > 0;
const [course, setCourse] = useState<Course | null>(useCourseFromUrl());
@@ -85,7 +87,7 @@ export default function Calendar(): ReactNode {
return (
<CalendarContext.Provider value>
<div className='h-full w-full flex flex-col'>
<div className='h-screen flex overflow-auto'>
<div className='h-screen flex overflow-auto screenshot:calendar-target'>
<div
className={clsx(
'py-spacing-6 relative h-full min-h-screen w-full flex flex-none flex-col justify-between overflow-clip whitespace-nowrap border-r border-ut-offwhite/50 shadow-[2px_0_10px,rgba(214_210_196_/_.1)] motion-safe:duration-300 motion-safe:ease-out-expo motion-safe:transition-[max-width] screenshot:hidden',
@@ -169,7 +171,11 @@ export default function Calendar(): ReactNode {
setShowSidebar(!showSidebar);
}}
/>
<div className='min-h-2xl min-w-5xl flex-grow gap-0 pl-spacing-3 screenshot:min-h-xl'>
<div
className={clsx('min-h-2xl min-w-5xl flex-grow gap-0 pl-spacing-3 screenshot:min-h-xl', {
'screenshot:flex-grow-0': displayBottomBar, // html-to-image seems to have a bug with flex-grow
})}
>
<CalendarGrid courseCells={courseCells} setCourse={setCourse} />
</div>
<CalendarBottomBar courseCells={courseCells} setCourse={setCourse} />

View File

@@ -5,6 +5,7 @@ import { Button } from '@views/components/common/Button';
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import Divider from '@views/components/common/Divider';
import { ExtensionRootWrapper, styleResetClass } from '@views/components/common/ExtensionRoot/ExtensionRoot';
import { LargeLogo } from '@views/components/common/LogoIcon';
import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses';
import useSchedules from '@views/hooks/useSchedules';
import clsx from 'clsx';
@@ -39,6 +40,9 @@ export default function CalendarHeader({ sidebarOpen, onSidebarToggle }: Calenda
/>
)}
<LargeLogo className='hidden! screenshot:flex!' />
<Divider className='self-center hidden! screenshot:block!' size='2.5rem' orientation='vertical' />
<div className='min-w-[11.5rem] screenshot:transform-origin-left screenshot:scale-120'>
<ScheduleTotalHoursAndCourses
scheduleName={activeSchedule.name}

View File

@@ -266,31 +266,38 @@ export const saveAsCal = async () => {
* @param calendarRef - The reference to the calendar component.
*/
export const saveCalAsPng = () => {
const WIDTH_PX = 1165;
const HEIGHT_PX = 754;
const SCALE = 2;
const rootNode = document.createElement('div');
rootNode.style.backgroundColor = 'white';
rootNode.style.position = 'fixed';
rootNode.style.zIndex = '1000';
rootNode.style.top = '-10000px';
rootNode.style.left = '-10000px';
rootNode.style.width = '1165px';
rootNode.style.height = '754px';
rootNode.style.width = `${WIDTH_PX}px`;
rootNode.style.height = `${HEIGHT_PX}px`;
document.body.appendChild(rootNode);
const clonedNode = document.querySelector('#root')!.cloneNode(true) as HTMLDivElement;
clonedNode.style.backgroundColor = 'white';
(clonedNode.firstChild as HTMLDivElement).classList.add('screenshot-in-progress');
return new Promise<void>((resolve, reject) => {
requestAnimationFrame(async () => {
rootNode.appendChild(clonedNode);
const calendarTarget = clonedNode.querySelector('.screenshot\\:calendar-target') as HTMLDivElement;
calendarTarget.style.width = `${WIDTH_PX}px`;
calendarTarget.style.height = `${HEIGHT_PX}px`;
return new Promise<void>((resolve, reject) => {
rootNode.appendChild(clonedNode);
requestAnimationFrame(async () => {
try {
const screenshotBlob = await toBlob(clonedNode, {
cacheBust: true,
canvasWidth: 1165 * 2,
canvasHeight: 754 * 2,
canvasWidth: WIDTH_PX * SCALE,
canvasHeight: HEIGHT_PX * SCALE,
skipAutoScale: true,
pixelRatio: 2,
pixelRatio: SCALE,
});
if (!screenshotBlob) {