Compare commits
	
		
			No commits in common. "18fa2810c48fc5bf98de004d889e8fc5fce07e53" and "246771e8a0dfb434b7b19b0c5b4f5941799872fc" have entirely different histories.
		
	
	
		
			18fa2810c4
			...
			246771e8a0
		
	
		
					 9 changed files with 80 additions and 268 deletions
				
			
		
							
								
								
									
										3
									
								
								.env
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								.env
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
DEV_SERVER_HTTPS_CERT_BASE=
 | 
					DEV_SERVER_HTTPS_CERT_BASE=
 | 
				
			||||||
DEV_SERVER_HTTPS_CERT_PASS=
 | 
					DEV_SERVER_HTTPS_CERT_PASS=
 | 
				
			||||||
DEV_LOCATOR_EDITOR=vscode
 | 
					DEV_LOCATOR_EDITOR=vscode
 | 
				
			||||||
VITE_DEVTOOLS_OVERLAY=true
 | 
					VITE_DEVTOOLS_OVERLAY=true
 | 
				
			||||||
VITE_PLATFROM_MASONRY_ALWAYS_COMPAT=
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								bun.lockb
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								bun.lockb
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -18,7 +18,6 @@
 | 
				
			||||||
    "@solid-devtools/overlay": "^0.30.1",
 | 
					    "@solid-devtools/overlay": "^0.30.1",
 | 
				
			||||||
    "@suid/vite-plugin": "^0.3.1",
 | 
					    "@suid/vite-plugin": "^0.3.1",
 | 
				
			||||||
    "@types/hammerjs": "^2.0.46",
 | 
					    "@types/hammerjs": "^2.0.46",
 | 
				
			||||||
    "@types/masonry-layout": "^4.2.8",
 | 
					 | 
				
			||||||
    "@vite-pwa/assets-generator": "^0.2.6",
 | 
					    "@vite-pwa/assets-generator": "^0.2.6",
 | 
				
			||||||
    "postcss": "^8.4.49",
 | 
					    "postcss": "^8.4.49",
 | 
				
			||||||
    "prettier": "^3.3.3",
 | 
					    "prettier": "^3.3.3",
 | 
				
			||||||
| 
						 | 
					@ -50,7 +49,6 @@
 | 
				
			||||||
    "fast-average-color": "^9.4.0",
 | 
					    "fast-average-color": "^9.4.0",
 | 
				
			||||||
    "hammerjs": "^2.0.8",
 | 
					    "hammerjs": "^2.0.8",
 | 
				
			||||||
    "iso-639-1": "^3.1.3",
 | 
					    "iso-639-1": "^3.1.3",
 | 
				
			||||||
    "masonry-layout": "^4.2.2",
 | 
					 | 
				
			||||||
    "masto": "^6.10.1",
 | 
					    "masto": "^6.10.1",
 | 
				
			||||||
    "nanostores": "^0.11.3",
 | 
					    "nanostores": "^0.11.3",
 | 
				
			||||||
    "normalize.css": "^8.0.1",
 | 
					    "normalize.css": "^8.0.1",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								src/overrides.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								src/overrides.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -11,10 +11,6 @@ interface ImportMetaEnv {
 | 
				
			||||||
   * Attach the overlay (in the dev mode) if it's `"true"`.
 | 
					   * Attach the overlay (in the dev mode) if it's `"true"`.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  readonly VITE_DEVTOOLS_OVERLAY?: string;
 | 
					  readonly VITE_DEVTOOLS_OVERLAY?: string;
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Always use compatible version of Masonry.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  readonly VITE_PLATFROM_MASONRY_ALWAYS_COMPAT?: string
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ImportMeta {
 | 
					interface ImportMeta {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +0,0 @@
 | 
				
			||||||
.CompatMasonry>* {
 | 
					 | 
				
			||||||
  margin-bottom: var(--Masonry-row-gap);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@supports (grid-template-rows: masonry) {
 | 
					 | 
				
			||||||
  .NativeMasonry {
 | 
					 | 
				
			||||||
    display: grid;
 | 
					 | 
				
			||||||
    grid-template-columns: repeat(auto-fit, minmax(44px, min-content));
 | 
					 | 
				
			||||||
    grid-template-rows: masonry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &:has(> :last-child:nth-child(2n)) {
 | 
					 | 
				
			||||||
      grid-template-columns: repeat(2, minmax(auto, min-content));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,158 +0,0 @@
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  type Component,
 | 
					 | 
				
			||||||
  type JSX,
 | 
					 | 
				
			||||||
  splitProps,
 | 
					 | 
				
			||||||
  type Ref,
 | 
					 | 
				
			||||||
  createRenderEffect,
 | 
					 | 
				
			||||||
  onCleanup,
 | 
					 | 
				
			||||||
  children,
 | 
					 | 
				
			||||||
  createEffect,
 | 
					 | 
				
			||||||
  createSignal,
 | 
					 | 
				
			||||||
  onMount,
 | 
					 | 
				
			||||||
} from "solid-js";
 | 
					 | 
				
			||||||
import { Dynamic, type DynamicProps } from "solid-js/web";
 | 
					 | 
				
			||||||
import MasonryLayout from "masonry-layout";
 | 
					 | 
				
			||||||
import { createElementSize } from "@solid-primitives/resize-observer";
 | 
					 | 
				
			||||||
import "./Masonry.css";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MasonryContainer =
 | 
					 | 
				
			||||||
  | Exclude<keyof JSX.IntrinsicElements, keyof JSX.SVGElementTags>
 | 
					 | 
				
			||||||
  | Component<{
 | 
					 | 
				
			||||||
      ref?: Ref<Element>;
 | 
					 | 
				
			||||||
      class?: string;
 | 
					 | 
				
			||||||
      children?: JSX.Element;
 | 
					 | 
				
			||||||
    }>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ElementOf<T extends MasonryContainer> =
 | 
					 | 
				
			||||||
  T extends Exclude<keyof JSX.IntrinsicElements, keyof JSX.SVGElementTags>
 | 
					 | 
				
			||||||
    ? JSX.IntrinsicElements[T] extends { ref?: Ref<infer E> }
 | 
					 | 
				
			||||||
      ? E
 | 
					 | 
				
			||||||
      : never
 | 
					 | 
				
			||||||
    : T extends Component<{ ref?: Ref<infer E> }>
 | 
					 | 
				
			||||||
      ? E
 | 
					 | 
				
			||||||
      : never;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function forwardRef<T>(value: T, ref?: Ref<T>) {
 | 
					 | 
				
			||||||
  if (!ref) return;
 | 
					 | 
				
			||||||
  (ref as (value: T) => void)(value);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function createMasonry(element: Element, options: () => MasonryLayout.Options) {
 | 
					 | 
				
			||||||
  const layout = new MasonryLayout(element, {
 | 
					 | 
				
			||||||
    initLayout: false,
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  onCleanup(() => layout.destroy?.());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const size = createElementSize(element);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  createRenderEffect(() => {
 | 
					 | 
				
			||||||
    const opts = options();
 | 
					 | 
				
			||||||
    layout.option?.(opts);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  createRenderEffect(() => {
 | 
					 | 
				
			||||||
    const width = size.width; // only tracking width
 | 
					 | 
				
			||||||
    layout.layout?.();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (import.meta.hot) {
 | 
					 | 
				
			||||||
    import.meta.hot.on("vite:afterUpdate", () => {
 | 
					 | 
				
			||||||
      layout.layout?.();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return layout;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const supportsCSSMasonryLayout = /* @__PURE__ */ CSS.supports(
 | 
					 | 
				
			||||||
  "grid-template-rows",
 | 
					 | 
				
			||||||
  "masonry",
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
console.debug("supports css masonry layout", supportsCSSMasonryLayout);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const useNativeImpl = import.meta.env.VITE_PLATFROM_MASONRY_ALWAYS_COMPAT
 | 
					 | 
				
			||||||
  ? false
 | 
					 | 
				
			||||||
  : supportsCSSMasonryLayout;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (import.meta.env.VITE_PLATFROM_MASONRY_ALWAYS_COMPAT) {
 | 
					 | 
				
			||||||
  console.warn(
 | 
					 | 
				
			||||||
    "Masonry is in compat mode because VITE_PLATFORM_MASONRY_ALWAYS_COMPAT is enabled",
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function MasonryCompat<T extends MasonryContainer>(
 | 
					 | 
				
			||||||
  oprops: DynamicProps<T> & { class?: string },
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
  const [props, rest] = splitProps(oprops, ["ref", "children", "class"]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const childrenComponents = children(() => props.children);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Dynamic
 | 
					 | 
				
			||||||
      ref={(element: ElementOf<T>) => {
 | 
					 | 
				
			||||||
        forwardRef(element, props.ref as Ref<typeof element> | undefined);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const [columnGap, setColumnGap] = createSignal<number>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const layout = createMasonry(element, () => {
 | 
					 | 
				
			||||||
          return {
 | 
					 | 
				
			||||||
            gutter: columnGap(),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        createEffect(() => {
 | 
					 | 
				
			||||||
          const computedStyle = window.getComputedStyle(element);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          const rowGap = computedStyle.rowGap;
 | 
					 | 
				
			||||||
          if (element instanceof HTMLElement) {
 | 
					 | 
				
			||||||
            element.style.setProperty("--Masonry-row-gap", rowGap);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          const colGap = computedStyle.columnGap;
 | 
					 | 
				
			||||||
          if (colGap) {
 | 
					 | 
				
			||||||
            setColumnGap(Number(colGap.slice(0, colGap.length - 2)));
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        createRenderEffect(() => {
 | 
					 | 
				
			||||||
          childrenComponents(); // just tracks
 | 
					 | 
				
			||||||
          setTimeout(() => {
 | 
					 | 
				
			||||||
            layout.reloadItems?.();
 | 
					 | 
				
			||||||
            layout.layout?.();
 | 
					 | 
				
			||||||
          }, 0);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      class={`Masonry CompatMasonry ${props.class || ""}`}
 | 
					 | 
				
			||||||
      {...rest}
 | 
					 | 
				
			||||||
      children={childrenComponents}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function MasonryNative<T extends MasonryContainer>(
 | 
					 | 
				
			||||||
  oprops: DynamicProps<T> & { class?: string },
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
  const [props, rest] = splitProps(oprops, ["class"]);
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Dynamic class={`Masonry NativeMasonry ${props.class || ""}`} {...rest} />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Masonry Layout Container.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * **Native if possible** This component uses css masonry layout
 | 
					 | 
				
			||||||
 * and fallback to masonry-layout if not supported. The children
 | 
					 | 
				
			||||||
 * must have specified width and height.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * **Children Changes** As the children changed, reflow will be triggered,
 | 
					 | 
				
			||||||
 * and there is might be a blink (or transition) for user. If it's not your
 | 
					 | 
				
			||||||
 * intention, don't remove/add the direct children. Instead wraps them under
 | 
					 | 
				
			||||||
 * containers and set the width and height on the container.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * **CSS compatibility** This component compatible to "gap" "row-gap"
 | 
					 | 
				
			||||||
 * "column-gap" property. But they are read only once after the element mounted.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default useNativeImpl ? MasonryNative : MasonryCompat;
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
.MediaAttachmentGrid {
 | 
					.MediaAttachmentGrid {
 | 
				
			||||||
  /* Note: MeidaAttachmentGrid has hard-coded layout calcalation */
 | 
					  /* Note: MeidaAttachmentGrid has hard-coded layout calcalation */
 | 
				
			||||||
  margin-top: 1em;
 | 
					  margin-top: 1em;
 | 
				
			||||||
  margin-left: var(--card-pad, 0);
 | 
					  margin-left: calc(var(--card-pad, 0) + 8px);
 | 
				
			||||||
  margin-right: var(--card-pad, 0);
 | 
					  margin-right: var(--card-pad, 0);
 | 
				
			||||||
  contain: layout style;
 | 
					 | 
				
			||||||
  gap: 4px;
 | 
					  gap: 4px;
 | 
				
			||||||
 | 
					  contain: layout style;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  > * {
 | 
					  > :where(img, video, .sensitive-placeholder) {
 | 
				
			||||||
    max-height: 35vh;
 | 
					    max-height: 35vh;
 | 
				
			||||||
    min-height: 40px;
 | 
					    min-height: 40px;
 | 
				
			||||||
    min-width: 40px;
 | 
					    min-width: 40px;
 | 
				
			||||||
| 
						 | 
					@ -22,20 +22,12 @@
 | 
				
			||||||
    &:focus-visible {
 | 
					    &:focus-visible {
 | 
				
			||||||
      outline: 8px solid var(--media-color-accent, var(--tutu-color-surface-d));
 | 
					      outline: 8px solid var(--media-color-accent, var(--tutu-color-surface-d));
 | 
				
			||||||
      border-color: var(--media-color-accent, var(--tutu-color-surface-d));
 | 
					      border-color: var(--media-color-accent, var(--tutu-color-surface-d));
 | 
				
			||||||
      z-index: calc(var(--tutu-zidx-nav) - 1);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  > * > * {
 | 
					 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
    height: 100%;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  > * > :where(img, video) {
 | 
					 | 
				
			||||||
    object-fit: contain;
 | 
					    object-fit: contain;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  > * >.sensitive-placeholder {
 | 
					  >.sensitive-placeholder {
 | 
				
			||||||
    display: inline-flex;
 | 
					    display: inline-flex;
 | 
				
			||||||
    display: inline flex;
 | 
					    display: inline flex;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
| 
						 | 
					@ -43,6 +35,6 @@
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:where(.thread-top, .thread-mid)>.MediaAttachmentGrid {
 | 
					:where(.thread-top, .thread-mid) > .MediaAttachmentGrid {
 | 
				
			||||||
  margin-left: calc(var(--card-pad, 0) + var(--toot-avatar-size, 0) + 8px);
 | 
					  margin-left: calc(var(--card-pad, 0) + var(--toot-avatar-size, 0) + 8px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,6 @@ import "./MediaAttachmentGrid.css";
 | 
				
			||||||
import cardStyle from "~material/cards.module.css";
 | 
					import cardStyle from "~material/cards.module.css";
 | 
				
			||||||
import { Preview } from "@suid/icons-material";
 | 
					import { Preview } from "@suid/icons-material";
 | 
				
			||||||
import { IconButton } from "@suid/material";
 | 
					import { IconButton } from "@suid/material";
 | 
				
			||||||
import Masonry from "~platform/Masonry";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ElementSize = { width: number; height: number };
 | 
					type ElementSize = { width: number; height: number };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -150,13 +149,13 @@ const MediaAttachmentGrid: Component<{
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Masonry
 | 
					    <section
 | 
				
			||||||
      component="section"
 | 
					 | 
				
			||||||
      ref={setRootRef}
 | 
					      ref={setRootRef}
 | 
				
			||||||
      class={`MediaAttachmentGrid ${cardStyle.cardNoPad}`}
 | 
					      class={`MediaAttachmentGrid ${cardStyle.cardNoPad}`}
 | 
				
			||||||
      classList={{
 | 
					      classList={{
 | 
				
			||||||
        sensitive: props.sensitive,
 | 
					        sensitive: props.sensitive,
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
 | 
					      style={{ "column-count": columnCount() }}
 | 
				
			||||||
      onClick={isolateCallback}
 | 
					      onClick={isolateCallback}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <Index each={props.attachments}>
 | 
					      <Index each={props.attachments}>
 | 
				
			||||||
| 
						 | 
					@ -165,79 +164,81 @@ const MediaAttachmentGrid: Component<{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          const style = createMemo(() => itemStyle(item()));
 | 
					          const style = createMemo(() => itemStyle(item()));
 | 
				
			||||||
          return (
 | 
					          return (
 | 
				
			||||||
            <div style={style()} role="presentation">
 | 
					            <Switch>
 | 
				
			||||||
              <Switch>
 | 
					              <Match when={props.sensitive && !isReveal(index)}>
 | 
				
			||||||
                <Match when={props.sensitive && !isReveal(index)}>
 | 
					                <div
 | 
				
			||||||
                  <div
 | 
					                  class="sensitive-placeholder"
 | 
				
			||||||
                    class="sensitive-placeholder"
 | 
					                  style={style()}
 | 
				
			||||||
                    data-sort={index}
 | 
					                  data-sort={index}
 | 
				
			||||||
                    data-media-type={item().type}
 | 
					                  data-media-type={item().type}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <IconButton
 | 
				
			||||||
 | 
					                    color="inherit"
 | 
				
			||||||
 | 
					                    size="large"
 | 
				
			||||||
 | 
					                    onClick={[addReveal, index]}
 | 
				
			||||||
 | 
					                    aria-label="Reveal this media"
 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    <IconButton
 | 
					                    <Preview />
 | 
				
			||||||
                      color="inherit"
 | 
					                  </IconButton>
 | 
				
			||||||
                      size="large"
 | 
					                </div>
 | 
				
			||||||
                      onClick={[addReveal, index]}
 | 
					              </Match>
 | 
				
			||||||
                      aria-label="Reveal this media"
 | 
					              <Match when={itemType() === "image"}>
 | 
				
			||||||
                    >
 | 
					                <img
 | 
				
			||||||
                      <Preview />
 | 
					                  src={item().previewUrl}
 | 
				
			||||||
                    </IconButton>
 | 
					                  width={item().meta?.small?.width}
 | 
				
			||||||
                  </div>
 | 
					                  height={item().meta?.small?.height}
 | 
				
			||||||
                </Match>
 | 
					                  alt={item().description || undefined}
 | 
				
			||||||
                <Match when={itemType() === "image"}>
 | 
					                  onClick={[openViewerFor, index]}
 | 
				
			||||||
                  <img
 | 
					                  loading="lazy"
 | 
				
			||||||
                    src={item().previewUrl}
 | 
					                  style={style()}
 | 
				
			||||||
                    width={item().meta?.small?.width}
 | 
					                  data-sort={index}
 | 
				
			||||||
                    height={item().meta?.small?.height}
 | 
					                  data-media-type={item().type}
 | 
				
			||||||
                    alt={item().description || undefined}
 | 
					                ></img>
 | 
				
			||||||
                    onClick={[openViewerFor, index]}
 | 
					              </Match>
 | 
				
			||||||
                    loading="lazy"
 | 
					              <Match when={itemType() === "video"}>
 | 
				
			||||||
                    data-sort={index}
 | 
					                <video
 | 
				
			||||||
                    data-media-type={item().type}
 | 
					                  src={item().url || undefined}
 | 
				
			||||||
                  ></img>
 | 
					                  autoplay={!props.sensitive && settings().autoPlayVideos}
 | 
				
			||||||
                </Match>
 | 
					                  playsinline={settings().autoPlayVideos ? true : undefined}
 | 
				
			||||||
                <Match when={itemType() === "video"}>
 | 
					                  controls
 | 
				
			||||||
                  <video
 | 
					                  poster={item().previewUrl}
 | 
				
			||||||
                    src={item().url || undefined}
 | 
					                  width={item().meta?.small?.width}
 | 
				
			||||||
                    autoplay={!props.sensitive && settings().autoPlayVideos}
 | 
					                  height={item().meta?.small?.height}
 | 
				
			||||||
                    playsinline={settings().autoPlayVideos ? true : undefined}
 | 
					                  style={style()}
 | 
				
			||||||
                    controls
 | 
					                  data-sort={index}
 | 
				
			||||||
                    poster={item().previewUrl}
 | 
					                  data-media-type={item().type}
 | 
				
			||||||
                    width={item().meta?.small?.width}
 | 
					                  preload="metadata"
 | 
				
			||||||
                    height={item().meta?.small?.height}
 | 
					                />
 | 
				
			||||||
                    data-sort={index}
 | 
					              </Match>
 | 
				
			||||||
                    data-media-type={item().type}
 | 
					              <Match when={itemType() === "gifv"}>
 | 
				
			||||||
                    preload="metadata"
 | 
					                <video
 | 
				
			||||||
                  />
 | 
					                  src={item().url || undefined}
 | 
				
			||||||
                </Match>
 | 
					                  autoplay={!props.sensitive && settings().autoPlayGIFs}
 | 
				
			||||||
                <Match when={itemType() === "gifv"}>
 | 
					                  controls
 | 
				
			||||||
                  <video
 | 
					                  playsinline /* or safari on iOS will play in full-screen */
 | 
				
			||||||
                    src={item().url || undefined}
 | 
					                  loop
 | 
				
			||||||
                    autoplay={!props.sensitive && settings().autoPlayGIFs}
 | 
					                  poster={item().previewUrl}
 | 
				
			||||||
                    controls
 | 
					                  width={item().meta?.small?.width}
 | 
				
			||||||
                    playsinline /* or safari on iOS will play in full-screen */
 | 
					                  height={item().meta?.small?.height}
 | 
				
			||||||
                    loop
 | 
					                  style={style()}
 | 
				
			||||||
                    poster={item().previewUrl}
 | 
					                  data-sort={index}
 | 
				
			||||||
                    width={item().meta?.small?.width}
 | 
					                  data-media-type={item().type}
 | 
				
			||||||
                    height={item().meta?.small?.height}
 | 
					                  preload="metadata"
 | 
				
			||||||
                    data-sort={index}
 | 
					                />
 | 
				
			||||||
                    data-media-type={item().type}
 | 
					              </Match>
 | 
				
			||||||
                    preload="metadata"
 | 
					              <Match when={itemType() === "audio"}>
 | 
				
			||||||
                  />
 | 
					                <audio
 | 
				
			||||||
                </Match>
 | 
					                  src={item().url || undefined}
 | 
				
			||||||
                <Match when={itemType() === "audio"}>
 | 
					                  controls
 | 
				
			||||||
                  <audio
 | 
					                  data-sort={index}
 | 
				
			||||||
                    src={item().url || undefined}
 | 
					                  data-media-type={item().type}
 | 
				
			||||||
                    controls
 | 
					                ></audio>
 | 
				
			||||||
                    data-sort={index}
 | 
					              </Match>
 | 
				
			||||||
                    data-media-type={item().type}
 | 
					            </Switch>
 | 
				
			||||||
                  ></audio>
 | 
					 | 
				
			||||||
                </Match>
 | 
					 | 
				
			||||||
              </Switch>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
      </Index>
 | 
					      </Index>
 | 
				
			||||||
    </Masonry>
 | 
					    </section>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
.TootContent {
 | 
					.TootContent {
 | 
				
			||||||
  margin-left: var(--card-pad, 0);
 | 
					  margin-left: calc(var(--card-pad, 0) + 8px);
 | 
				
			||||||
  margin-right: var(--card-pad, 0);
 | 
					  margin-right: var(--card-pad, 0);
 | 
				
			||||||
  line-height: 1.5;
 | 
					  line-height: 1.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue