From 2567f5faa5358786154e82dad9db55d093e248f0 Mon Sep 17 00:00:00 2001
From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com>
Date: Sat, 8 Oct 2022 13:15:30 +1300
Subject: [PATCH] Add Image Gallery Drawer

---
 frontend/src/app/App.scss                     |   4 +
 frontend/src/app/App.tsx                      |   4 +-
 .../features/gallery/CurrentImageDisplay.scss |  32 ++--
 .../src/features/gallery/ImageGallery.scss    |  93 ++++++++---
 .../src/features/gallery/ImageGallery.tsx     | 144 +++++++++++-------
 .../src/features/gallery/ImageGalleryOld.tsx  | 129 ++++++++++++++++
 .../ImageMetadataViewer.scss                  |   4 +-
 frontend/src/features/gallery/gallerySlice.ts |   6 +
 frontend/src/features/system/Console.tsx      |   9 ++
 .../system/HotkeysModal/HotkeysModal.tsx      |  10 ++
 .../tabs/ImageToImage/ImageToImage.scss       |  20 ++-
 .../tabs/ImageToImage/ImageToImage.tsx        |  11 +-
 .../tabs/TextToImage/TextToImage.scss         |  19 ++-
 .../features/tabs/TextToImage/TextToImage.tsx |   8 +-
 frontend/src/styles/Mixins/_Variables.scss    |   3 +
 frontend/src/styles/_Animations.scss          |   8 +
 frontend/src/styles/_Colors_Dark.scss         |   5 +-
 frontend/src/styles/_Colors_Light.scss        |   5 +-
 frontend/src/styles/index.scss                |   1 +
 19 files changed, 406 insertions(+), 109 deletions(-)
 create mode 100644 frontend/src/features/gallery/ImageGalleryOld.tsx
 create mode 100644 frontend/src/styles/_Animations.scss

diff --git a/frontend/src/app/App.scss b/frontend/src/app/App.scss
index f9b1c9f54d..67aee81cdb 100644
--- a/frontend/src/app/App.scss
+++ b/frontend/src/app/App.scss
@@ -15,3 +15,7 @@
   width: $app-width;
   height: $app-height;
 }
+
+.app-console {
+  z-index: 9999;
+}
diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx
index 18975e2ca9..f91bc24c51 100644
--- a/frontend/src/app/App.tsx
+++ b/frontend/src/app/App.tsx
@@ -26,7 +26,9 @@ const App = () => {
         <SiteHeader />
         <InvokeTabs />
       </div>
-      <Console />
+      <div className="app-console">
+        <Console />
+      </div>
     </div>
   ) : (
     <Loading />
diff --git a/frontend/src/features/gallery/CurrentImageDisplay.scss b/frontend/src/features/gallery/CurrentImageDisplay.scss
index 35739fa9e0..5f1e8c9df5 100644
--- a/frontend/src/features/gallery/CurrentImageDisplay.scss
+++ b/frontend/src/features/gallery/CurrentImageDisplay.scss
@@ -11,21 +11,6 @@
   border-radius: 0.5rem;
 }
 
-.current-image-display-placeholder {
-  background-color: var(--background-color-secondary);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  height: 100%;
-
-  svg {
-    width: 10rem;
-    height: 10rem;
-    color: var(--svg-color);
-  }
-}
-
 .current-image-tools {
   width: 100%;
   height: 100%;
@@ -106,3 +91,20 @@
   filter: drop-shadow(0 0 1rem var(--text-color-secondary));
   opacity: 70%;
 }
+
+.current-image-display-placeholder {
+  background-color: var(--background-color-secondary);
+  display: grid;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  border-radius: 0.5rem;
+
+  svg {
+    width: 10rem;
+    height: 10rem;
+    color: var(--svg-color);
+  }
+}
diff --git a/frontend/src/features/gallery/ImageGallery.scss b/frontend/src/features/gallery/ImageGallery.scss
index 135caf3e31..984b2a6e4c 100644
--- a/frontend/src/features/gallery/ImageGallery.scss
+++ b/frontend/src/features/gallery/ImageGallery.scss
@@ -1,43 +1,67 @@
 @use '../../styles/Mixins/' as *;
 
+.image-gallery-area {
+  .image-gallery-popup-btn {
+    @include Button(
+      $btn-width: 3rem,
+      $btn-height: 3rem,
+      $icon-size: 22px,
+      $btn-color: var(--btn-grey),
+      $btn-color-hover: var(--btn-grey-hover)
+    );
+  }
+}
+
+.image-gallery-popup {
+  background-color: var(--tab-color);
+  position: fixed !important;
+  top: 0;
+  right: 0;
+  padding: 1rem;
+  animation: slideOut 0.3s ease-out;
+  display: grid;
+  grid-auto-rows: max-content;
+  row-gap: 1rem;
+  border-left-width: 0.2rem;
+  border-color: var(--gallery-resizeable-color);
+}
+
+.image-gallery-header {
+  display: grid;
+  grid-template-columns: auto max-content;
+  align-items: center;
+
+  h1 {
+    font-weight: bold;
+  }
+}
+
+.image-gallery-close-btn {
+  background-color: var(--btn-load-more) !important;
+  &:hover {
+    background-color: var(--btn-load-more-hover) !important;
+  }
+}
+
 .image-gallery-container {
   display: grid;
-  row-gap: 1rem;
-  grid-auto-rows: max-content;
-  min-width: 16rem;
-}
-
-.image-gallery-container-placeholder {
-  display: grid;
-  background-color: var(--background-color-secondary);
-  border-radius: 0.5rem;
-  place-items: center;
-  padding: 2rem 0;
-
-  p {
-    color: var(--subtext-color-bright);
-  }
-
-  svg {
-    width: 5rem;
-    height: 5rem;
-    color: var(--svg-color);
-  }
+  gap: 1rem;
+  max-height: $app-gallery-popover-height;
+  overflow-y: scroll;
+  @include HideScrollbar;
 }
 
 .image-gallery {
   display: grid;
-  grid-template-columns: repeat(2, max-content);
+  grid-template-columns: repeat(auto-fill, minmax(120px, auto));
   gap: 0.6rem;
   justify-items: center;
-  max-height: $app-gallery-height;
-  overflow-y: scroll;
-  @include HideScrollbar;
 }
 
 .image-gallery-load-more-btn {
   background-color: var(--btn-load-more) !important;
   font-size: 0.85rem !important;
+  font-family: Inter;
 
   &:disabled {
     &:hover {
@@ -49,3 +73,22 @@
     background-color: var(--btn-load-more-hover) !important;
   }
 }
+
+.image-gallery-container-placeholder {
+  display: grid;
+  background-color: var(--background-color-secondary);
+  border-radius: 0.5rem;
+  place-items: center;
+  padding: 2rem 0;
+
+  p {
+    color: var(--subtext-color-bright);
+    font-family: Inter;
+  }
+
+  svg {
+    width: 5rem;
+    height: 5rem;
+    color: var(--svg-color);
+  }
+}
diff --git a/frontend/src/features/gallery/ImageGallery.tsx b/frontend/src/features/gallery/ImageGallery.tsx
index 70ccdf54a9..2bb13318b3 100644
--- a/frontend/src/features/gallery/ImageGallery.tsx
+++ b/frontend/src/features/gallery/ImageGallery.tsx
@@ -1,32 +1,47 @@
-import { Button } from '@chakra-ui/react';
+import { Button, IconButton } from '@chakra-ui/button';
+import { Resizable } from 're-resizable';
+import React from 'react';
 import { useHotkeys } from 'react-hotkeys-hook';
-import { MdPhotoLibrary } from 'react-icons/md';
+import { MdClear, MdPhotoLibrary } from 'react-icons/md';
 import { requestImages } from '../../app/socketio/actions';
-import { RootState, useAppDispatch } from '../../app/store';
-import { useAppSelector } from '../../app/store';
-import { selectNextImage, selectPrevImage } from './gallerySlice';
+import { RootState, useAppDispatch, useAppSelector } from '../../app/store';
+import {
+  selectNextImage,
+  selectPrevImage,
+  setShouldShowGallery,
+} from './gallerySlice';
 import HoverableImage from './HoverableImage';
 
-/**
- * Simple image gallery.
- */
-const ImageGallery = () => {
-  const { images, currentImageUuid, areMoreImagesAvailable } = useAppSelector(
-    (state: RootState) => state.gallery
-  );
+export default function ImageGallery() {
+  const {
+    images,
+    currentImageUuid,
+    areMoreImagesAvailable,
+    shouldShowGallery,
+  } = useAppSelector((state: RootState) => state.gallery);
+
   const dispatch = useAppDispatch();
-  /**
-   * I don't like that this needs to rerender whenever the current image is changed.
-   * What if we have a large number of images? I suppose pagination (planned) will
-   * mitigate this issue.
-   *
-   * TODO: Refactor if performance complaints, or after migrating to new API which supports pagination.
-   */
+
+  const handleShowGalleryToggle = () => {
+    dispatch(setShouldShowGallery(!shouldShowGallery));
+  };
+
+  const handleGalleryClose = () => {
+    dispatch(setShouldShowGallery(false));
+  };
 
   const handleClickLoadMore = () => {
     dispatch(requestImages());
   };
 
+  useHotkeys(
+    'g',
+    () => {
+      handleShowGalleryToggle();
+    },
+    [shouldShowGallery]
+  );
+
   useHotkeys(
     'left',
     () => {
@@ -44,41 +59,64 @@ const ImageGallery = () => {
   );
 
   return (
-    <div className="image-gallery-container">
-      {images.length ? (
-        <>
-          <p>
-            <strong>Your Invocations</strong>
-          </p>
-          <div className="image-gallery">
-            {images.map((image) => {
-              const { uuid } = image;
-              const isSelected = currentImageUuid === uuid;
-              return (
-                <HoverableImage
-                  key={uuid}
-                  image={image}
-                  isSelected={isSelected}
-                />
-              );
-            })}
-          </div>
-        </>
-      ) : (
-        <div className="image-gallery-container-placeholder">
+    <div className="image-gallery-area">
+      {!shouldShowGallery && (
+        <Button
+          colorScheme="teal"
+          onClick={handleShowGalleryToggle}
+          className="image-gallery-popup-btn"
+        >
           <MdPhotoLibrary />
-          <p>No Images In Gallery</p>
-        </div>
+        </Button>
+      )}
+
+      {shouldShowGallery && (
+        <Resizable
+          defaultSize={{ width: 'auto', height: '100%' }}
+          minWidth={'18%'}
+          className="image-gallery-popup"
+        >
+          <div className="image-gallery-header">
+            <h1>Your Invocations</h1>
+            <IconButton
+              size={'sm'}
+              aria-label={'Close Gallery'}
+              onClick={handleGalleryClose}
+              className="image-gallery-close-btn"
+              icon={<MdClear />}
+            />
+          </div>
+          <div className="image-gallery-container">
+            {images.length ? (
+              <div className="image-gallery">
+                {images.map((image) => {
+                  const { uuid } = image;
+                  const isSelected = currentImageUuid === uuid;
+                  return (
+                    <HoverableImage
+                      key={uuid}
+                      image={image}
+                      isSelected={isSelected}
+                    />
+                  );
+                })}
+              </div>
+            ) : (
+              <div className="image-gallery-container-placeholder">
+                <MdPhotoLibrary />
+                <p>No Images In Gallery</p>
+              </div>
+            )}
+            <Button
+              onClick={handleClickLoadMore}
+              isDisabled={!areMoreImagesAvailable}
+              className="image-gallery-load-more-btn"
+            >
+              {areMoreImagesAvailable ? 'Load More' : 'All Images Loaded'}
+            </Button>
+          </div>
+        </Resizable>
       )}
-      <Button
-        onClick={handleClickLoadMore}
-        isDisabled={!areMoreImagesAvailable}
-        className="image-gallery-load-more-btn"
-      >
-        {areMoreImagesAvailable ? 'Load More' : 'All Images Loaded'}
-      </Button>
     </div>
   );
-};
-
-export default ImageGallery;
+}
diff --git a/frontend/src/features/gallery/ImageGalleryOld.tsx b/frontend/src/features/gallery/ImageGalleryOld.tsx
new file mode 100644
index 0000000000..e1ff8e03bb
--- /dev/null
+++ b/frontend/src/features/gallery/ImageGalleryOld.tsx
@@ -0,0 +1,129 @@
+import {
+  Button,
+  Drawer,
+  DrawerBody,
+  DrawerCloseButton,
+  DrawerContent,
+  DrawerHeader,
+  useDisclosure,
+} from '@chakra-ui/react';
+import React from 'react';
+import { useHotkeys } from 'react-hotkeys-hook';
+import { MdPhotoLibrary } from 'react-icons/md';
+import { requestImages } from '../../app/socketio/actions';
+import { RootState, useAppDispatch } from '../../app/store';
+import { useAppSelector } from '../../app/store';
+import { selectNextImage, selectPrevImage } from './gallerySlice';
+import HoverableImage from './HoverableImage';
+
+/**
+ * Simple image gallery.
+ */
+const ImageGalleryOld = () => {
+  const { images, currentImageUuid, areMoreImagesAvailable } = useAppSelector(
+    (state: RootState) => state.gallery
+  );
+  const dispatch = useAppDispatch();
+
+  const { isOpen, onOpen, onClose } = useDisclosure();
+
+  /**
+   * I don't like that this needs to rerender whenever the current image is changed.
+   * What if we have a large number of images? I suppose pagination (planned) will
+   * mitigate this issue.
+   *
+   * TODO: Refactor if performance complaints, or after migrating to new API which supports pagination.
+   */
+
+  const handleClickLoadMore = () => {
+    dispatch(requestImages());
+  };
+
+  useHotkeys(
+    'g',
+    () => {
+      if (isOpen) {
+        onClose();
+      } else {
+        onOpen();
+      }
+    },
+    [isOpen]
+  );
+
+  useHotkeys(
+    'left',
+    () => {
+      dispatch(selectPrevImage());
+    },
+    []
+  );
+
+  useHotkeys(
+    'right',
+    () => {
+      dispatch(selectNextImage());
+    },
+    []
+  );
+
+  return (
+    <div className="image-gallery-area">
+      <Button
+        colorScheme="teal"
+        onClick={onOpen}
+        className="image-gallery-popup-btn"
+      >
+        <MdPhotoLibrary />
+      </Button>
+      <Drawer
+        isOpen={isOpen}
+        placement="right"
+        onClose={onClose}
+        autoFocus={false}
+        trapFocus={false}
+        closeOnOverlayClick={false}
+      >
+        <DrawerContent className="image-gallery-popup">
+          <div className="image-gallery-header">
+            <DrawerHeader>Your Invocations</DrawerHeader>
+            <DrawerCloseButton />
+          </div>
+          <DrawerBody className="image-gallery-body">
+            <div className="image-gallery-container">
+              {images.length ? (
+                <div className="image-gallery">
+                  {images.map((image) => {
+                    const { uuid } = image;
+                    const isSelected = currentImageUuid === uuid;
+                    return (
+                      <HoverableImage
+                        key={uuid}
+                        image={image}
+                        isSelected={isSelected}
+                      />
+                    );
+                  })}
+                </div>
+              ) : (
+                <div className="image-gallery-container-placeholder">
+                  <MdPhotoLibrary />
+                  <p>No Images In Gallery</p>
+                </div>
+              )}
+              <Button
+                onClick={handleClickLoadMore}
+                isDisabled={!areMoreImagesAvailable}
+                className="image-gallery-load-more-btn"
+              >
+                {areMoreImagesAvailable ? 'Load More' : 'All Images Loaded'}
+              </Button>
+            </div>
+          </DrawerBody>
+        </DrawerContent>
+      </Drawer>
+    </div>
+  );
+};
+
+export default ImageGallery;
diff --git a/frontend/src/features/gallery/ImageMetaDataViewer/ImageMetadataViewer.scss b/frontend/src/features/gallery/ImageMetaDataViewer/ImageMetadataViewer.scss
index 4eb3dc5fce..e5c33672ae 100644
--- a/frontend/src/features/gallery/ImageMetaDataViewer/ImageMetadataViewer.scss
+++ b/frontend/src/features/gallery/ImageMetaDataViewer/ImageMetadataViewer.scss
@@ -6,8 +6,8 @@
   padding: 1rem;
   background-color: var(--metadata-bg-color);
   overflow: scroll;
-  max-height: calc($app-content-height - 4rem);
-  z-index: 1;
+  max-height: $app-metadata-height;
+  z-index: 10;
 }
 
 .image-json-viewer {
diff --git a/frontend/src/features/gallery/gallerySlice.ts b/frontend/src/features/gallery/gallerySlice.ts
index 415b80d326..02ec67bb20 100644
--- a/frontend/src/features/gallery/gallerySlice.ts
+++ b/frontend/src/features/gallery/gallerySlice.ts
@@ -11,12 +11,14 @@ export interface GalleryState {
   areMoreImagesAvailable: boolean;
   latest_mtime?: number;
   earliest_mtime?: number;
+  shouldShowGallery: boolean;
 }
 
 const initialState: GalleryState = {
   currentImageUuid: '',
   images: [],
   areMoreImagesAvailable: true,
+  shouldShowGallery: true,
 };
 
 export const gallerySlice = createSlice({
@@ -138,6 +140,9 @@ export const gallerySlice = createSlice({
         state.areMoreImagesAvailable = areMoreImagesAvailable;
       }
     },
+    setShouldShowGallery: (state, action: PayloadAction<boolean>) => {
+      state.shouldShowGallery = action.payload;
+    },
   },
 });
 
@@ -150,6 +155,7 @@ export const {
   setIntermediateImage,
   selectNextImage,
   selectPrevImage,
+  setShouldShowGallery,
 } = gallerySlice.actions;
 
 export default gallerySlice.reducer;
diff --git a/frontend/src/features/system/Console.tsx b/frontend/src/features/system/Console.tsx
index 799488f486..22a6632936 100644
--- a/frontend/src/features/system/Console.tsx
+++ b/frontend/src/features/system/Console.tsx
@@ -7,6 +7,7 @@ import { FaAngleDoubleDown, FaCode, FaMinus } from 'react-icons/fa';
 import { createSelector } from '@reduxjs/toolkit';
 import { isEqual } from 'lodash';
 import { Resizable } from 're-resizable';
+import { useHotkeys } from 'react-hotkeys-hook';
 
 const logSelector = createSelector(
   (state: RootState) => state.system,
@@ -66,6 +67,14 @@ const Console = () => {
     dispatch(setShouldShowLogViewer(!shouldShowLogViewer));
   };
 
+  useHotkeys(
+    '`',
+    () => {
+      dispatch(setShouldShowLogViewer(!shouldShowLogViewer));
+    },
+    [shouldShowLogViewer]
+  );
+
   return (
     <>
       {shouldShowLogViewer && (
diff --git a/frontend/src/features/system/HotkeysModal/HotkeysModal.tsx b/frontend/src/features/system/HotkeysModal/HotkeysModal.tsx
index 16fb7d6bf6..12004640ef 100644
--- a/frontend/src/features/system/HotkeysModal/HotkeysModal.tsx
+++ b/frontend/src/features/system/HotkeysModal/HotkeysModal.tsx
@@ -23,6 +23,11 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
   const hotkeys = [
     { title: 'Invoke', desc: 'Generate an image', hotkey: 'Ctrl+Enter' },
     { title: 'Cancel', desc: 'Cancel image generation', hotkey: 'Shift+X' },
+    {
+      title: 'Toggle Gallery',
+      desc: 'Open and close the gallery drawer',
+      hotkey: 'G',
+    },
     {
       title: 'Set Seed',
       desc: 'Use the seed of the current image',
@@ -71,6 +76,11 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
       desc: 'Switch between dark and light modes',
       hotkey: 'Shift+D',
     },
+    {
+      title: 'Console Toggle',
+      desc: 'Open and close console',
+      hotkey: '`',
+    },
   ];
 
   const renderHotkeyModalItems = () => {
diff --git a/frontend/src/features/tabs/ImageToImage/ImageToImage.scss b/frontend/src/features/tabs/ImageToImage/ImageToImage.scss
index d1c444d748..18fd97aeb2 100644
--- a/frontend/src/features/tabs/ImageToImage/ImageToImage.scss
+++ b/frontend/src/features/tabs/ImageToImage/ImageToImage.scss
@@ -2,7 +2,7 @@
 
 .image-to-image-workarea {
   display: grid;
-  grid-template-columns: max-content auto max-content;
+  grid-template-columns: max-content auto;
   column-gap: 1rem;
 }
 
@@ -16,6 +16,22 @@
   @include HideScrollbar;
 }
 
+.image-to-image-display-area {
+  display: grid;
+  grid-template-areas: 'image-to-image-display-area';
+
+  .image-to-image-display {
+    grid-area: image-to-image-display-area;
+  }
+
+  .image-gallery-area {
+    grid-area: image-to-image-display-area;
+    z-index: 2;
+    place-self: end;
+    margin: 1rem;
+  }
+}
+
 .image-to-image-strength-main-option {
   display: grid;
   grid-template-columns: none !important;
@@ -52,7 +68,7 @@
 .image-to-image-dual-preview {
   grid-area: img2img-preview;
   display: grid;
-  grid-template-columns: 1fr 1fr;
+  grid-template-columns: max-content max-content;
   column-gap: 0.5rem;
   padding: 0 1rem;
   place-content: center;
diff --git a/frontend/src/features/tabs/ImageToImage/ImageToImage.tsx b/frontend/src/features/tabs/ImageToImage/ImageToImage.tsx
index 03a4d35d9a..585dc8f323 100644
--- a/frontend/src/features/tabs/ImageToImage/ImageToImage.tsx
+++ b/frontend/src/features/tabs/ImageToImage/ImageToImage.tsx
@@ -1,15 +1,16 @@
 import React from 'react';
-import ImageGallery from '../../gallery/ImageGallery';
-import ImageToImageDisplay from './ImageToImageDisplay';
-
 import ImageToImagePanel from './ImageToImagePanel';
+import ImageToImageDisplay from './ImageToImageDisplay';
+import ImageGallery from '../../gallery/ImageGallery';
 
 export default function ImageToImage() {
   return (
     <div className="image-to-image-workarea">
       <ImageToImagePanel />
-      <ImageToImageDisplay />
-      <ImageGallery />
+      <div className="image-to-image-display-area">
+        <ImageToImageDisplay />
+        <ImageGallery />
+      </div>
     </div>
   );
 }
diff --git a/frontend/src/features/tabs/TextToImage/TextToImage.scss b/frontend/src/features/tabs/TextToImage/TextToImage.scss
index af67928b5b..843209d71b 100644
--- a/frontend/src/features/tabs/TextToImage/TextToImage.scss
+++ b/frontend/src/features/tabs/TextToImage/TextToImage.scss
@@ -2,7 +2,7 @@
 
 .text-to-image-workarea {
   display: grid;
-  grid-template-columns: max-content auto max-content;
+  grid-template-columns: max-content auto;
   column-gap: 1rem;
 }
 
@@ -14,3 +14,20 @@
   overflow-y: scroll;
   @include HideScrollbar;
 }
+
+.text-to-image-display {
+  display: grid;
+  grid-template-areas: 'text-to-image-display';
+
+  .current-image-display,
+  .current-image-display-placeholder {
+    grid-area: text-to-image-display;
+  }
+
+  .image-gallery-area {
+    grid-area: text-to-image-display;
+    z-index: 2;
+    place-self: end;
+    margin: 1rem;
+  }
+}
diff --git a/frontend/src/features/tabs/TextToImage/TextToImage.tsx b/frontend/src/features/tabs/TextToImage/TextToImage.tsx
index 476bed6e63..9b2b8f1988 100644
--- a/frontend/src/features/tabs/TextToImage/TextToImage.tsx
+++ b/frontend/src/features/tabs/TextToImage/TextToImage.tsx
@@ -1,14 +1,16 @@
 import React from 'react';
+import TextToImagePanel from './TextToImagePanel';
 import CurrentImageDisplay from '../../gallery/CurrentImageDisplay';
 import ImageGallery from '../../gallery/ImageGallery';
-import TextToImagePanel from './TextToImagePanel';
 
 export default function TextToImage() {
   return (
     <div className="text-to-image-workarea">
       <TextToImagePanel />
-      <CurrentImageDisplay />
-      <ImageGallery />
+      <div className="text-to-image-display">
+        <CurrentImageDisplay />
+        <ImageGallery />
+      </div>
     </div>
   );
 }
diff --git a/frontend/src/styles/Mixins/_Variables.scss b/frontend/src/styles/Mixins/_Variables.scss
index cf7691669a..1f8b013e27 100644
--- a/frontend/src/styles/Mixins/_Variables.scss
+++ b/frontend/src/styles/Mixins/_Variables.scss
@@ -8,6 +8,9 @@ $app-width: calc(100vw - $app-cutoff);
 $app-height: calc(100vh - $app-cutoff);
 $app-content-height: calc(100vh - $app-content-height-cutoff);
 $app-gallery-height: calc(100vh - ($app-content-height-cutoff + 6rem));
+$app-gallery-popover-height: calc(
+  100vh - ($app-content-height-cutoff - 2.5rem)
+);
 $app-metadata-height: calc(100vh - ($app-content-height-cutoff + 4.4rem));
 
 // option bar
diff --git a/frontend/src/styles/_Animations.scss b/frontend/src/styles/_Animations.scss
new file mode 100644
index 0000000000..0794183a05
--- /dev/null
+++ b/frontend/src/styles/_Animations.scss
@@ -0,0 +1,8 @@
+@keyframes slideOut {
+  from {
+    transform: translateX(10rem);
+  }
+  to {
+    transform: translateX(0);
+  }
+}
diff --git a/frontend/src/styles/_Colors_Dark.scss b/frontend/src/styles/_Colors_Dark.scss
index 83e5f41678..b87fbb3fdf 100644
--- a/frontend/src/styles/_Colors_Dark.scss
+++ b/frontend/src/styles/_Colors_Dark.scss
@@ -46,7 +46,7 @@
   --btn-red-hover: rgb(255, 75, 75);
 
   --btn-load-more: rgb(30, 32, 42);
-  --btn-load-more-hover: rgb(36, 38, 48);
+  --btn-load-more-hover: rgb(54, 56, 66);
 
   // Switch
   --switch-bg-color: rgb(100, 102, 110);
@@ -92,4 +92,7 @@
 
   // Img2Img
   --img2img-img-bg-color: rgb(30, 32, 42);
+
+  // Gallery
+  --gallery-resizeable-color: rgb(36, 38, 48);
 }
diff --git a/frontend/src/styles/_Colors_Light.scss b/frontend/src/styles/_Colors_Light.scss
index 4d394cf601..370fabaa93 100644
--- a/frontend/src/styles/_Colors_Light.scss
+++ b/frontend/src/styles/_Colors_Light.scss
@@ -46,7 +46,7 @@
   --btn-red-hover: rgb(255, 55, 55);
 
   --btn-load-more: rgb(202, 204, 206);
-  --btn-load-more-hover: rgb(206, 208, 210);
+  --btn-load-more-hover: rgb(178, 180, 182);
 
   // Switch
   --switch-bg-color: rgb(178, 180, 182);
@@ -91,4 +91,7 @@
 
   // Img2Img
   --img2img-img-bg-color: rgb(180, 182, 184);
+
+  // Gallery
+  --gallery-resizeable-color: rgb(192, 194, 196);
 }
diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss
index b18cc3101a..b16b8d60f4 100644
--- a/frontend/src/styles/index.scss
+++ b/frontend/src/styles/index.scss
@@ -2,6 +2,7 @@
 @use 'Colors_Dark';
 @use 'Colors_Light';
 @use 'Fonts';
+@use 'Animations';
 
 // Component Styles
 //app