diff --git a/app/javascript/mastodon/blurhash.ts b/app/javascript/mastodon/blurhash.ts
index cafe7b12d..a1d1a0f4e 100644
--- a/app/javascript/mastodon/blurhash.ts
+++ b/app/javascript/mastodon/blurhash.ts
@@ -96,13 +96,19 @@ export const decode83 = (str: string) => {
   return value;
 };
 
-export const intToRGB = (int: number) => ({
+export interface RGB {
+  r: number;
+  g: number;
+  b: number;
+}
+
+export const intToRGB = (int: number): RGB => ({
   r: Math.max(0, int >> 16),
   g: Math.max(0, (int >> 8) & 255),
   b: Math.max(0, int & 255),
 });
 
-export const getAverageFromBlurhash = (blurhash: string) => {
+export const getAverageFromBlurhash = (blurhash: string | null) => {
   if (!blurhash) {
     return null;
   }
diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
deleted file mode 100644
index 50f91a927..000000000
--- a/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import PropTypes from 'prop-types';
-import { PureComponent } from 'react';
-
-import { FormattedMessage } from 'react-intl';
-
-import { connect } from 'react-redux';
-
-import CancelPresentationIcon from '@/material-icons/400-24px/cancel_presentation.svg?react';
-import { removePictureInPicture } from 'mastodon/actions/picture_in_picture';
-import { Icon }  from 'mastodon/components/icon';
-
-class PictureInPicturePlaceholder extends PureComponent {
-
-  static propTypes = {
-    dispatch: PropTypes.func.isRequired,
-    aspectRatio: PropTypes.string,
-  };
-
-  handleClick = () => {
-    const { dispatch } = this.props;
-    dispatch(removePictureInPicture());
-  };
-
-  render () {
-    const { aspectRatio } = this.props;
-
-    return (
-      
-        
-        
-      
-    );
-  }
-
-}
-
-export default connect()(PictureInPicturePlaceholder);
diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.tsx b/app/javascript/mastodon/components/picture_in_picture_placeholder.tsx
new file mode 100644
index 000000000..829ab5feb
--- /dev/null
+++ b/app/javascript/mastodon/components/picture_in_picture_placeholder.tsx
@@ -0,0 +1,46 @@
+import { useCallback } from 'react';
+
+import { FormattedMessage } from 'react-intl';
+
+import PipExitIcon from '@/material-icons/400-24px/pip_exit.svg?react';
+import { removePictureInPicture } from 'mastodon/actions/picture_in_picture';
+import { Icon } from 'mastodon/components/icon';
+import { useAppDispatch } from 'mastodon/store';
+
+export const PictureInPicturePlaceholder: React.FC<{ aspectRatio: string }> = ({
+  aspectRatio,
+}) => {
+  const dispatch = useAppDispatch();
+
+  const handleClick = useCallback(() => {
+    dispatch(removePictureInPicture());
+  }, [dispatch]);
+
+  const handleKeyDown = useCallback(
+    (e: React.KeyboardEvent) => {
+      if (e.key === 'Enter' || e.key === ' ') {
+        e.preventDefault();
+        e.stopPropagation();
+        handleClick();
+      }
+    },
+    [handleClick],
+  );
+
+  return (
+    
+      
+      
+    
+  );
+};
diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx
index c45942dde..21d596a58 100644
--- a/app/javascript/mastodon/components/status.jsx
+++ b/app/javascript/mastodon/components/status.jsx
@@ -17,7 +17,7 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
 import { ContentWarning } from 'mastodon/components/content_warning';
 import { FilterWarning } from 'mastodon/components/filter_warning';
 import { Icon }  from 'mastodon/components/icon';
-import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder';
+import { PictureInPicturePlaceholder } from 'mastodon/components/picture_in_picture_placeholder';
 import { withOptionalRouter, WithOptionalRouterPropTypes } from 'mastodon/utils/react_router';
 
 import Card from '../features/status/components/card';
@@ -484,9 +484,6 @@ class Status extends ImmutablePureComponent {
                 foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
                 accentColor={attachment.getIn(['meta', 'colors', 'accent'])}
                 duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
-                width={this.props.cachedMediaWidth}
-                height={110}
-                cacheWidth={this.props.cacheMediaWidth}
                 deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
                 sensitive={status.get('sensitive')}
                 blurhash={attachment.get('blurhash')}
diff --git a/app/javascript/mastodon/containers/media_container.jsx b/app/javascript/mastodon/containers/media_container.jsx
index 9c07341fa..e826dbfa9 100644
--- a/app/javascript/mastodon/containers/media_container.jsx
+++ b/app/javascript/mastodon/containers/media_container.jsx
@@ -8,7 +8,7 @@ import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
 import MediaGallery from 'mastodon/components/media_gallery';
 import ModalRoot from 'mastodon/components/modal_root';
 import { Poll } from 'mastodon/components/poll';
-import Audio from 'mastodon/features/audio';
+import { Audio } from 'mastodon/features/audio';
 import Card from 'mastodon/features/status/components/card';
 import MediaModal from 'mastodon/features/ui/components/media_modal';
 import { Video } from 'mastodon/features/video';
diff --git a/app/javascript/mastodon/features/alt_text_modal/index.tsx b/app/javascript/mastodon/features/alt_text_modal/index.tsx
index e2d05a99c..08e4a8917 100644
--- a/app/javascript/mastodon/features/alt_text_modal/index.tsx
+++ b/app/javascript/mastodon/features/alt_text_modal/index.tsx
@@ -27,7 +27,7 @@ import { Button } from 'mastodon/components/button';
 import { GIFV } from 'mastodon/components/gifv';
 import { LoadingIndicator } from 'mastodon/components/loading_indicator';
 import { Skeleton } from 'mastodon/components/skeleton';
-import Audio from 'mastodon/features/audio';
+import { Audio } from 'mastodon/features/audio';
 import { CharacterCounter } from 'mastodon/features/compose/components/character_counter';
 import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
 import { Video, getPointerPosition } from 'mastodon/features/video';
@@ -212,11 +212,11 @@ const Preview: React.FC<{
     return (