feat: add Phokus UI Lab
Add a dev-only Vite UI mode with Tauri API mocks, in-memory fixture scenarios, reusable media source handling, and documentation for browser-based visual testing.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
||||
import { DuplicateGroup, useGalleryStore } from "../store";
|
||||
import { FolderScopeDropdown } from "./FolderScopeDropdown";
|
||||
import { mediaSrc } from "../lib/mediaSrc";
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes >= 1_073_741_824) return `${(bytes / 1_073_741_824).toFixed(1)} GB`;
|
||||
@@ -68,7 +68,7 @@ function DuplicateGroupCard({ group }: { group: DuplicateGroup }) {
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{group.images.map((image) => {
|
||||
const isSelected = selectedIds.has(image.id);
|
||||
const src = image.thumbnail_path ? convertFileSrc(image.thumbnail_path) : null;
|
||||
const src = mediaSrc(image.thumbnail_path);
|
||||
return (
|
||||
<button
|
||||
key={image.id}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useCallback, useMemo, useState } from "react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
||||
import { ImageRecord, parseSearchValue, tileSizeForZoom, useGalleryStore } from "../store";
|
||||
import { BulkActionBar } from "./BulkActionBar";
|
||||
import { Tooltip } from "./Tooltip";
|
||||
import { mediaSrc } from "../lib/mediaSrc";
|
||||
|
||||
const GAP = 6;
|
||||
|
||||
@@ -123,7 +123,7 @@ export function ImageTile({
|
||||
const toggleGallerySelected = useGalleryStore((state) => state.toggleGallerySelected);
|
||||
const canFindSimilar = image.embedding_status === "ready";
|
||||
|
||||
const src = image.thumbnail_path ? convertFileSrc(image.thumbnail_path) : null;
|
||||
const src = mediaSrc(image.thumbnail_path);
|
||||
|
||||
return (
|
||||
<Tooltip label={image.filename} delay={500} block followCursor>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useCallback, useRef, useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { convertFileSrc, invoke } from "@tauri-apps/api/core";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { revealItemInDir } from "@tauri-apps/plugin-opener";
|
||||
import { useGalleryStore, ImageTag, ImageExif, AiRating } from "../store";
|
||||
import { VideoPlayer } from "./VideoPlayer";
|
||||
import { mediaSrc } from "../lib/mediaSrc";
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
@@ -475,12 +476,12 @@ export function Lightbox() {
|
||||
transition={{ duration: 0.12 }}
|
||||
>
|
||||
{selectedImage.media_kind === "video" ? (
|
||||
<VideoPlayer src={convertFileSrc(selectedImage.path)} />
|
||||
<VideoPlayer src={mediaSrc(selectedImage.path) ?? ""} />
|
||||
) : (
|
||||
<>
|
||||
<img
|
||||
ref={imgRef}
|
||||
src={convertFileSrc(selectedImage.path)}
|
||||
src={mediaSrc(selectedImage.path) ?? ""}
|
||||
alt={selectedImage.filename}
|
||||
className="max-w-full rounded-2xl shadow-2xl"
|
||||
draggable={false}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Reorder, useDragControls } from "framer-motion";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
||||
import { useGalleryStore, Folder, Album, IndexProgress } from "../store";
|
||||
import { ThemedDropdown } from "./ThemedDropdown";
|
||||
import { mediaSrc } from "../lib/mediaSrc";
|
||||
|
||||
interface ContextMenuState {
|
||||
folderId: number;
|
||||
@@ -445,7 +445,7 @@ function AlbumItem({
|
||||
setRenaming(false);
|
||||
};
|
||||
|
||||
const cover = album.cover_thumbnail_path ? convertFileSrc(album.cover_thumbnail_path) : null;
|
||||
const cover = mediaSrc(album.cover_thumbnail_path);
|
||||
|
||||
const row = (
|
||||
<div
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
||||
import { ExploreTagEntry, TagCloudEntry, useGalleryStore } from "../store";
|
||||
import { FolderScopeDropdown } from "./FolderScopeDropdown";
|
||||
import { Tooltip } from "./Tooltip";
|
||||
import { mediaSrc } from "../lib/mediaSrc";
|
||||
|
||||
const ACCENTS = [
|
||||
"#60a5fa",
|
||||
@@ -148,7 +148,7 @@ function buildCloud(entries: TagCloudEntry[], containerW: number, containerH: nu
|
||||
}
|
||||
|
||||
function CloudCard({ node, onOpen, animated }: { node: PlacedNode; onOpen: (imageIds: number[]) => void; animated: boolean }) {
|
||||
const src = node.entry.thumbnail_path ? convertFileSrc(node.entry.thumbnail_path) : null;
|
||||
const src = mediaSrc(node.entry.thumbnail_path);
|
||||
const { w, h, accent } = node;
|
||||
const driftTransition = {
|
||||
duration: node.driftDuration,
|
||||
|
||||
Reference in New Issue
Block a user