From 553fadd2d4d45459c3baeec998f461ef44c2a9a4 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Thu, 20 Nov 2025 21:43:48 -0800 Subject: [PATCH] Switch to ts-client types from root package --- apps/tauri/.taurignore | 1 + core/src/domain/file.rs | 39 +- ...51117_000001_add_blurhash_to_media_data.rs | 1 + ...000002_add_unique_constraint_to_entries.rs | 1 + core/src/ops/libraries/open/mod.rs | 1 + core/src/ops/media/blurhash.rs | 1 + core/src/ops/volumes/track/input.rs | 1 + core/src/ops/volumes/untrack/input.rs | 1 + crates/log-analyzer/.gitignore | 1 + crates/log-analyzer/Cargo.toml | 1 + crates/log-analyzer/examples/simple.rs | 1 + crates/log-analyzer/src/output/json.rs | 1 + crates/log-analyzer/src/pattern/template.rs | 1 + crates/log-analyzer/src/pattern/types.rs | 1 + packages/interface/src/Explorer.tsx | 2 +- packages/interface/src/Inspector.tsx | 2 +- .../src/components/Explorer/File/File.tsx | 2 +- .../src/components/Explorer/File/Metadata.tsx | 2 +- .../src/components/Explorer/File/Thumb.tsx | 338 +-- .../Explorer/File/ThumbstripScrubber.tsx | 2 +- .../src/components/Explorer/File/Title.tsx | 2 +- .../components/Explorer/SelectionContext.tsx | 2 +- .../src/components/Explorer/SortMenu.tsx | 2 +- .../Explorer/components/AddLocationModal.tsx | 2 +- .../Explorer/components/Breadcrumb.tsx | 2 +- .../Explorer/components/LocationsSection.tsx | 2 +- .../Explorer/components/PathBar.tsx | 2 +- .../src/components/Explorer/context.tsx | 2 +- .../Explorer/hooks/useExplorerKeyboard.ts | 4 +- .../src/components/Explorer/utils.ts | 2 +- .../Explorer/views/ColumnView/Column.tsx | 4 +- .../Explorer/views/ColumnView/ColumnItem.tsx | 4 +- .../Explorer/views/ColumnView/ColumnView.tsx | 4 +- .../Explorer/views/GridView/FileCard.tsx | 10 +- .../Explorer/views/GridView/GridView.tsx | 2 +- .../Explorer/views/ListView/FileRow.tsx | 6 +- .../Explorer/views/ListView/ListView.tsx | 2 +- .../views/MediaView/MediaViewItem.tsx | 2 +- .../Explorer/views/MediaView/utils.ts | 2 +- .../Explorer/views/SizeView/SizeCircle.tsx | 6 +- .../Explorer/views/SizeView/SizeView.tsx | 8 +- .../JobManager/components/JobProgressBar.tsx | 2 +- .../components/JobStatusIndicator.tsx | 2 +- .../src/components/JobManager/types.ts | 2 +- .../components/QuickPreview/AudioPlayer.tsx | 2 +- .../QuickPreview/ContentRenderer.tsx | 4 +- .../components/QuickPreview/QuickPreview.tsx | 2 +- .../QuickPreview/QuickPreviewModal.tsx | 2 +- .../src/components/QuickPreview/Subtitles.tsx | 2 +- .../QuickPreview/TimelineScrubber.tsx | 2 +- .../components/QuickPreview/VideoPlayer.tsx | 2 +- .../SpacesSidebar/AddGroupModal.tsx | 2 +- .../components/SpacesSidebar/DeviceGroup.tsx | 2 +- .../components/SpacesSidebar/SpaceGroup.tsx | 2 +- .../components/SpacesSidebar/SpaceItem.tsx | 2 +- .../SpacesSidebar/SpaceSwitcher.tsx | 2 +- .../components/SpacesSidebar/VolumesGroup.tsx | 2 +- .../src/components/SyncSetupModal.tsx | 2 +- packages/interface/src/context.tsx | 2 +- .../interface/src/hooks/useJobDispatch.ts | 1 + .../src/inspectors/FileInspector.tsx | 2000 ++++++++--------- .../src/inspectors/LocationInspector.tsx | 2 +- .../src/routes/overview/StorageOverview.tsx | 2 +- .../interface/src/routes/overview/index.tsx | 2 +- packages/ts-client/package.json | 10 + packages/ts-client/src/deviceIcons.ts | 1 + packages/ts-client/src/event-filter.ts | 1 - packages/ts-client/src/generated/types.ts | 264 +-- packages/ts-client/src/hooks/useMutation.ts | 1 + .../ts-client/src/hooks/useNormalizedQuery.ts | 5 + packages/ts-client/src/hooks/useQuery.ts | 1 + packages/ts-client/src/subscriptionManager.ts | 1 + packages/ts-client/tsconfig.json | 2 +- 73 files changed, 1353 insertions(+), 1450 deletions(-) diff --git a/apps/tauri/.taurignore b/apps/tauri/.taurignore index 4c41fded5..e6c4cbde7 100644 --- a/apps/tauri/.taurignore +++ b/apps/tauri/.taurignore @@ -19,3 +19,4 @@ + diff --git a/core/src/domain/file.rs b/core/src/domain/file.rs index da6f36201..d2d33cb8c 100644 --- a/core/src/domain/file.rs +++ b/core/src/domain/file.rs @@ -21,19 +21,13 @@ use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Type)] pub enum EntryKind { /// Regular file - File { - /// File extension (without dot) - extension: Option, - }, + File, /// Directory Directory, /// Symbolic link - Symlink { - /// Target path - target: String, - }, + Symlink, } /// Represents a file within the Spacedrive VDFS. @@ -49,9 +43,15 @@ pub struct File { /// The universal path to the file in Spacedrive's VDFS pub sd_path: SdPath, + /// The file kind (file, directory, symlink) + pub kind: EntryKind, + /// The name of the file, including the extension pub name: String, + /// The file extension (without dot) + pub extension: Option, + /// The size of the file in bytes pub size: u64, @@ -78,10 +78,8 @@ pub struct File { pub accessed_at: Option>, /// Additional computed fields - pub content_kind: ContentKind, - pub extension: Option, - pub kind: EntryKind, - pub is_local: bool, + pub content_kind: ContentKind, // This is redundant with ContentIdentity, it lives inside + pub is_local: bool, // this is also redundant with SdPath /// Video duration (for grid display optimization) pub duration_seconds: Option, @@ -226,22 +224,13 @@ impl File { // Convert entity kind to domain EntryKind let kind = match model.kind { - 0 => EntryKind::File { - extension: model.extension.clone(), - }, + 0 => EntryKind::File, 1 => EntryKind::Directory, - 2 => EntryKind::Symlink { - target: String::new(), - }, - _ => EntryKind::File { - extension: model.extension.clone(), - }, + 2 => EntryKind::Symlink, + _ => EntryKind::File, }; - let extension = match &kind { - EntryKind::File { extension } => extension.clone(), - _ => None, - }; + let extension = model.extension.clone(); // Generate UUID from id if uuid is None let id = model.uuid.unwrap_or_else(|| { diff --git a/core/src/infra/db/migration/m20251117_000001_add_blurhash_to_media_data.rs b/core/src/infra/db/migration/m20251117_000001_add_blurhash_to_media_data.rs index 62872c552..81e3b51dc 100644 --- a/core/src/infra/db/migration/m20251117_000001_add_blurhash_to_media_data.rs +++ b/core/src/infra/db/migration/m20251117_000001_add_blurhash_to_media_data.rs @@ -69,3 +69,4 @@ enum VideoMediaData { } + diff --git a/core/src/infra/db/migration/m20251117_000002_add_unique_constraint_to_entries.rs b/core/src/infra/db/migration/m20251117_000002_add_unique_constraint_to_entries.rs index 2338de187..9c5fe5ccb 100644 --- a/core/src/infra/db/migration/m20251117_000002_add_unique_constraint_to_entries.rs +++ b/core/src/infra/db/migration/m20251117_000002_add_unique_constraint_to_entries.rs @@ -48,3 +48,4 @@ enum Entries { + diff --git a/core/src/ops/libraries/open/mod.rs b/core/src/ops/libraries/open/mod.rs index aada39fb8..3fefa84e4 100644 --- a/core/src/ops/libraries/open/mod.rs +++ b/core/src/ops/libraries/open/mod.rs @@ -9,3 +9,4 @@ pub use input::LibraryOpenInput; pub use output::LibraryOpenOutput; + diff --git a/core/src/ops/media/blurhash.rs b/core/src/ops/media/blurhash.rs index 5de786172..b82bcb936 100644 --- a/core/src/ops/media/blurhash.rs +++ b/core/src/ops/media/blurhash.rs @@ -123,3 +123,4 @@ mod tests { } + diff --git a/core/src/ops/volumes/track/input.rs b/core/src/ops/volumes/track/input.rs index 0e4801bce..b71b18c55 100644 --- a/core/src/ops/volumes/track/input.rs +++ b/core/src/ops/volumes/track/input.rs @@ -15,3 +15,4 @@ pub struct VolumeTrackInput { + diff --git a/core/src/ops/volumes/untrack/input.rs b/core/src/ops/volumes/untrack/input.rs index 38889219c..f69e120a7 100644 --- a/core/src/ops/volumes/untrack/input.rs +++ b/core/src/ops/volumes/untrack/input.rs @@ -12,3 +12,4 @@ pub struct VolumeUntrackInput { + diff --git a/crates/log-analyzer/.gitignore b/crates/log-analyzer/.gitignore index b30b44148..c0da3ff23 100644 --- a/crates/log-analyzer/.gitignore +++ b/crates/log-analyzer/.gitignore @@ -6,3 +6,4 @@ analysis.md + diff --git a/crates/log-analyzer/Cargo.toml b/crates/log-analyzer/Cargo.toml index fe25b1082..3ed23598c 100644 --- a/crates/log-analyzer/Cargo.toml +++ b/crates/log-analyzer/Cargo.toml @@ -40,3 +40,4 @@ required-features = ["cli"] + diff --git a/crates/log-analyzer/examples/simple.rs b/crates/log-analyzer/examples/simple.rs index d19b4e7f0..5db5eeb6e 100644 --- a/crates/log-analyzer/examples/simple.rs +++ b/crates/log-analyzer/examples/simple.rs @@ -51,3 +51,4 @@ fn main() -> Result<()> { + diff --git a/crates/log-analyzer/src/output/json.rs b/crates/log-analyzer/src/output/json.rs index be2cc0829..bc586192e 100644 --- a/crates/log-analyzer/src/output/json.rs +++ b/crates/log-analyzer/src/output/json.rs @@ -18,3 +18,4 @@ pub fn export_json(templates: &[Template], groups: &[LogGroup]) -> Result(); const thumbErrorCache = new Map(); export const Thumb = memo(function Thumb({ - file, - size = 100, - className, - frameClassName, - iconScale = 1, - squareMode = false, + file, + size = 100, + className, + frameClassName, + iconScale = 1, + squareMode = false, }: ThumbProps) { - const cacheKey = `${file.id}-${size}`; + const cacheKey = `${file.id}-${size}`; - const [thumbLoaded, setThumbLoaded] = useState( - () => thumbLoadedCache.get(cacheKey) || false, - ); - const [thumbError, setThumbError] = useState( - () => thumbErrorCache.get(cacheKey) || false, - ); + const [thumbLoaded, setThumbLoaded] = useState( + () => thumbLoadedCache.get(cacheKey) || false, + ); + const [thumbError, setThumbError] = useState( + () => thumbErrorCache.get(cacheKey) || false, + ); - // Update cache when state changes - useEffect(() => { - if (thumbLoaded) thumbLoadedCache.set(cacheKey, true); - }, [thumbLoaded, cacheKey]); + // Update cache when state changes + useEffect(() => { + if (thumbLoaded) thumbLoadedCache.set(cacheKey, true); + }, [thumbLoaded, cacheKey]); - useEffect(() => { - if (thumbError) thumbErrorCache.set(cacheKey, true); - }, [thumbError, cacheKey]); + useEffect(() => { + if (thumbError) thumbErrorCache.set(cacheKey, true); + }, [thumbError, cacheKey]); - const iconSize = size * iconScale; + const iconSize = size * iconScale; - // Check if this is a video with thumbstrip sidecar - const isVideo = file.content_identity?.kind === "video"; - const hasThumbstrip = file.sidecars?.some((s) => s.kind === "thumbstrip"); + // Check if this is a video with thumbstrip sidecar + const isVideo = file.content_identity?.kind === "video"; + const hasThumbstrip = file.sidecars?.some((s) => s.kind === "thumbstrip"); - // Get appropriate thumbnail URL from sidecars based on size - const getThumbnailUrl = (targetSize: number) => { - const serverUrl = (window as any).__SPACEDRIVE_SERVER_URL__; - const libraryId = (window as any).__SPACEDRIVE_LIBRARY_ID__; + // Get appropriate thumbnail URL from sidecars based on size + const getThumbnailUrl = (targetSize: number) => { + const serverUrl = (window as any).__SPACEDRIVE_SERVER_URL__; + const libraryId = (window as any).__SPACEDRIVE_LIBRARY_ID__; - if (!serverUrl || !libraryId) { - return null; - } + if (!serverUrl || !libraryId) { + return null; + } - // Need content_identity to build sidecar URL - if (!file.content_identity?.uuid) { - return null; - } + // Need content_identity to build sidecar URL + if (!file.content_identity?.uuid) { + return null; + } - // Find thumbnail sidecar closest to requested size - const thumbnails = file.sidecars.filter((s) => s.kind === "thumb"); + // Find thumbnail sidecar closest to requested size + const thumbnails = file.sidecars.filter((s) => s.kind === "thumb"); - if (thumbnails.length === 0) { - return null; - } + if (thumbnails.length === 0) { + return null; + } - // Prefer 1x (lower resolution) variants for better performance - // Only use higher resolution for very large sizes (>400px) - const preferredSize = targetSize <= 400 ? targetSize * 0.6 : targetSize; + // Prefer 1x (lower resolution) variants for better performance + // Only use higher resolution for very large sizes (>400px) + const preferredSize = targetSize <= 400 ? targetSize * 0.6 : targetSize; - const thumbnail = thumbnails.sort((a, b) => { - // Parse variant (e.g., "grid@1x", "detail@1x") to get size and scale - const aSize = parseInt( - a.variant.split("x")[0]?.replace(/\D/g, "") || "0", - ); - const bSize = parseInt( - b.variant.split("x")[0]?.replace(/\D/g, "") || "0", - ); + const thumbnail = thumbnails.sort((a, b) => { + // Parse variant (e.g., "grid@1x", "detail@1x") to get size and scale + const aSize = parseInt( + a.variant.split("x")[0]?.replace(/\D/g, "") || "0", + ); + const bSize = parseInt( + b.variant.split("x")[0]?.replace(/\D/g, "") || "0", + ); - // Extract scale factor (1x, 2x, 3x) from variants like "grid@1x" or "detail@2x" - const aScaleMatch = a.variant.match(/@(\d+)x/); - const bScaleMatch = b.variant.match(/@(\d+)x/); - const aScale = aScaleMatch ? parseInt(aScaleMatch[1]) : 1; - const bScale = bScaleMatch ? parseInt(bScaleMatch[1]) : 1; + // Extract scale factor (1x, 2x, 3x) from variants like "grid@1x" or "detail@2x" + const aScaleMatch = a.variant.match(/@(\d+)x/); + const bScaleMatch = b.variant.match(/@(\d+)x/); + const aScale = aScaleMatch ? parseInt(aScaleMatch[1]) : 1; + const bScale = bScaleMatch ? parseInt(bScaleMatch[1]) : 1; - // Strongly prefer 1x variants (add penalty for higher scales) - const aPenalty = (aScale - 1) * 100; - const bPenalty = (bScale - 1) * 100; + // Strongly prefer 1x variants (add penalty for higher scales) + const aPenalty = (aScale - 1) * 100; + const bPenalty = (bScale - 1) * 100; - // Find closest match to preferred size, with scale penalty - return ( - Math.abs(aSize - preferredSize) + - aPenalty - - (Math.abs(bSize - preferredSize) + bPenalty) - ); - })[0]; + // Find closest match to preferred size, with scale penalty + return ( + Math.abs(aSize - preferredSize) + + aPenalty - + (Math.abs(bSize - preferredSize) + bPenalty) + ); + })[0]; - const contentUuid = file.content_identity.uuid; - const url = `${serverUrl}/sidecar/${libraryId}/${contentUuid}/${thumbnail.kind}/${thumbnail.variant}.${thumbnail.format}`; + const contentUuid = file.content_identity.uuid; + const url = `${serverUrl}/sidecar/${libraryId}/${contentUuid}/${thumbnail.kind}/${thumbnail.variant}.${thumbnail.format}`; - return url; - }; + return url; + }; - const thumbnailSrc = getThumbnailUrl(size); + const thumbnailSrc = getThumbnailUrl(size); - // Get Spacedrive asset icon (dark theme) - const kindCapitalized = file.content_identity?.kind - ? file.content_identity.kind.charAt(0).toUpperCase() + - file.content_identity.kind.slice(1) - : "Document"; + // This is jank and has to be done in several places. Ideally a util function. + const fileKind = + file?.content_identity?.kind && file.content_identity.kind !== "unknown" + ? file.content_identity.kind + : file.kind === "File" + ? file.extension || "File" + : file.kind; + // this too + const kindCapitalized = fileKind.charAt(0).toUpperCase() + fileKind.slice(1); - const icon = getIcon( - kindCapitalized, - true, // Dark theme - file.extension, - file.kind === "Directory", - ); + const icon = getIcon( + kindCapitalized, + true, // Dark theme + file.extension, + file.kind === "Directory", + ); - return ( -
- {/* Always show icon first (instant), then thumbnail loads over it */} - + return ( +
+ {/* Always show icon first (instant), then thumbnail loads over it */} + - {/* Load thumbnail if available */} - {thumbnailSrc && !thumbError && ( - {file.name} setThumbLoaded(true)} - onError={() => setThumbError(true)} - /> - )} + {/* Load thumbnail if available */} + {thumbnailSrc && !thumbError && ( + {file.name} setThumbLoaded(true)} + onError={() => setThumbError(true)} + /> + )} - {/* Thumbstrip scrubber overlay (for videos with thumbstrips) */} - {isVideo && hasThumbstrip && thumbLoaded && ( - - )} -
- ); + {/* Thumbstrip scrubber overlay (for videos with thumbstrips) */} + {isVideo && hasThumbstrip && thumbLoaded && ( + + )} +
+ ); }); export function Icon({ - file, - size = 24, - className, + file, + size = 24, + className, }: { - file: File; - size?: number; - className?: string; + file: File; + size?: number; + className?: string; }) { - const kindCapitalized = file.content_identity?.kind - ? file.content_identity.kind.charAt(0).toUpperCase() + - file.content_identity.kind.slice(1) - : "Document"; + const kindCapitalized = file.content_identity?.kind + ? file.content_identity.kind.charAt(0).toUpperCase() + + file.content_identity.kind.slice(1) + : "Document"; - const icon = getIcon( - kindCapitalized, - true, // Dark theme - file.extension, - file.kind === "Directory", - ); + const icon = getIcon( + kindCapitalized, + true, // Dark theme + file.kind.type === "File" ? file.kind.data?.extension : undefined, + file.kind.type === "Directory", + ); - return ( - - ); + return ( + + ); } diff --git a/packages/interface/src/components/Explorer/File/ThumbstripScrubber.tsx b/packages/interface/src/components/Explorer/File/ThumbstripScrubber.tsx index 8a1dd8430..cf06e83b7 100644 --- a/packages/interface/src/components/Explorer/File/ThumbstripScrubber.tsx +++ b/packages/interface/src/components/Explorer/File/ThumbstripScrubber.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useEffect, memo } from "react"; import clsx from "clsx"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; interface ThumbstripScrubberProps { file: File; diff --git a/packages/interface/src/components/Explorer/File/Title.tsx b/packages/interface/src/components/Explorer/File/Title.tsx index 3efe403d8..609df1179 100644 --- a/packages/interface/src/components/Explorer/File/Title.tsx +++ b/packages/interface/src/components/Explorer/File/Title.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useRef } from "react"; import clsx from "clsx"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; interface TitleProps { file: File; diff --git a/packages/interface/src/components/Explorer/SelectionContext.tsx b/packages/interface/src/components/Explorer/SelectionContext.tsx index 47b560651..a77304aa2 100644 --- a/packages/interface/src/components/Explorer/SelectionContext.tsx +++ b/packages/interface/src/components/Explorer/SelectionContext.tsx @@ -1,6 +1,6 @@ import { createContext, useContext, useState, useCallback, useMemo, useEffect, type ReactNode } from "react"; import { usePlatform } from "../../platform"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; interface SelectionContextValue { selectedFiles: File[]; diff --git a/packages/interface/src/components/Explorer/SortMenu.tsx b/packages/interface/src/components/Explorer/SortMenu.tsx index 7c2efc278..cad6bd9f7 100644 --- a/packages/interface/src/components/Explorer/SortMenu.tsx +++ b/packages/interface/src/components/Explorer/SortMenu.tsx @@ -12,7 +12,7 @@ import { import { motion, AnimatePresence } from "framer-motion"; import clsx from "clsx"; import { TopBarButton } from "@sd/ui"; -import type { DirectorySortBy, MediaSortBy } from "@sd/ts-client/generated/types"; +import type { DirectorySortBy, MediaSortBy } from "@sd/ts-client"; interface SortMenuProps { sortBy: DirectorySortBy | MediaSortBy; diff --git a/packages/interface/src/components/Explorer/components/AddLocationModal.tsx b/packages/interface/src/components/Explorer/components/AddLocationModal.tsx index bcc2f057b..50b3252c2 100644 --- a/packages/interface/src/components/Explorer/components/AddLocationModal.tsx +++ b/packages/interface/src/components/Explorer/components/AddLocationModal.tsx @@ -15,7 +15,7 @@ import * as Tabs from "@sd/ui/Tabs"; import type { IndexMode, LocationAddInput, -} from "@sd/ts-client/generated/types"; +} from "@sd/ts-client"; import { useLibraryMutation, useLibraryQuery } from "../../../context"; import { usePlatform } from "../../../platform"; import { NewLocation } from "@sd/assets/icons"; diff --git a/packages/interface/src/components/Explorer/components/Breadcrumb.tsx b/packages/interface/src/components/Explorer/components/Breadcrumb.tsx index cae4ef5b7..b03123a8d 100644 --- a/packages/interface/src/components/Explorer/components/Breadcrumb.tsx +++ b/packages/interface/src/components/Explorer/components/Breadcrumb.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import type { SdPath } from "@sd/ts-client/generated/types"; +import type { SdPath } from "@sd/ts-client"; interface BreadcrumbProps { path: SdPath; diff --git a/packages/interface/src/components/Explorer/components/LocationsSection.tsx b/packages/interface/src/components/Explorer/components/LocationsSection.tsx index 7f9e0bf0a..c2e29fd7d 100644 --- a/packages/interface/src/components/Explorer/components/LocationsSection.tsx +++ b/packages/interface/src/components/Explorer/components/LocationsSection.tsx @@ -1,7 +1,7 @@ import { useNavigate, useParams } from "react-router-dom"; import { useRef, useEffect } from "react"; import { Plus } from "@phosphor-icons/react"; -import type { LocationInfo } from "@sd/ts-client/generated/types"; +import type { LocationInfo } from "@sd/ts-client"; import { useNormalizedCache } from "../../../context"; import { Section } from "./Section"; import { SidebarItem } from "./SidebarItem"; diff --git a/packages/interface/src/components/Explorer/components/PathBar.tsx b/packages/interface/src/components/Explorer/components/PathBar.tsx index fad58357b..a09bb3242 100644 --- a/packages/interface/src/components/Explorer/components/PathBar.tsx +++ b/packages/interface/src/components/Explorer/components/PathBar.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from "react"; import { motion } from "framer-motion"; import clsx from "clsx"; import { CaretRight } from "@phosphor-icons/react"; -import type { SdPath, LibraryDeviceInfo } from "@sd/ts-client/generated/types"; +import type { SdPath, LibraryDeviceInfo } from "@sd/ts-client"; import { getDeviceIconBySlug } from "@sd/ts-client"; import { sdPathToUri } from "../utils"; import LaptopIcon from "@sd/assets/icons/Laptop.png"; diff --git a/packages/interface/src/components/Explorer/context.tsx b/packages/interface/src/components/Explorer/context.tsx index b64677f4a..ccc9c511d 100644 --- a/packages/interface/src/components/Explorer/context.tsx +++ b/packages/interface/src/components/Explorer/context.tsx @@ -16,7 +16,7 @@ import type { LibraryDeviceInfo, DirectorySortBy, MediaSortBy, -} from "@sd/ts-client/generated/types"; +} from "@sd/ts-client"; interface ViewSettings { gridSize: number; // 80-400px diff --git a/packages/interface/src/components/Explorer/hooks/useExplorerKeyboard.ts b/packages/interface/src/components/Explorer/hooks/useExplorerKeyboard.ts index 6f754cbee..b0990b990 100644 --- a/packages/interface/src/components/Explorer/hooks/useExplorerKeyboard.ts +++ b/packages/interface/src/components/Explorer/hooks/useExplorerKeyboard.ts @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { useExplorer } from "../context"; import { useSelection } from "../SelectionContext"; import { useNormalizedCache } from "../../../context"; -import type { DirectorySortBy } from "@sd/ts-client/generated/types"; +import type { DirectorySortBy } from "@sd/ts-client"; export function useExplorerKeyboard() { const { currentPath, sortBy, setCurrentPath, viewMode, viewSettings, sidebarVisible, inspectorVisible, openQuickPreview } = useExplorer(); @@ -85,7 +85,7 @@ export function useExplorerKeyboard() { // Enter: Navigate into directory (for column view) if (e.key === "Enter" && selectedFiles.length === 1) { const selected = selectedFiles[0]; - if (selected.kind === "Directory") { + if (selected.kind.type === "Directory") { e.preventDefault(); setCurrentPath(selected.sd_path); } diff --git a/packages/interface/src/components/Explorer/utils.ts b/packages/interface/src/components/Explorer/utils.ts index 2ffc373ab..afc4a2197 100644 --- a/packages/interface/src/components/Explorer/utils.ts +++ b/packages/interface/src/components/Explorer/utils.ts @@ -2,7 +2,7 @@ import LaptopIcon from "@sd/assets/icons/Laptop.png"; import MobileIcon from "@sd/assets/icons/Mobile.png"; import ServerIcon from "@sd/assets/icons/Server.png"; import PCIcon from "@sd/assets/icons/PC.png"; -import type { SdPath } from "@sd/ts-client/generated/types"; +import type { SdPath } from "@sd/ts-client"; export function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; diff --git a/packages/interface/src/components/Explorer/views/ColumnView/Column.tsx b/packages/interface/src/components/Explorer/views/ColumnView/Column.tsx index c60cef25f..7fc337ab5 100644 --- a/packages/interface/src/components/Explorer/views/ColumnView/Column.tsx +++ b/packages/interface/src/components/Explorer/views/ColumnView/Column.tsx @@ -1,7 +1,7 @@ import { useRef, useMemo } from "react"; import { useVirtualizer } from "@tanstack/react-virtual"; import clsx from "clsx"; -import type { File, SdPath } from "@sd/ts-client/generated/types"; +import type { File, SdPath } from "@sd/ts-client"; import { useNormalizedCache } from "../../../../context"; import { ColumnItem } from "./ColumnItem"; import { useExplorer } from "../../context"; @@ -67,7 +67,7 @@ export function Column({ path, isActive, onNavigate }: ColumnProps) { icon: FolderOpen, label: "Open", onClick: (file: File) => { - if (file.kind === "Directory") { + if (file.kind.type === "Directory") { onNavigate(file.sd_path); } }, diff --git a/packages/interface/src/components/Explorer/views/ColumnView/ColumnItem.tsx b/packages/interface/src/components/Explorer/views/ColumnView/ColumnItem.tsx index b4870b853..acaeddba3 100644 --- a/packages/interface/src/components/Explorer/views/ColumnView/ColumnItem.tsx +++ b/packages/interface/src/components/Explorer/views/ColumnView/ColumnItem.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { File as FileComponent } from "../../File"; interface ColumnItemProps { @@ -40,7 +40,7 @@ export function ColumnItem({ > {file.name} - {file.kind === "Directory" && ( + {file.kind.type === "Directory" && ( { - if (selectedFiles.length === 1 && selectedFiles[0].kind === "Directory") { + if (selectedFiles.length === 1 && selectedFiles[0].kind.type === "Directory") { const selectedDir = selectedFiles[0]; const selectedPath = selectedDir.sd_path; diff --git a/packages/interface/src/components/Explorer/views/GridView/FileCard.tsx b/packages/interface/src/components/Explorer/views/GridView/FileCard.tsx index f0bb983e7..f2555db63 100644 --- a/packages/interface/src/components/Explorer/views/GridView/FileCard.tsx +++ b/packages/interface/src/components/Explorer/views/GridView/FileCard.tsx @@ -19,7 +19,7 @@ import { Crop, FileVideo, } from "@phosphor-icons/react"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { File as FileComponent } from "../../File"; import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; @@ -70,7 +70,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele icon: FolderOpen, label: "Open", onClick: () => { - if (file.kind === "Directory") { + if (file.kind.type === "Directory") { setCurrentPath(file.sd_path); } else { console.log("Open file:", file.name); @@ -78,7 +78,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele } }, keybind: "⌘O", - condition: () => file.kind === "Directory" || file.kind === "File", + condition: () => file.kind.type === "Directory" || file.kind.type === "File", }, { icon: MagnifyingGlass, @@ -308,7 +308,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele type: "submenu", icon: FileText, label: "Document Processing", - condition: () => ["pdf", "doc", "docx"].includes(file.extension || ""), + condition: () => ["pdf", "doc", "docx"].includes(file.kind.type === "File" ? file.kind.data?.extension || "" : ""), submenu: [ { icon: TextAa, @@ -425,7 +425,7 @@ export const FileCard = memo(function FileCard({ file, fileIndex, allFiles, sele }; const handleDoubleClick = () => { - if (file.kind === "Directory") { + if (file.kind.type === "Directory") { setCurrentPath(file.sd_path); } }; diff --git a/packages/interface/src/components/Explorer/views/GridView/GridView.tsx b/packages/interface/src/components/Explorer/views/GridView/GridView.tsx index f05cf5dbb..a2d9d1e5b 100644 --- a/packages/interface/src/components/Explorer/views/GridView/GridView.tsx +++ b/packages/interface/src/components/Explorer/views/GridView/GridView.tsx @@ -2,7 +2,7 @@ import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; import { useNormalizedCache } from "../../../../context"; import { FileCard } from "./FileCard"; -import type { DirectorySortBy } from "@sd/ts-client/generated/types"; +import type { DirectorySortBy } from "@sd/ts-client"; export function GridView() { const { currentPath, sortBy, viewSettings } = useExplorer(); diff --git a/packages/interface/src/components/Explorer/views/ListView/FileRow.tsx b/packages/interface/src/components/Explorer/views/ListView/FileRow.tsx index d61ef79e6..43dc8042b 100644 --- a/packages/interface/src/components/Explorer/views/ListView/FileRow.tsx +++ b/packages/interface/src/components/Explorer/views/ListView/FileRow.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { File as FileComponent } from "../../File"; import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; @@ -24,7 +24,7 @@ export function FileRow({ file, fileIndex, allFiles }: FileRowProps) { }; const handleDoubleClick = () => { - if (file.kind === "Directory") { + if (file.kind.type === "Directory") { setCurrentPath(file.sd_path); } }; @@ -60,7 +60,7 @@ export function FileRow({ file, fileIndex, allFiles }: FileRowProps) { {formatRelativeTime(file.modified_at)}
- {file.extension || "Folder"} + {file.kind.type === "File" ? file.kind.data?.extension || "—" : "Folder"}
); diff --git a/packages/interface/src/components/Explorer/views/ListView/ListView.tsx b/packages/interface/src/components/Explorer/views/ListView/ListView.tsx index af57455ed..b255d359a 100644 --- a/packages/interface/src/components/Explorer/views/ListView/ListView.tsx +++ b/packages/interface/src/components/Explorer/views/ListView/ListView.tsx @@ -1,7 +1,7 @@ import { useExplorer } from "../../context"; import { useNormalizedCache } from "../../../../context"; import { FileRow } from "./FileRow"; -import type { DirectorySortBy } from "@sd/ts-client/generated/types"; +import type { DirectorySortBy } from "@sd/ts-client"; export function ListView() { const { currentPath, sortBy } = useExplorer(); diff --git a/packages/interface/src/components/Explorer/views/MediaView/MediaViewItem.tsx b/packages/interface/src/components/Explorer/views/MediaView/MediaViewItem.tsx index 7f1ef683f..229205a21 100644 --- a/packages/interface/src/components/Explorer/views/MediaView/MediaViewItem.tsx +++ b/packages/interface/src/components/Explorer/views/MediaView/MediaViewItem.tsx @@ -17,7 +17,7 @@ import { Crop, FileVideo, } from "@phosphor-icons/react"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { File as FileComponent } from "../../File"; import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; diff --git a/packages/interface/src/components/Explorer/views/MediaView/utils.ts b/packages/interface/src/components/Explorer/views/MediaView/utils.ts index 0cfb4f850..3e728437e 100644 --- a/packages/interface/src/components/Explorer/views/MediaView/utils.ts +++ b/packages/interface/src/components/Explorer/views/MediaView/utils.ts @@ -1,4 +1,4 @@ -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; export function formatDate( date: Date | { from: Date; to: Date }, diff --git a/packages/interface/src/components/Explorer/views/SizeView/SizeCircle.tsx b/packages/interface/src/components/Explorer/views/SizeView/SizeCircle.tsx index 235c9da77..e3ae1509d 100644 --- a/packages/interface/src/components/Explorer/views/SizeView/SizeCircle.tsx +++ b/packages/interface/src/components/Explorer/views/SizeView/SizeCircle.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { formatBytes } from "../../utils"; interface SizeCircleProps { @@ -11,7 +11,7 @@ interface SizeCircleProps { // Get file extension or type function getFileType(file: File): string { - if (file.kind === "Directory") return "Folder"; + if (file.kind.type === "Directory") return "Folder"; const name = file.name; const lastDot = name.lastIndexOf("."); @@ -22,7 +22,7 @@ function getFileType(file: File): string { // Get color based on file type function getFileColor(file: File): string { - if (file.kind === "Directory") return "bg-blue-500"; + if (file.kind.type === "Directory") return "bg-blue-500"; const ext = file.name.split(".").pop()?.toLowerCase() || ""; diff --git a/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx b/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx index cf0f64574..f60478273 100644 --- a/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx +++ b/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useMemo, useState } from "react"; import * as d3 from "d3"; -import type { File, DirectorySortBy } from "@sd/ts-client/generated/types"; +import type { File, DirectorySortBy } from "@sd/ts-client"; import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; import { useNormalizedCache } from "../../../../context"; @@ -29,7 +29,7 @@ function getTailwindColor(className: string): string { } function getFileColorClass(file: File): string { - if (file.kind === "Directory") return "bg-accent"; + if (file.kind.type === "Directory") return "bg-accent"; const ext = file.name.split(".").pop()?.toLowerCase() || ""; @@ -71,7 +71,7 @@ function getFileColor(file: File): string { } function getFileType(file: File): string { - if (file.kind === "Directory") return "Folder"; + if (file.kind.type === "Directory") return "Folder"; const name = file.name; const lastDot = name.lastIndexOf("."); @@ -348,7 +348,7 @@ export function SizeView() { } // Navigate if directory - if (d.data.file.kind === "Directory") { + if (d.data.file.kind.type === "Directory") { setCurrentPathRef.current(d.data.file.sd_path); } }) diff --git a/packages/interface/src/components/JobManager/components/JobProgressBar.tsx b/packages/interface/src/components/JobManager/components/JobProgressBar.tsx index d9cd37877..a3d829377 100644 --- a/packages/interface/src/components/JobManager/components/JobProgressBar.tsx +++ b/packages/interface/src/components/JobManager/components/JobProgressBar.tsx @@ -1,4 +1,4 @@ -import type { JobStatus } from "@sd/ts-client/generated/types"; +import type { JobStatus } from "@sd/ts-client"; import { JOB_STATUS_COLORS, PROGRESS_BAR_HEIGHT } from "../types"; interface JobProgressBarProps { diff --git a/packages/interface/src/components/JobManager/components/JobStatusIndicator.tsx b/packages/interface/src/components/JobManager/components/JobStatusIndicator.tsx index b70a287de..efd456541 100644 --- a/packages/interface/src/components/JobManager/components/JobStatusIndicator.tsx +++ b/packages/interface/src/components/JobManager/components/JobStatusIndicator.tsx @@ -1,4 +1,4 @@ -import type { JobStatus } from "@sd/ts-client/generated/types"; +import type { JobStatus } from "@sd/ts-client"; import { MagnifyingGlass, Image, diff --git a/packages/interface/src/components/JobManager/types.ts b/packages/interface/src/components/JobManager/types.ts index d0e234a52..7b21c48ef 100644 --- a/packages/interface/src/components/JobManager/types.ts +++ b/packages/interface/src/components/JobManager/types.ts @@ -1,4 +1,4 @@ -import type { JobStatus, JobListItem as GeneratedJobListItem, JsonValue, SdPath } from "@sd/ts-client/generated/types"; +import type { JobStatus, JobListItem as GeneratedJobListItem, JsonValue, SdPath } from "@sd/ts-client"; // Extend the generated type with runtime fields from JobProgress events export type JobListItem = GeneratedJobListItem & { diff --git a/packages/interface/src/components/QuickPreview/AudioPlayer.tsx b/packages/interface/src/components/QuickPreview/AudioPlayer.tsx index fe0409410..7a178a9f3 100644 --- a/packages/interface/src/components/QuickPreview/AudioPlayer.tsx +++ b/packages/interface/src/components/QuickPreview/AudioPlayer.tsx @@ -8,7 +8,7 @@ import { SkipForward, } from "@phosphor-icons/react"; import { motion, AnimatePresence } from "framer-motion"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { File as FileComponent } from "../Explorer/File"; import { formatBytes } from "../Explorer/utils"; diff --git a/packages/interface/src/components/QuickPreview/ContentRenderer.tsx b/packages/interface/src/components/QuickPreview/ContentRenderer.tsx index cdca02758..4d7cbb2c2 100644 --- a/packages/interface/src/components/QuickPreview/ContentRenderer.tsx +++ b/packages/interface/src/components/QuickPreview/ContentRenderer.tsx @@ -1,4 +1,4 @@ -import type { File, ContentKind } from "@sd/ts-client/generated/types"; +import type { File, ContentKind } from "@sd/ts-client"; import { File as FileComponent } from "../Explorer/File"; import { formatBytes } from "../Explorer/utils"; import { usePlatform } from "../../platform"; @@ -295,7 +295,7 @@ function DefaultRenderer({ file }: ContentRendererProps) { export function ContentRenderer({ file }: ContentRendererProps) { // Handle directories first - if (file.kind === "Directory") { + if (file.kind.type === "Directory") { return (
Folder Icon diff --git a/packages/interface/src/components/QuickPreview/QuickPreview.tsx b/packages/interface/src/components/QuickPreview/QuickPreview.tsx index cb585674f..273209573 100644 --- a/packages/interface/src/components/QuickPreview/QuickPreview.tsx +++ b/packages/interface/src/components/QuickPreview/QuickPreview.tsx @@ -1,6 +1,6 @@ import { useLibraryQuery } from "../../context"; import { usePlatform } from "../../platform"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { useEffect, useState } from "react"; import { formatBytes } from "../Explorer/utils"; import { X } from "@phosphor-icons/react"; diff --git a/packages/interface/src/components/QuickPreview/QuickPreviewModal.tsx b/packages/interface/src/components/QuickPreview/QuickPreviewModal.tsx index 4a4bad145..9898360cc 100644 --- a/packages/interface/src/components/QuickPreview/QuickPreviewModal.tsx +++ b/packages/interface/src/components/QuickPreview/QuickPreviewModal.tsx @@ -1,7 +1,7 @@ import { motion, AnimatePresence } from 'framer-motion'; import { X, ArrowLeft, ArrowRight } from '@phosphor-icons/react'; import { useEffect } from 'react'; -import type { File } from '@sd/ts-client/generated/types'; +import type { File } from '@sd/ts-client'; import { useLibraryQuery } from '../../context'; import { Inspector } from '../../Inspector'; import { ContentRenderer } from './ContentRenderer'; diff --git a/packages/interface/src/components/QuickPreview/Subtitles.tsx b/packages/interface/src/components/QuickPreview/Subtitles.tsx index f2a8bfa33..4e11e2b6f 100644 --- a/packages/interface/src/components/QuickPreview/Subtitles.tsx +++ b/packages/interface/src/components/QuickPreview/Subtitles.tsx @@ -1,5 +1,5 @@ import { useEffect, useState, useRef } from 'react'; -import type { File } from '@sd/ts-client/generated/types'; +import type { File } from '@sd/ts-client'; interface SubtitleCue { index: number; diff --git a/packages/interface/src/components/QuickPreview/TimelineScrubber.tsx b/packages/interface/src/components/QuickPreview/TimelineScrubber.tsx index c8a65f648..525adfa1c 100644 --- a/packages/interface/src/components/QuickPreview/TimelineScrubber.tsx +++ b/packages/interface/src/components/QuickPreview/TimelineScrubber.tsx @@ -1,5 +1,5 @@ import { memo } from 'react'; -import type { File } from '@sd/ts-client/generated/types'; +import type { File } from '@sd/ts-client'; interface TimelineScrubberProps { file: File; diff --git a/packages/interface/src/components/QuickPreview/VideoPlayer.tsx b/packages/interface/src/components/QuickPreview/VideoPlayer.tsx index a0e086671..2d587588f 100644 --- a/packages/interface/src/components/QuickPreview/VideoPlayer.tsx +++ b/packages/interface/src/components/QuickPreview/VideoPlayer.tsx @@ -1,7 +1,7 @@ import { useState, useRef, useEffect } from 'react'; import { Play, Pause, SpeakerHigh, SpeakerSlash, ArrowsOut, ClosedCaptioning, MagnifyingGlassPlus, MagnifyingGlassMinus, ArrowCounterClockwise, Gear } from '@phosphor-icons/react'; import { motion, AnimatePresence } from 'framer-motion'; -import type { File } from '@sd/ts-client/generated/types'; +import type { File } from '@sd/ts-client'; import { Subtitles, type SubtitleSettings } from './Subtitles'; import { SubtitleSettingsMenu } from './SubtitleSettingsMenu'; import { useZoomPan } from './useZoomPan'; diff --git a/packages/interface/src/components/SpacesSidebar/AddGroupModal.tsx b/packages/interface/src/components/SpacesSidebar/AddGroupModal.tsx index a7d519083..d5c4ea034 100644 --- a/packages/interface/src/components/SpacesSidebar/AddGroupModal.tsx +++ b/packages/interface/src/components/SpacesSidebar/AddGroupModal.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { Input, Label, dialogManager, useDialog, Dialog } from '@sd/ui'; import { useLibraryMutation } from '@sd/ts-client'; import { useForm } from 'react-hook-form'; -import type { GroupType } from '@sd/ts-client/generated/types'; +import type { GroupType } from '@sd/ts-client'; interface FormData { groupName: string; diff --git a/packages/interface/src/components/SpacesSidebar/DeviceGroup.tsx b/packages/interface/src/components/SpacesSidebar/DeviceGroup.tsx index 15a54cc10..19b283cb6 100644 --- a/packages/interface/src/components/SpacesSidebar/DeviceGroup.tsx +++ b/packages/interface/src/components/SpacesSidebar/DeviceGroup.tsx @@ -1,6 +1,6 @@ import { CaretRight, Desktop } from '@phosphor-icons/react'; import clsx from 'clsx'; -import type { SpaceItem as SpaceItemType } from '@sd/ts-client/generated/types'; +import type { SpaceItem as SpaceItemType } from '@sd/ts-client'; import { SpaceItem } from './SpaceItem'; interface DeviceGroupProps { diff --git a/packages/interface/src/components/SpacesSidebar/SpaceGroup.tsx b/packages/interface/src/components/SpacesSidebar/SpaceGroup.tsx index 044c5e4c6..51d6fa0ff 100644 --- a/packages/interface/src/components/SpacesSidebar/SpaceGroup.tsx +++ b/packages/interface/src/components/SpacesSidebar/SpaceGroup.tsx @@ -4,7 +4,7 @@ import type { SpaceGroup as SpaceGroupType, SpaceItem as SpaceItemType, GroupType, -} from '@sd/ts-client/generated/types'; +} from '@sd/ts-client'; import { useSidebarStore } from '@sd/ts-client/stores/sidebar'; import { SpaceItem } from './SpaceItem'; import { DeviceGroup } from './DeviceGroup'; diff --git a/packages/interface/src/components/SpacesSidebar/SpaceItem.tsx b/packages/interface/src/components/SpacesSidebar/SpaceItem.tsx index a4d638546..0d881bceb 100644 --- a/packages/interface/src/components/SpacesSidebar/SpaceItem.tsx +++ b/packages/interface/src/components/SpacesSidebar/SpaceItem.tsx @@ -12,7 +12,7 @@ import { Location } from "@sd/assets/icons"; import type { SpaceItem as SpaceItemType, ItemType, -} from "@sd/ts-client/generated/types"; +} from "@sd/ts-client"; interface SpaceItemProps { item: SpaceItemType; diff --git a/packages/interface/src/components/SpacesSidebar/SpaceSwitcher.tsx b/packages/interface/src/components/SpacesSidebar/SpaceSwitcher.tsx index 7505ffb1e..182d261bc 100644 --- a/packages/interface/src/components/SpacesSidebar/SpaceSwitcher.tsx +++ b/packages/interface/src/components/SpacesSidebar/SpaceSwitcher.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import { CaretDown, Plus, GearSix } from '@phosphor-icons/react'; import { DropdownMenu } from '@sd/ui'; -import type { Space } from '@sd/ts-client/generated/types'; +import type { Space } from '@sd/ts-client'; import { useCreateSpaceDialog } from './CreateSpaceModal'; interface SpaceSwitcherProps { diff --git a/packages/interface/src/components/SpacesSidebar/VolumesGroup.tsx b/packages/interface/src/components/SpacesSidebar/VolumesGroup.tsx index 95fa604fb..ebd954cff 100644 --- a/packages/interface/src/components/SpacesSidebar/VolumesGroup.tsx +++ b/packages/interface/src/components/SpacesSidebar/VolumesGroup.tsx @@ -2,7 +2,7 @@ import { CaretRight } from "@phosphor-icons/react"; import clsx from "clsx"; import { useNormalizedCache } from "@sd/ts-client"; import { SpaceItem } from "./SpaceItem"; -import type { VolumeItem } from "@sd/ts-client/generated/types"; +import type { VolumeItem } from "@sd/ts-client"; interface VolumesGroupProps { isCollapsed: boolean; diff --git a/packages/interface/src/components/SyncSetupModal.tsx b/packages/interface/src/components/SyncSetupModal.tsx index 8f843aa52..d7d995f48 100644 --- a/packages/interface/src/components/SyncSetupModal.tsx +++ b/packages/interface/src/components/SyncSetupModal.tsx @@ -8,7 +8,7 @@ import { Share, SignIn } from '@phosphor-icons/react'; -import type { LibrarySyncAction, PairedDeviceInfo, RemoteLibraryInfo } from '@sd/ts-client/generated/types'; +import type { LibrarySyncAction, PairedDeviceInfo, RemoteLibraryInfo } from '@sd/ts-client'; import { Button, Dialog, dialogManager, useDialog } from '@sd/ui'; import { useCoreQuery, useCoreMutation, useSpacedriveClient } from '../context'; diff --git a/packages/interface/src/context.tsx b/packages/interface/src/context.tsx index f33a14ec5..6e8793eda 100644 --- a/packages/interface/src/context.tsx +++ b/packages/interface/src/context.tsx @@ -28,4 +28,4 @@ export type { LocationInfo, LocationsListOutput, LibraryInfo, -} from "@sd/ts-client/generated/types"; \ No newline at end of file +} from "@sd/ts-client"; \ No newline at end of file diff --git a/packages/interface/src/hooks/useJobDispatch.ts b/packages/interface/src/hooks/useJobDispatch.ts index 5369f1305..01524821f 100644 --- a/packages/interface/src/hooks/useJobDispatch.ts +++ b/packages/interface/src/hooks/useJobDispatch.ts @@ -58,3 +58,4 @@ export function useJobDispatch() { + diff --git a/packages/interface/src/inspectors/FileInspector.tsx b/packages/interface/src/inspectors/FileInspector.tsx index 2c52eb204..4794c5d55 100644 --- a/packages/interface/src/inspectors/FileInspector.tsx +++ b/packages/interface/src/inspectors/FileInspector.tsx @@ -1,38 +1,38 @@ import { - Info, - Tag as TagIcon, - Calendar, - HardDrive, - Hash, - Fingerprint, - Palette, - Image, - ClockCounterClockwise, - DotsThree, - MapPin, - ChatCircle, - PaperPlaneRight, - Paperclip, - Sparkle, - TextAa, - Microphone, - ArrowsClockwise, - MagnifyingGlass, - Trash, - FilmStrip, - VideoCamera, + Info, + Tag as TagIcon, + Calendar, + HardDrive, + Hash, + Fingerprint, + Palette, + Image, + ClockCounterClockwise, + DotsThree, + MapPin, + ChatCircle, + PaperPlaneRight, + Paperclip, + Sparkle, + TextAa, + Microphone, + ArrowsClockwise, + MagnifyingGlass, + Trash, + FilmStrip, + VideoCamera, } from "@phosphor-icons/react"; import { useState } from "react"; import { - InfoRow, - Tag, - Section, - Divider, - Tabs, - TabContent, + InfoRow, + Tag, + Section, + Divider, + Tabs, + TabContent, } from "../components/Inspector"; import clsx from "clsx"; -import type { File } from "@sd/ts-client/generated/types"; +import type { File } from "@sd/ts-client"; import { useNormalizedCache, useLibraryMutation } from "../context"; import { formatBytes } from "../components/Explorer/utils"; import { File as FileComponent } from "../components/Explorer/File"; @@ -40,1114 +40,994 @@ import { useContextMenu } from "../hooks/useContextMenu"; import { usePlatform } from "../platform"; interface FileInspectorProps { - file: File; + file: File; } export function FileInspector({ file }: FileInspectorProps) { - const [activeTab, setActiveTab] = useState("overview"); + const [activeTab, setActiveTab] = useState("overview"); - const fileQuery = useNormalizedCache<{ file_id: string }, File>({ - wireMethod: "query:files.by_id", - input: { file_id: file?.id || "" }, - resourceType: "file", - resourceId: file?.id, // Filter batch events to only this file - enabled: !!file?.id, - }); + const fileQuery = useNormalizedCache<{ file_id: string }, File>({ + wireMethod: "query:files.by_id", + input: { file_id: file?.id || "" }, + resourceType: "file", + resourceId: file?.id, // Filter batch events to only this file + enabled: !!file?.id, + }); - const fileData = fileQuery.data || file; + const fileData = fileQuery.data || file; - const tabs = [ - { id: "overview", label: "Overview", icon: Info }, - { id: "sidecars", label: "Sidecars", icon: Image }, - { id: "instances", label: "Instances", icon: MapPin }, - { id: "chat", label: "Chat", icon: ChatCircle, badge: 3 }, - { id: "activity", label: "Activity", icon: ClockCounterClockwise }, - { id: "details", label: "More", icon: DotsThree }, - ]; + const tabs = [ + { id: "overview", label: "Overview", icon: Info }, + { id: "sidecars", label: "Sidecars", icon: Image }, + { id: "instances", label: "Instances", icon: MapPin }, + { id: "chat", label: "Chat", icon: ChatCircle, badge: 3 }, + { id: "activity", label: "Activity", icon: ClockCounterClockwise }, + { id: "details", label: "More", icon: DotsThree }, + ]; - return ( - <> - {/* Tabs */} - + return ( + <> + {/* Tabs */} + - {/* Tab Content */} -
- - - + {/* Tab Content */} +
+ + + - - - + + + - - - + + + - - - + + + - - - + + + - - - -
- - ); + + + +
+ + ); } function OverviewTab({ file }: { file: File }) { - const formatDate = (dateStr: string) => { - const date = new Date(dateStr); - return date.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - year: "numeric", - }); - }; + const formatDate = (dateStr: string) => { + const date = new Date(dateStr); + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); + }; - // AI Processing mutations - const extractText = useLibraryMutation("media.ocr.extract"); - const transcribeAudio = useLibraryMutation("media.speech.transcribe"); - const regenerateThumbnail = useLibraryMutation( - "media.thumbnail.regenerate", - ); - const generateThumbstrip = useLibraryMutation("media.thumbstrip.generate"); - const generateProxy = useLibraryMutation("media.proxy.generate"); + // AI Processing mutations + const extractText = useLibraryMutation("media.ocr.extract"); + const transcribeAudio = useLibraryMutation("media.speech.transcribe"); + const regenerateThumbnail = useLibraryMutation("media.thumbnail.regenerate"); + const generateThumbstrip = useLibraryMutation("media.thumbstrip.generate"); + const generateProxy = useLibraryMutation("media.proxy.generate"); - // Check content kind for available actions - const isImage = file?.content_identity?.kind === "image"; - const isVideo = file?.content_identity?.kind === "video"; - const isAudio = file?.content_identity?.kind === "audio"; - const hasText = file?.content_identity?.text_content; + // Check content kind for available actions + const isImage = file?.content_identity?.kind === "image"; + const isVideo = file?.content_identity?.kind === "video"; + const isAudio = file?.content_identity?.kind === "audio"; + const hasText = file?.content_identity?.text_content; - return ( -
- {/* Thumbnail */} -
- -
+ const fileKind = + file?.content_identity?.kind && file.content_identity.kind !== "unknown" + ? file.content_identity.kind + : file.kind === "File" + ? file.extension || "File" + : file.kind; - {/* File name */} -
-

- {file.name} -

-

- {String(file.content_kind || file.kind)} -

-
+ return ( +
+ {/* Thumbnail */} +
+ +
- + {/* File name */} +
+

+ {file.name} +

+

{fileKind}

+
- {/* Details */} -
- - {file.extension && ( - - )} - -
+ - {/* Dates */} -
- {/* Show capture date for media files */} - {file.video_media_data?.date_captured && ( - - )} - {file.image_media_data?.date_taken && ( - - )} - - - {file.accessed_at && ( - - )} -
+ {/* Details */} +
+ + {file.kind === "File" && file.extension && ( + + )} + +
- {/* Image Metadata */} - {file.image_media_data && ( -
- - {file.image_media_data.camera_make && ( - - )} - {file.image_media_data.lens_model && ( - - )} - {file.image_media_data.iso && ( - - )} - {file.image_media_data.focal_length && ( - - )} - {file.image_media_data.aperture && ( - - )} - {file.image_media_data.shutter_speed && ( - - )} -
- )} + {/* Dates */} +
+ {/* Show capture date for media files */} + {file.video_media_data?.date_captured && ( + + )} + {file.image_media_data?.date_taken && ( + + )} + + + {file.accessed_at && ( + + )} +
- {/* Video Metadata */} - {file.video_media_data && ( -
- - {file.video_media_data.duration_seconds && ( - - )} - {file.video_media_data.codec && ( - - )} - {file.video_media_data.bit_rate && ( - - )} - {file.video_media_data.fps_num && - file.video_media_data.fps_den && - file.video_media_data.fps_den !== 0 && ( - - )} - {file.video_media_data.audio_codec && ( - - )} -
- )} + {/* Image Metadata */} + {file.image_media_data && ( +
+ + {file.image_media_data.camera_make && ( + + )} + {file.image_media_data.lens_model && ( + + )} + {file.image_media_data.iso && ( + + )} + {file.image_media_data.focal_length && ( + + )} + {file.image_media_data.aperture && ( + + )} + {file.image_media_data.shutter_speed && ( + + )} +
+ )} - {/* Audio Metadata */} - {file.audio_media_data && ( -
- {file.audio_media_data.artist && ( - - )} - {file.audio_media_data.album && ( - - )} - {file.audio_media_data.title && ( - - )} - {file.audio_media_data.duration_seconds && ( - - )} - {file.audio_media_data.codec && ( - - )} - {file.audio_media_data.bit_rate && ( - - )} - {file.audio_media_data.genre && ( - - )} - {file.audio_media_data.year && ( - - )} -
- )} + {/* Video Metadata */} + {file.video_media_data && ( +
+ + {file.video_media_data.duration_seconds && ( + + )} + {file.video_media_data.codec && ( + + )} + {file.video_media_data.bit_rate && ( + + )} + {file.video_media_data.fps_num && + file.video_media_data.fps_den && + file.video_media_data.fps_den !== 0 && ( + + )} + {file.video_media_data.audio_codec && ( + + )} +
+ )} - {/* Storage */} -
- - -
+ {/* Audio Metadata */} + {file.audio_media_data && ( +
+ {file.audio_media_data.artist && ( + + )} + {file.audio_media_data.album && ( + + )} + {file.audio_media_data.title && ( + + )} + {file.audio_media_data.duration_seconds && ( + + )} + {file.audio_media_data.codec && ( + + )} + {file.audio_media_data.bit_rate && ( + + )} + {file.audio_media_data.genre && ( + + )} + {file.audio_media_data.year && ( + + )} +
+ )} - {/* Tags */} - {file.tags && file.tags.length > 0 && ( -
-
- {file.tags.map((tag) => ( - - {tag.canonical_name} - - ))} -
-
- )} + {/* Storage */} +
+ + +
- {/* AI Processing */} - {(isImage || isVideo || isAudio) && ( -
-
- {/* OCR for images */} - {isImage && ( - - )} + {/* Tags */} + {file.tags && file.tags.length > 0 && ( +
+
+ {file.tags.map((tag) => ( + + {tag.canonical_name} + + ))} +
+
+ )} - {/* Speech-to-text for audio/video */} - {(isVideo || isAudio) && ( - - )} + {/* AI Processing */} + {(isImage || isVideo || isAudio) && ( +
+
+ {/* OCR for images */} + {isImage && ( + + )} - {/* Regenerate thumbnails */} - {(isImage || isVideo) && ( - - )} + {/* Speech-to-text for audio/video */} + {(isVideo || isAudio) && ( + + )} - {/* Generate thumbstrip (for videos) */} - {isVideo && ( - - )} + {/* Regenerate thumbnails */} + {(isImage || isVideo) && ( + + )} - {/* Generate proxy (for videos) */} - {isVideo && ( - - )} + {/* Generate thumbstrip (for videos) */} + {isVideo && ( + + )} - {/* Show extracted text if available */} - {hasText && ( -
-
- - - Extracted Text - -
-
-									{file.content_identity.text_content}
-								
-
- )} -
-
- )} -
- ); + {/* Generate proxy (for videos) */} + {isVideo && ( + + )} + + {/* Show extracted text if available */} + {hasText && ( +
+
+ + + + + Extracted Text + +
+
+                  {file.content_identity.text_content}
+                
+
+ )} +
+ + )} +
+ ); } function SidecarsTab({ file }: { file: File }) { - const sidecars = file.sidecars || []; - const platform = usePlatform(); + const sidecars = file.sidecars || []; + const platform = usePlatform(); - // Helper to get sidecar URL - const getSidecarUrl = (sidecar: any) => { - if (typeof window === "undefined") return null; - const serverUrl = (window as any).__SPACEDRIVE_SERVER_URL__; - const libraryId = (window as any).__SPACEDRIVE_LIBRARY_ID__; + // Helper to get sidecar URL + const getSidecarUrl = (sidecar: any) => { + if (typeof window === "undefined") return null; + const serverUrl = (window as any).__SPACEDRIVE_SERVER_URL__; + const libraryId = (window as any).__SPACEDRIVE_LIBRARY_ID__; - if (!serverUrl || !libraryId || !file.content_identity) return null; + if (!serverUrl || !libraryId || !file.content_identity) return null; - const contentUuid = file.content_identity.uuid; - return `${serverUrl}/sidecar/${libraryId}/${contentUuid}/${sidecar.kind}/${sidecar.variant}.${sidecar.format}`; - }; + const contentUuid = file.content_identity.uuid; + return `${serverUrl}/sidecar/${libraryId}/${contentUuid}/${sidecar.kind}/${sidecar.variant}.${sidecar.format}`; + }; - return ( -
-

- Derivative files and associated content generated by Spacedrive -

+ return ( +
+

+ Derivative files and associated content generated by Spacedrive +

- {sidecars.length === 0 ? ( -
- No sidecars generated yet -
- ) : ( -
- {sidecars.map((sidecar, i) => ( - - ))} -
- )} -
- ); + {sidecars.length === 0 ? ( +
+ No sidecars generated yet +
+ ) : ( +
+ {sidecars.map((sidecar, i) => ( + + ))} +
+ )} +
+ ); } function SidecarItem({ - sidecar, - file, - sidecarUrl, - platform, + sidecar, + file, + sidecarUrl, + platform, }: { - sidecar: any; - file: File; - sidecarUrl: string | null; - platform: ReturnType; + sidecar: any; + file: File; + sidecarUrl: string | null; + platform: ReturnType; }) { - const isImage = - (sidecar.kind === "thumb" || sidecar.kind === "thumbstrip") && - (sidecar.format === "webp" || - sidecar.format === "jpg" || - sidecar.format === "png"); + const isImage = + (sidecar.kind === "thumb" || sidecar.kind === "thumbstrip") && + (sidecar.format === "webp" || + sidecar.format === "jpg" || + sidecar.format === "png"); - const contextMenu = useContextMenu({ - items: [ - { - icon: MagnifyingGlass, - label: "Show in Finder", - onClick: async () => { - if ( - platform.getSidecarPath && - platform.revealFile && - file.content_identity - ) { - try { - const libraryId = (window as any) - .__SPACEDRIVE_LIBRARY_ID__; - if (!libraryId) { - alert("Library ID not found"); - return; - } + const contextMenu = useContextMenu({ + items: [ + { + icon: MagnifyingGlass, + label: "Show in Finder", + onClick: async () => { + if ( + platform.getSidecarPath && + platform.revealFile && + file.content_identity + ) { + try { + const libraryId = (window as any).__SPACEDRIVE_LIBRARY_ID__; + if (!libraryId) { + alert("Library ID not found"); + return; + } - const sidecarPath = await platform.getSidecarPath( - libraryId, - file.content_identity.uuid, - sidecar.kind, - sidecar.variant, - sidecar.format, - ); + const sidecarPath = await platform.getSidecarPath( + libraryId, + file.content_identity.uuid, + sidecar.kind, + sidecar.variant, + sidecar.format, + ); - await platform.revealFile(sidecarPath); - } catch (err) { - console.error("Failed to reveal sidecar:", err); - alert(`Failed to reveal sidecar: ${err}`); - } - } - }, - condition: () => - !!platform.getSidecarPath && - !!platform.revealFile && - !!file.content_identity, - }, - { - icon: Trash, - label: "Delete Sidecar", - onClick: () => { - console.log("Delete sidecar:", sidecar); - // TODO: Implement sidecar deletion - }, - variant: "danger" as const, - }, - ], - }); + await platform.revealFile(sidecarPath); + } catch (err) { + console.error("Failed to reveal sidecar:", err); + alert(`Failed to reveal sidecar: ${err}`); + } + } + }, + condition: () => + !!platform.getSidecarPath && + !!platform.revealFile && + !!file.content_identity, + }, + { + icon: Trash, + label: "Delete Sidecar", + onClick: () => { + console.log("Delete sidecar:", sidecar); + // TODO: Implement sidecar deletion + }, + variant: "danger" as const, + }, + ], + }); - const handleContextMenu = async (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - await contextMenu.show(e); - }; + const handleContextMenu = async (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + await contextMenu.show(e); + }; - return ( -
- {/* Preview thumbnail for image sidecars */} - {isImage && sidecarUrl ? ( -
- {`${sidecar.variant} { - // Fallback to icon on error - e.currentTarget.style.display = "none"; - if (e.currentTarget.nextElementSibling) { - ( - e.currentTarget - .nextElementSibling as HTMLElement - ).style.display = "flex"; - } - }} - /> -
- -
-
- ) : ( -
- -
- )} + return ( +
+ {/* Preview thumbnail for image sidecars */} + {isImage && sidecarUrl ? ( +
+ {`${sidecar.variant} { + // Fallback to icon on error + e.currentTarget.style.display = "none"; + if (e.currentTarget.nextElementSibling) { + ( + e.currentTarget.nextElementSibling as HTMLElement + ).style.display = "flex"; + } + }} + /> +
+ +
+
+ ) : ( +
+ +
+ )} -
-
- {String(sidecar.kind)} -
-
- {String(sidecar.variant)} · {formatBytes(sidecar.size)} -
-
- {String(sidecar.format).toUpperCase()} -
-
- - {String(sidecar.status)} - -
- ); +
+
+ {String(sidecar.kind)} +
+
+ {String(sidecar.variant)} · {formatBytes(sidecar.size)} +
+
+ {String(sidecar.format).toUpperCase()} +
+
+ + {String(sidecar.status)} + +
+ ); } function InstancesTab({ file }: { file: File }) { - const alternatePaths = file.alternate_paths || []; - const allPaths = [file.sd_path, ...alternatePaths]; + const alternatePaths = file.alternate_paths || []; + const allPaths = [file.sd_path, ...alternatePaths]; - const getPathDisplay = (sdPath: typeof file.sd_path) => { - if (sdPath.type === "Physical") { - return sdPath.path; - } else if (sdPath.type === "Cloud") { - return `${sdPath.volume_fingerprint}/${sdPath.path}`; - } else { - return `content://${sdPath.content_id}`; - } - }; + const getPathDisplay = (sdPath: typeof file.sd_path) => { + if ("Physical" in sdPath) { + return sdPath.Physical.path; + } else if ("Cloud" in sdPath) { + return sdPath.Cloud.path; + } else { + return "Content"; + } + }; - return ( -
-

- All copies of this file across your devices and locations -

+ return ( +
+

+ All copies of this file across your devices and locations +

- {allPaths.length === 1 ? ( -
- No alternate instances found -
- ) : ( -
- {allPaths.map((sdPath, i) => ( -
-
- -
-
- {getPathDisplay(sdPath)} -
-
- {sdPath.type === "Physical" && - "Local Device"} - {sdPath.type === "Cloud" && - "Cloud Storage"} - {sdPath.type === "Content" && - "Content Addressed"} -
-
-
-
-
- ))} -
- )} -
- ); + {allPaths.length === 1 ? ( +
+ No alternate instances found +
+ ) : ( +
+ {allPaths.map((sdPath, i) => ( +
+
+ + + +
+
+ {getPathDisplay(sdPath)} +
+
+ {"Physical" in sdPath && "Local Device"} + {"Cloud" in sdPath && "Cloud Storage"} + {"Content" in sdPath && "Content Addressed"} +
+
+
+
+
+ ))} +
+ )} +
+ ); } function ChatTab() { - const [message, setMessage] = useState(""); + const [message, setMessage] = useState(""); - const messages = [ - { - id: 1, - sender: "Sarah", - avatar: "S", - content: "Can you check if this photo is also on the NAS?", - time: "2:34 PM", - isUser: false, - }, - { - id: 2, - sender: "You", - avatar: "J", - content: "Yeah, it's synced. Shows 3 instances across devices.", - time: "2:35 PM", - isUser: true, - }, - { - id: 3, - sender: "AI Assistant", - avatar: "", - content: - "I found 2 similar photos in your library from the same location. Would you like me to create a collection?", - time: "2:36 PM", - isUser: false, - isAI: true, - unread: true, - }, - { - id: 4, - sender: "Sarah", - avatar: "S", - content: "Perfect, thanks! Can you share the collection with me?", - time: "2:37 PM", - isUser: false, - unread: true, - }, - { - id: 5, - sender: "Alex", - avatar: "A", - content: "I just tagged this as Summer 2025 btw", - time: "2:38 PM", - isUser: false, - unread: true, - }, - ]; + const messages = [ + { + id: 1, + sender: "Sarah", + avatar: "S", + content: "Can you check if this photo is also on the NAS?", + time: "2:34 PM", + isUser: false, + }, + { + id: 2, + sender: "You", + avatar: "J", + content: "Yeah, it's synced. Shows 3 instances across devices.", + time: "2:35 PM", + isUser: true, + }, + { + id: 3, + sender: "AI Assistant", + avatar: "", + content: + "I found 2 similar photos in your library from the same location. Would you like me to create a collection?", + time: "2:36 PM", + isUser: false, + isAI: true, + unread: true, + }, + { + id: 4, + sender: "Sarah", + avatar: "S", + content: "Perfect, thanks! Can you share the collection with me?", + time: "2:37 PM", + isUser: false, + unread: true, + }, + { + id: 5, + sender: "Alex", + avatar: "A", + content: "I just tagged this as Summer 2025 btw", + time: "2:38 PM", + isUser: false, + unread: true, + }, + ]; - return ( -
- {/* Messages */} -
- {messages.map((msg) => ( -
- {/* Avatar */} -
- {msg.avatar} -
+ return ( +
+ {/* Messages */} +
+ {messages.map((msg) => ( +
+ {/* Avatar */} +
+ {msg.avatar} +
- {/* Message bubble */} -
-
- {!msg.isUser && ( -
- {msg.sender} -
- )} -

- {msg.content} -

-
- - {msg.time} - -
-
- ))} -
+ {/* Message bubble */} +
+
+ {!msg.isUser && ( +
+ {msg.sender} +
+ )} +

+ {msg.content} +

+
+ + {msg.time} + +
+
+ ))} +
- {/* Input */} -
-
- + {/* Input */} +
+
+ -
- setMessage(e.target.value)} - placeholder="Type a message..." - className="flex-1 bg-transparent text-xs text-sidebar-ink placeholder:text-sidebar-inkDull outline-none" - /> -
+
+ setMessage(e.target.value)} + placeholder="Type a message..." + className="flex-1 bg-transparent text-xs text-sidebar-ink placeholder:text-sidebar-inkDull outline-none" + /> +
- -
+ +
-
- - - -
-
-
- ); +
+ + + +
+
+
+ ); } function ActivityTab() { - const activity = [ - { action: "Synced to NAS", time: "2 min ago", device: "MacBook Pro" }, - { action: "Uploaded to S3", time: "1 hour ago", device: "MacBook Pro" }, - { - action: "Thumbnail generated", - time: "2 hours ago", - device: "MacBook Pro", - }, - { action: "Tagged as 'Travel'", time: "3 hours ago", device: "iPhone" }, - { action: "Created", time: "Jan 15, 2025", device: "iPhone" }, - ]; + const activity = [ + { action: "Synced to NAS", time: "2 min ago", device: "MacBook Pro" }, + { action: "Uploaded to S3", time: "1 hour ago", device: "MacBook Pro" }, + { + action: "Thumbnail generated", + time: "2 hours ago", + device: "MacBook Pro", + }, + { action: "Tagged as 'Travel'", time: "3 hours ago", device: "iPhone" }, + { action: "Created", time: "Jan 15, 2025", device: "iPhone" }, + ]; - return ( -
-

- History of changes and sync operations -

+ return ( +
+

+ History of changes and sync operations +

-
- {activity.map((item, i) => ( -
- -
-
- {item.action} -
-
- {item.time} · {item.device} -
-
-
- ))} -
-
- ); +
+ {activity.map((item, i) => ( +
+ + + +
+
{item.action}
+
+ {item.time} · {item.device} +
+
+
+ ))} +
+
+ ); } function DetailsTab({ file }: { file: File }) { - return ( -
- {/* Content Identity */} - {file.content_identity && ( -
- - {file.content_identity.integrity_hash && ( - - )} - {file.content_identity.mime_type && ( - - )} -
- )} + return ( +
+ {/* Content Identity */} + {file.content_identity && ( +
+ + {file.content_identity.integrity_hash && ( + + )} + {file.content_identity.mime_type_id !== null && ( + + )} +
+ )} - {/* Media Info */} - {file.content_identity?.media_data && ( -
- -
- )} + {/* Metadata */} +
+ + + {file.extension && ( + + )} +
- {/* Metadata */} -
- - - {file.extension && ( - - )} -
- - {/* System */} -
- - - -
-
- ); + {/* System */} +
+ + + +
+
+ ); } diff --git a/packages/interface/src/inspectors/LocationInspector.tsx b/packages/interface/src/inspectors/LocationInspector.tsx index afd95b520..30aa40fda 100644 --- a/packages/interface/src/inspectors/LocationInspector.tsx +++ b/packages/interface/src/inspectors/LocationInspector.tsx @@ -27,7 +27,7 @@ import { TabContent, } from "../components/Inspector"; import clsx from "clsx"; -import type { LocationInfo } from "@sd/ts-client/generated/types"; +import type { LocationInfo } from "@sd/ts-client"; import { useLibraryMutation } from "../context"; import LocationIcon from "@sd/assets/icons/Location.png"; diff --git a/packages/interface/src/routes/overview/StorageOverview.tsx b/packages/interface/src/routes/overview/StorageOverview.tsx index 88bc5f6bf..8eee4ed00 100644 --- a/packages/interface/src/routes/overview/StorageOverview.tsx +++ b/packages/interface/src/routes/overview/StorageOverview.tsx @@ -14,7 +14,7 @@ import type { VolumeItem, LibraryDeviceInfo, ListLibraryDevicesInput, -} from "@sd/ts-client/generated/types"; +} from "@sd/ts-client"; function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; diff --git a/packages/interface/src/routes/overview/index.tsx b/packages/interface/src/routes/overview/index.tsx index 0f74a6566..cfb0d3842 100644 --- a/packages/interface/src/routes/overview/index.tsx +++ b/packages/interface/src/routes/overview/index.tsx @@ -11,7 +11,7 @@ import { DevicesPanel } from "./DevicesPanel"; import { ContentBreakdown } from "./ContentBreakdown"; import { OverviewTopBar } from "./OverviewTopBar"; import { useNormalizedCache } from "../../context"; -import type { LibraryInfoOutput } from "@sd/ts-client/generated/types"; +import type { LibraryInfoOutput } from "@sd/ts-client"; export function Overview() { // Fetch library info with statistics using normalizedCache diff --git a/packages/ts-client/package.json b/packages/ts-client/package.json index 613be7608..a51ef7a13 100644 --- a/packages/ts-client/package.json +++ b/packages/ts-client/package.json @@ -4,6 +4,16 @@ "description": "Type-safe TypeScript client for Spacedrive daemon", "main": "dist/index.js", "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./generated/types": { + "types": "./dist/generated/types.d.ts", + "default": "./dist/generated/types.js" + } + }, "scripts": { "build": "tsc", "dev": "tsc --watch", diff --git a/packages/ts-client/src/deviceIcons.ts b/packages/ts-client/src/deviceIcons.ts index a2cadde5d..ac147a452 100644 --- a/packages/ts-client/src/deviceIcons.ts +++ b/packages/ts-client/src/deviceIcons.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import type { LibraryDeviceInfo } from "./generated/types"; import Laptop from "@sd/assets/icons/Laptop.png"; import Mobile from "@sd/assets/icons/Mobile.png"; diff --git a/packages/ts-client/src/event-filter.ts b/packages/ts-client/src/event-filter.ts index 9ed194d48..91f7c1fdb 100644 --- a/packages/ts-client/src/event-filter.ts +++ b/packages/ts-client/src/event-filter.ts @@ -88,6 +88,5 @@ export const DEFAULT_EVENT_SUBSCRIPTION: EventVariant[] = [ * Noisy events that are excluded from the default subscription */ export const NOISY_EVENTS: EventVariant[] = [ - "LogMessage", "IndexingProgress", ]; diff --git a/packages/ts-client/src/generated/types.ts b/packages/ts-client/src/generated/types.ts index 7c88967c8..f7b920f83 100644 --- a/packages/ts-client/src/generated/types.ts +++ b/packages/ts-client/src/generated/types.ts @@ -3,6 +3,8 @@ // This file has been generated by Specta. DO NOT EDIT. +export type Empty = Record; + export type ActionContextInfo = { action_type: string; initiated_at: string; initiated_by: string | null; action_input: JsonValue; context: JsonValue }; export type AddGroupInput = { space_id: string; name: string; group_type: GroupType }; @@ -390,11 +392,7 @@ export type EntryKind = /** * Regular file */ -{ File: { -/** - * File extension (without dot) - */ -extension: string | null } } | +"File" | /** * Directory */ @@ -402,11 +400,7 @@ extension: string | null } } | /** * Symbolic link */ -{ Symlink: { -/** - * Target path - */ -target: string } }; +"Symlink"; /** * Error event for tracking recent errors @@ -511,10 +505,18 @@ id: string; * The universal path to the file in Spacedrive's VDFS */ sd_path: SdPath; +/** + * The file kind (file, directory, symlink) + */ +kind: EntryKind; /** * The name of the file, including the extension */ name: string; +/** + * The file extension (without dot) + */ +extension: string | null; /** * The size of the file in bytes */ @@ -546,7 +548,7 @@ created_at: string; modified_at: string; accessed_at: string | null; /** * Additional computed fields */ -content_kind: ContentKind; extension: string | null; kind: EntryKind; is_local: boolean; +content_kind: ContentKind; is_local: boolean; /** * Video duration (for grid display optimization) */ @@ -3177,185 +3179,185 @@ success: boolean }; // ===== API Type Unions ===== export type CoreAction = - { type: 'network.start'; input: NetworkStartInput; output: NetworkStartOutput } - | { type: 'network.device.revoke'; input: DeviceRevokeInput; output: DeviceRevokeOutput } - | { type: 'network.pair.cancel'; input: PairCancelInput; output: PairCancelOutput } - | { type: 'network.stop'; input: NetworkStopInput; output: NetworkStopOutput } - | { type: 'network.sync_setup'; input: LibrarySyncSetupInput; output: LibrarySyncSetupOutput } + { type: 'network.stop'; input: NetworkStopInput; output: NetworkStopOutput } + | { type: 'network.start'; input: NetworkStartInput; output: NetworkStartOutput } | { type: 'network.pair.join'; input: PairJoinInput; output: PairJoinOutput } | { type: 'network.pair.generate'; input: PairGenerateInput; output: PairGenerateOutput } | { type: 'libraries.open'; input: LibraryOpenInput; output: LibraryOpenOutput } - | { type: 'network.spacedrop.send'; input: SpacedropSendInput; output: SpacedropSendOutput } - | { type: 'libraries.delete'; input: LibraryDeleteInput; output: LibraryDeleteOutput } | { type: 'libraries.create'; input: LibraryCreateInput; output: LibraryCreateOutput } | { type: 'models.whisper.delete'; input: DeleteWhisperModelInput; output: DeleteWhisperModelOutput } | { type: 'models.whisper.download'; input: DownloadWhisperModelInput; output: DownloadWhisperModelOutput } + | { type: 'network.sync_setup'; input: LibrarySyncSetupInput; output: LibrarySyncSetupOutput } + | { type: 'network.device.revoke'; input: DeviceRevokeInput; output: DeviceRevokeOutput } + | { type: 'libraries.delete'; input: LibraryDeleteInput; output: LibraryDeleteOutput } + | { type: 'network.pair.cancel'; input: PairCancelInput; output: PairCancelOutput } + | { type: 'network.spacedrop.send'; input: SpacedropSendInput; output: SpacedropSendOutput } ; export type LibraryAction = - { type: 'volumes.track'; input: VolumeTrackInput; output: VolumeTrackOutput } - | { type: 'locations.rescan'; input: LocationRescanInput; output: LocationRescanOutput } - | { type: 'volumes.untrack'; input: VolumeUntrackInput; output: VolumeUntrackOutput } - | { type: 'volumes.remove_cloud'; input: VolumeRemoveCloudInput; output: VolumeRemoveCloudOutput } - | { type: 'media.ocr.extract'; input: ExtractTextInput; output: ExtractTextOutput } - | { type: 'spaces.delete_item'; input: DeleteItemInput; output: DeleteItemOutput } - | { type: 'libraries.rename'; input: LibraryRenameInput; output: LibraryRenameOutput } - | { type: 'indexing.start'; input: IndexInput; output: JobReceipt } - | { type: 'volumes.speed_test'; input: VolumeSpeedTestInput; output: VolumeSpeedTestOutput } - | { type: 'spaces.delete'; input: SpaceDeleteInput; output: SpaceDeleteOutput } - | { type: 'media.proxy.generate'; input: GenerateProxyInput; output: GenerateProxyOutput } - | { type: 'volumes.add_cloud'; input: VolumeAddCloudInput; output: VolumeAddCloudOutput } - | { type: 'indexing.verify'; input: IndexVerifyInput; output: IndexVerifyOutput } - | { type: 'jobs.resume'; input: JobResumeInput; output: JobResumeOutput } - | { type: 'files.copy'; input: FileCopyInput; output: JobReceipt } - | { type: 'spaces.delete_group'; input: DeleteGroupInput; output: DeleteGroupOutput } - | { type: 'spaces.update_group'; input: UpdateGroupInput; output: UpdateGroupOutput } - | { type: 'media.speech.transcribe'; input: TranscribeAudioInput; output: TranscribeAudioOutput } - | { type: 'locations.remove'; input: LocationRemoveInput; output: LocationRemoveOutput } - | { type: 'spaces.reorder_items'; input: ReorderItemsInput; output: ReorderOutput } - | { type: 'spaces.reorder_groups'; input: ReorderGroupsInput; output: ReorderOutput } - | { type: 'tags.apply'; input: ApplyTagsInput; output: ApplyTagsOutput } - | { type: 'volumes.refresh'; input: VolumeRefreshInput; output: VolumeRefreshOutput } + { type: 'jobs.resume'; input: JobResumeInput; output: JobResumeOutput } | { type: 'media.thumbnail.regenerate'; input: RegenerateThumbnailInput; output: RegenerateThumbnailOutput } | { type: 'media.thumbnail'; input: ThumbnailInput; output: JobReceipt } - | { type: 'media.thumbstrip.generate'; input: GenerateThumbstripInput; output: GenerateThumbstripOutput } - | { type: 'spaces.add_item'; input: AddItemInput; output: AddItemOutput } - | { type: 'jobs.pause'; input: JobPauseInput; output: JobPauseOutput } - | { type: 'locations.update'; input: LocationUpdateInput; output: LocationUpdateOutput } - | { type: 'locations.add'; input: LocationAddInput; output: LocationAddOutput } - | { type: 'jobs.cancel'; input: JobCancelInput; output: JobCancelOutput } - | { type: 'spaces.add_group'; input: AddGroupInput; output: AddGroupOutput } - | { type: 'spaces.update'; input: SpaceUpdateInput; output: SpaceUpdateOutput } - | { type: 'files.delete'; input: FileDeleteInput; output: JobReceipt } - | { type: 'tags.create'; input: CreateTagInput; output: CreateTagOutput } + | { type: 'libraries.rename'; input: LibraryRenameInput; output: LibraryRenameOutput } + | { type: 'spaces.delete_group'; input: DeleteGroupInput; output: DeleteGroupOutput } + | { type: 'tags.apply'; input: ApplyTagsInput; output: ApplyTagsOutput } + | { type: 'locations.rescan'; input: LocationRescanInput; output: LocationRescanOutput } + | { type: 'volumes.add_cloud'; input: VolumeAddCloudInput; output: VolumeAddCloudOutput } | { type: 'libraries.export'; input: LibraryExportInput; output: LibraryExportOutput } + | { type: 'volumes.speed_test'; input: VolumeSpeedTestInput; output: VolumeSpeedTestOutput } | { type: 'spaces.create'; input: SpaceCreateInput; output: SpaceCreateOutput } + | { type: 'spaces.delete'; input: SpaceDeleteInput; output: SpaceDeleteOutput } + | { type: 'locations.add'; input: LocationAddInput; output: LocationAddOutput } + | { type: 'indexing.start'; input: IndexInput; output: JobReceipt } + | { type: 'spaces.add_group'; input: AddGroupInput; output: AddGroupOutput } + | { type: 'tags.create'; input: CreateTagInput; output: CreateTagOutput } + | { type: 'spaces.delete_item'; input: DeleteItemInput; output: DeleteItemOutput } + | { type: 'jobs.cancel'; input: JobCancelInput; output: JobCancelOutput } + | { type: 'volumes.track'; input: VolumeTrackInput; output: VolumeTrackOutput } + | { type: 'spaces.add_item'; input: AddItemInput; output: AddItemOutput } + | { type: 'spaces.update'; input: SpaceUpdateInput; output: SpaceUpdateOutput } + | { type: 'jobs.pause'; input: JobPauseInput; output: JobPauseOutput } + | { type: 'media.proxy.generate'; input: GenerateProxyInput; output: GenerateProxyOutput } + | { type: 'volumes.remove_cloud'; input: VolumeRemoveCloudInput; output: VolumeRemoveCloudOutput } + | { type: 'media.ocr.extract'; input: ExtractTextInput; output: ExtractTextOutput } + | { type: 'volumes.refresh'; input: VolumeRefreshInput; output: VolumeRefreshOutput } + | { type: 'volumes.untrack'; input: VolumeUntrackInput; output: VolumeUntrackOutput } + | { type: 'locations.update'; input: LocationUpdateInput; output: LocationUpdateOutput } | { type: 'locations.triggerJob'; input: LocationTriggerJobInput; output: LocationTriggerJobOutput } + | { type: 'files.copy'; input: FileCopyInput; output: JobReceipt } + | { type: 'media.speech.transcribe'; input: TranscribeAudioInput; output: TranscribeAudioOutput } + | { type: 'indexing.verify'; input: IndexVerifyInput; output: IndexVerifyOutput } + | { type: 'locations.remove'; input: LocationRemoveInput; output: LocationRemoveOutput } + | { type: 'files.delete'; input: FileDeleteInput; output: JobReceipt } + | { type: 'media.thumbstrip.generate'; input: GenerateThumbstripInput; output: GenerateThumbstripOutput } + | { type: 'spaces.update_group'; input: UpdateGroupInput; output: UpdateGroupOutput } + | { type: 'spaces.reorder_items'; input: ReorderItemsInput; output: ReorderOutput } + | { type: 'spaces.reorder_groups'; input: ReorderGroupsInput; output: ReorderOutput } ; export type CoreQuery = - { type: 'libraries.list'; input: ListLibrariesInput; output: [LibraryInfo] } - | { type: 'core.status'; input: Empty; output: CoreStatus } - | { type: 'network.status'; input: NetworkStatusQueryInput; output: NetworkStatus } + { type: 'network.devices.list'; input: ListPairedDevicesInput; output: ListPairedDevicesOutput } | { type: 'models.whisper.list'; input: ListWhisperModelsInput; output: ListWhisperModelsOutput } + | { type: 'network.pair.status'; input: PairStatusQueryInput; output: PairStatusOutput } | { type: 'network.sync_setup.discover'; input: DiscoverRemoteLibrariesInput; output: DiscoverRemoteLibrariesOutput } | { type: 'core.events.list'; input: ListEventsInput; output: ListEventsOutput } - | { type: 'network.pair.status'; input: PairStatusQueryInput; output: PairStatusOutput } - | { type: 'network.devices.list'; input: ListPairedDevicesInput; output: ListPairedDevicesOutput } + | { type: 'libraries.list'; input: ListLibrariesInput; output: [LibraryInfo] } + | { type: 'core.status'; input: Empty; output: CoreStatus } + | { type: 'network.status'; input: NetworkStatusQueryInput; output: NetworkStatus } ; export type LibraryQuery = - { type: 'files.by_id'; input: FileByIdQuery; output: File } - | { type: 'spaces.get_layout'; input: SpaceLayoutQueryInput; output: SpaceLayout } + { type: 'files.media_listing'; input: MediaListingInput; output: MediaListingOutput } | { type: 'tags.search'; input: SearchTagsInput; output: SearchTagsOutput } - | { type: 'locations.list'; input: LocationsListQueryInput; output: LocationsListOutput } - | { type: 'test.ping'; input: PingInput; output: PingOutput } - | { type: 'spaces.list'; input: SpacesListQueryInput; output: SpacesListOutput } - | { type: 'sync.metrics'; input: GetSyncMetricsInput; output: GetSyncMetricsOutput } - | { type: 'files.media_listing'; input: MediaListingInput; output: MediaListingOutput } - | { type: 'jobs.info'; input: JobInfoQueryInput; output: JobInfoOutput } - | { type: 'libraries.info'; input: LibraryInfoQueryInput; output: LibraryInfoOutput } - | { type: 'files.unique_to_location'; input: UniqueToLocationInput; output: UniqueToLocationOutput } - | { type: 'files.by_path'; input: FileByPathQuery; output: File } - | { type: 'locations.suggested'; input: SuggestedLocationsQueryInput; output: SuggestedLocationsOutput } - | { type: 'search.files'; input: FileSearchInput; output: FileSearchOutput } - | { type: 'jobs.list'; input: JobListInput; output: JobListOutput } - | { type: 'devices.list'; input: ListLibraryDevicesInput; output: [LibraryDeviceInfo] } - | { type: 'volumes.list'; input: VolumeListQueryInput; output: VolumeListOutput } | { type: 'spaces.get'; input: SpaceGetQueryInput; output: SpaceGetOutput } + | { type: 'spaces.list'; input: SpacesListQueryInput; output: SpacesListOutput } + | { type: 'libraries.info'; input: LibraryInfoQueryInput; output: LibraryInfoOutput } + | { type: 'search.files'; input: FileSearchInput; output: FileSearchOutput } + | { type: 'sync.metrics'; input: GetSyncMetricsInput; output: GetSyncMetricsOutput } + | { type: 'jobs.info'; input: JobInfoQueryInput; output: JobInfoOutput } + | { type: 'files.unique_to_location'; input: UniqueToLocationInput; output: UniqueToLocationOutput } + | { type: 'jobs.list'; input: JobListInput; output: JobListOutput } + | { type: 'volumes.list'; input: VolumeListQueryInput; output: VolumeListOutput } + | { type: 'locations.list'; input: LocationsListQueryInput; output: LocationsListOutput } + | { type: 'locations.suggested'; input: SuggestedLocationsQueryInput; output: SuggestedLocationsOutput } + | { type: 'files.by_path'; input: FileByPathQuery; output: File } | { type: 'files.directory_listing'; input: DirectoryListingInput; output: DirectoryListingOutput } + | { type: 'devices.list'; input: ListLibraryDevicesInput; output: [LibraryDeviceInfo] } + | { type: 'files.by_id'; input: FileByIdQuery; output: File } + | { type: 'test.ping'; input: PingInput; output: PingOutput } + | { type: 'spaces.get_layout'; input: SpaceLayoutQueryInput; output: SpaceLayout } ; // ===== Wire Method Mappings ===== export const WIRE_METHODS = { coreActions: { - 'network.start': 'action:network.start.input', - 'network.device.revoke': 'action:network.device.revoke.input', - 'network.pair.cancel': 'action:network.pair.cancel.input', 'network.stop': 'action:network.stop.input', - 'network.sync_setup': 'action:network.sync_setup.input', + 'network.start': 'action:network.start.input', 'network.pair.join': 'action:network.pair.join.input', 'network.pair.generate': 'action:network.pair.generate.input', 'libraries.open': 'action:libraries.open.input', - 'network.spacedrop.send': 'action:network.spacedrop.send.input', - 'libraries.delete': 'action:libraries.delete.input', 'libraries.create': 'action:libraries.create.input', 'models.whisper.delete': 'action:models.whisper.delete.input', 'models.whisper.download': 'action:models.whisper.download.input', + 'network.sync_setup': 'action:network.sync_setup.input', + 'network.device.revoke': 'action:network.device.revoke.input', + 'libraries.delete': 'action:libraries.delete.input', + 'network.pair.cancel': 'action:network.pair.cancel.input', + 'network.spacedrop.send': 'action:network.spacedrop.send.input', }, libraryActions: { - 'volumes.track': 'action:volumes.track.input', - 'locations.rescan': 'action:locations.rescan.input', - 'volumes.untrack': 'action:volumes.untrack.input', - 'volumes.remove_cloud': 'action:volumes.remove_cloud.input', - 'media.ocr.extract': 'action:media.ocr.extract.input', - 'spaces.delete_item': 'action:spaces.delete_item.input', - 'libraries.rename': 'action:libraries.rename.input', - 'indexing.start': 'action:indexing.start.input', - 'volumes.speed_test': 'action:volumes.speed_test.input', - 'spaces.delete': 'action:spaces.delete.input', - 'media.proxy.generate': 'action:media.proxy.generate.input', - 'volumes.add_cloud': 'action:volumes.add_cloud.input', - 'indexing.verify': 'action:indexing.verify.input', 'jobs.resume': 'action:jobs.resume.input', - 'files.copy': 'action:files.copy.input', - 'spaces.delete_group': 'action:spaces.delete_group.input', - 'spaces.update_group': 'action:spaces.update_group.input', - 'media.speech.transcribe': 'action:media.speech.transcribe.input', - 'locations.remove': 'action:locations.remove.input', - 'spaces.reorder_items': 'action:spaces.reorder_items.input', - 'spaces.reorder_groups': 'action:spaces.reorder_groups.input', - 'tags.apply': 'action:tags.apply.input', - 'volumes.refresh': 'action:volumes.refresh.input', 'media.thumbnail.regenerate': 'action:media.thumbnail.regenerate.input', 'media.thumbnail': 'action:media.thumbnail.input', - 'media.thumbstrip.generate': 'action:media.thumbstrip.generate.input', - 'spaces.add_item': 'action:spaces.add_item.input', - 'jobs.pause': 'action:jobs.pause.input', - 'locations.update': 'action:locations.update.input', - 'locations.add': 'action:locations.add.input', - 'jobs.cancel': 'action:jobs.cancel.input', - 'spaces.add_group': 'action:spaces.add_group.input', - 'spaces.update': 'action:spaces.update.input', - 'files.delete': 'action:files.delete.input', - 'tags.create': 'action:tags.create.input', + 'libraries.rename': 'action:libraries.rename.input', + 'spaces.delete_group': 'action:spaces.delete_group.input', + 'tags.apply': 'action:tags.apply.input', + 'locations.rescan': 'action:locations.rescan.input', + 'volumes.add_cloud': 'action:volumes.add_cloud.input', 'libraries.export': 'action:libraries.export.input', + 'volumes.speed_test': 'action:volumes.speed_test.input', 'spaces.create': 'action:spaces.create.input', + 'spaces.delete': 'action:spaces.delete.input', + 'locations.add': 'action:locations.add.input', + 'indexing.start': 'action:indexing.start.input', + 'spaces.add_group': 'action:spaces.add_group.input', + 'tags.create': 'action:tags.create.input', + 'spaces.delete_item': 'action:spaces.delete_item.input', + 'jobs.cancel': 'action:jobs.cancel.input', + 'volumes.track': 'action:volumes.track.input', + 'spaces.add_item': 'action:spaces.add_item.input', + 'spaces.update': 'action:spaces.update.input', + 'jobs.pause': 'action:jobs.pause.input', + 'media.proxy.generate': 'action:media.proxy.generate.input', + 'volumes.remove_cloud': 'action:volumes.remove_cloud.input', + 'media.ocr.extract': 'action:media.ocr.extract.input', + 'volumes.refresh': 'action:volumes.refresh.input', + 'volumes.untrack': 'action:volumes.untrack.input', + 'locations.update': 'action:locations.update.input', 'locations.triggerJob': 'action:locations.triggerJob.input', + 'files.copy': 'action:files.copy.input', + 'media.speech.transcribe': 'action:media.speech.transcribe.input', + 'indexing.verify': 'action:indexing.verify.input', + 'locations.remove': 'action:locations.remove.input', + 'files.delete': 'action:files.delete.input', + 'media.thumbstrip.generate': 'action:media.thumbstrip.generate.input', + 'spaces.update_group': 'action:spaces.update_group.input', + 'spaces.reorder_items': 'action:spaces.reorder_items.input', + 'spaces.reorder_groups': 'action:spaces.reorder_groups.input', }, coreQueries: { + 'network.devices.list': 'query:network.devices.list', + 'models.whisper.list': 'query:models.whisper.list', + 'network.pair.status': 'query:network.pair.status', + 'network.sync_setup.discover': 'query:network.sync_setup.discover', + 'core.events.list': 'query:core.events.list', 'libraries.list': 'query:libraries.list', 'core.status': 'query:core.status', 'network.status': 'query:network.status', - 'models.whisper.list': 'query:models.whisper.list', - 'network.sync_setup.discover': 'query:network.sync_setup.discover', - 'core.events.list': 'query:core.events.list', - 'network.pair.status': 'query:network.pair.status', - 'network.devices.list': 'query:network.devices.list', }, libraryQueries: { - 'files.by_id': 'query:files.by_id', - 'spaces.get_layout': 'query:spaces.get_layout', - 'tags.search': 'query:tags.search', - 'locations.list': 'query:locations.list', - 'test.ping': 'query:test.ping', - 'spaces.list': 'query:spaces.list', - 'sync.metrics': 'query:sync.metrics', 'files.media_listing': 'query:files.media_listing', - 'jobs.info': 'query:jobs.info', - 'libraries.info': 'query:libraries.info', - 'files.unique_to_location': 'query:files.unique_to_location', - 'files.by_path': 'query:files.by_path', - 'locations.suggested': 'query:locations.suggested', - 'search.files': 'query:search.files', - 'jobs.list': 'query:jobs.list', - 'devices.list': 'query:devices.list', - 'volumes.list': 'query:volumes.list', + 'tags.search': 'query:tags.search', 'spaces.get': 'query:spaces.get', + 'spaces.list': 'query:spaces.list', + 'libraries.info': 'query:libraries.info', + 'search.files': 'query:search.files', + 'sync.metrics': 'query:sync.metrics', + 'jobs.info': 'query:jobs.info', + 'files.unique_to_location': 'query:files.unique_to_location', + 'jobs.list': 'query:jobs.list', + 'volumes.list': 'query:volumes.list', + 'locations.list': 'query:locations.list', + 'locations.suggested': 'query:locations.suggested', + 'files.by_path': 'query:files.by_path', 'files.directory_listing': 'query:files.directory_listing', + 'devices.list': 'query:devices.list', + 'files.by_id': 'query:files.by_id', + 'test.ping': 'query:test.ping', + 'spaces.get_layout': 'query:spaces.get_layout', }, } as const; diff --git a/packages/ts-client/src/hooks/useMutation.ts b/packages/ts-client/src/hooks/useMutation.ts index afa5b3c5c..ee6beffd8 100644 --- a/packages/ts-client/src/hooks/useMutation.ts +++ b/packages/ts-client/src/hooks/useMutation.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import { useMutation, type UseMutationOptions, type UseMutationResult } from "@tanstack/react-query"; import { useSpacedriveClient } from "./useClient"; import type { CoreAction, LibraryAction } from "../generated/types"; diff --git a/packages/ts-client/src/hooks/useNormalizedQuery.ts b/packages/ts-client/src/hooks/useNormalizedQuery.ts index 65dc83fca..291ec3ef6 100644 --- a/packages/ts-client/src/hooks/useNormalizedQuery.ts +++ b/packages/ts-client/src/hooks/useNormalizedQuery.ts @@ -205,6 +205,11 @@ export function handleResourceEvent( queryKey: any[], queryClient: QueryClient, ) { + // Skip string events (like "CoreStarted", "CoreShutdown") + if (typeof event === "string") { + return; + } + // Refresh event - invalidate all queries if ("Refresh" in event) { queryClient.invalidateQueries(); diff --git a/packages/ts-client/src/hooks/useQuery.ts b/packages/ts-client/src/hooks/useQuery.ts index 07e591e1e..d541231a8 100644 --- a/packages/ts-client/src/hooks/useQuery.ts +++ b/packages/ts-client/src/hooks/useQuery.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import { useQuery, type UseQueryOptions, type UseQueryResult } from "@tanstack/react-query"; import { useSpacedriveClient } from "./useClient"; import type { CoreQuery, LibraryQuery } from "../generated/types"; diff --git a/packages/ts-client/src/subscriptionManager.ts b/packages/ts-client/src/subscriptionManager.ts index 775c25078..369258bb9 100644 --- a/packages/ts-client/src/subscriptionManager.ts +++ b/packages/ts-client/src/subscriptionManager.ts @@ -20,6 +20,7 @@ interface EventFilter { resource_type?: string; path_scope?: any; include_descendants?: boolean; + event_types?: string[]; } interface SubscriptionEntry { diff --git a/packages/ts-client/tsconfig.json b/packages/ts-client/tsconfig.json index 7c618c976..f3e1c2605 100644 --- a/packages/ts-client/tsconfig.json +++ b/packages/ts-client/tsconfig.json @@ -19,5 +19,5 @@ "types": ["jest", "node"] }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx", "**/__tests__/**"] }