Fix build issues on Linux

Managed to successfully build on Linux, updated xtask to resolve errors,
and fixed interface not loading properly when using Tauri GUI.
This commit is contained in:
Gedeon Sainrival 2025-12-02 13:17:28 -05:00
parent e3ba15632f
commit f01a2994f1
No known key found for this signature in database
GPG Key ID: 340B7FAB63612C3C
11 changed files with 6721 additions and 10683 deletions

2
.gitmodules vendored
View File

@ -5,7 +5,7 @@
path = apps/macos
url = https://github.com/spacedriveapp/macos.git
[submodule "workbench"]
path = workbench
path = docs/workbench
url = https://github.com/spacedriveapp/design.git
[submodule "apps/landing"]
path = apps/landing

12
Cargo.lock generated
View File

@ -4909,6 +4909,17 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "hostname"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd"
dependencies = [
"cfg-if",
"libc",
"windows-link 0.2.0",
]
[[package]]
name = "hostname-validator"
version = "1.1.1"
@ -10496,6 +10507,7 @@ dependencies = [
"hex",
"hkdf",
"hmac",
"hostname",
"hound",
"if-watch",
"image",

File diff suppressed because it is too large Load Diff

10521
bun.lock

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,7 @@ specta-swift = { git = "https://github.com/jamiepine/specta", branch = "main" }
specta-typescript = { git = "https://github.com/jamiepine/specta", branch = "main" }
strum = { version = "0.26", features = ["derive"] }
toml = "0.8"
hostname = "^0.4"
# Error handling

View File

@ -284,6 +284,8 @@ fn parse_subvolume_info(output: &str) -> VolumeResult<SubvolumeInfo> {
/// Enhance volume with Btrfs-specific information from mount point
pub async fn enhance_volume_from_mount(volume: &mut Volume) -> VolumeResult<()> {
use super::FilesystemHandler;
let handler = BtrfsHandler::new();
handler.enhance_volume(volume).await
}

View File

@ -373,7 +373,9 @@ fn parse_zfs_size(size_str: &str) -> Option<u64> {
/// Enhance volume with ZFS-specific information from mount point
pub async fn enhance_volume_from_mount(volume: &mut Volume) -> VolumeResult<()> {
let handler = ZfsHandler::new();
use super::FilesystemHandler;
let handler = ZfsHandler;
handler.enhance_volume(volume).await
}

View File

@ -11,7 +11,12 @@ import { Dialogs } from "@sd/ui";
import { Inspector, type InspectorVariant } from "./Inspector";
import { TopBarProvider, TopBar } from "./TopBar";
import { motion, AnimatePresence } from "framer-motion";
import { ExplorerProvider, useExplorer, Sidebar, getSpaceItemKeyFromRoute } from "./components/explorer";
import {
ExplorerProvider,
useExplorer,
Sidebar,
getSpaceItemKeyFromRoute,
} from "./components/Explorer";
import {
SelectionProvider,
useSelection,
@ -19,7 +24,10 @@ import {
import { KeyboardHandler } from "./components/Explorer/KeyboardHandler";
import { TagAssignmentMode } from "./components/Explorer/TagAssignmentMode";
import { SpacesSidebar } from "./components/SpacesSidebar";
import { QuickPreviewFullscreen, PREVIEW_LAYER_ID } from "./components/QuickPreview";
import {
QuickPreviewFullscreen,
PREVIEW_LAYER_ID,
} from "./components/QuickPreview";
import { createExplorerRouter } from "./router";
import { useNormalizedCache } from "./context";
import { usePlatform } from "./platform";
@ -49,7 +57,10 @@ export function ExplorerLayout() {
// Sync route with explorer context for view preferences
useEffect(() => {
const spaceItemKey = getSpaceItemKeyFromRoute(location.pathname, location.search);
const spaceItemKey = getSpaceItemKeyFromRoute(
location.pathname,
location.search,
);
setSpaceItemId(spaceItemKey);
}, [location.pathname, location.search, setSpaceItemId]);
@ -129,7 +140,9 @@ export function ExplorerLayout() {
<TopBar
sidebarWidth={sidebarVisible ? 224 : 0}
inspectorWidth={inspectorVisible && !isOverview && !isKnowledgeView ? 284 : 0}
inspectorWidth={
inspectorVisible && !isOverview && !isKnowledgeView ? 284 : 0
}
isPreviewActive={isPreviewActive}
/>
@ -193,7 +206,9 @@ export function ExplorerLayout() {
hasPrevious={false}
hasNext={false}
sidebarWidth={sidebarVisible ? 220 : 0}
inspectorWidth={inspectorVisible && !isOverview && !isKnowledgeView ? 280 : 0}
inspectorWidth={
inspectorVisible && !isOverview && !isKnowledgeView ? 280 : 0
}
/>
)}
</div>

View File

@ -1,6 +1,6 @@
import { createBrowserRouter } from "react-router-dom";
import { Overview } from "./routes/overview";
import { ExplorerView } from "./components/explorer";
import { ExplorerView } from "./components/Explorer";
import { ExplorerLayout } from "./Explorer";
import { JobsScreen } from "./components/JobManager";
import { DaemonManager } from "./routes/DaemonManager";
@ -10,52 +10,64 @@ import { TagView } from "./routes/tag";
* Router for the main Explorer interface
*/
export function createExplorerRouter() {
return createBrowserRouter([
{
path: "/",
element: <ExplorerLayout />,
children: [
{
index: true,
element: <Overview />,
},
{
path: "explorer",
element: <ExplorerView />,
},
{
path: "location/:locationId",
element: <ExplorerView />,
},
{
path: "location/:locationId/*",
element: <ExplorerView />,
},
{
path: "favorites",
element: <div className="flex items-center justify-center h-full text-ink">Favorites (coming soon)</div>,
},
{
path: "recents",
element: <div className="flex items-center justify-center h-full text-ink">Recents (coming soon)</div>,
},
{
path: "tag/:tagId",
element: <TagView />,
},
{
path: "search",
element: <div className="flex items-center justify-center h-full text-ink">Search (coming soon)</div>,
},
{
path: "jobs",
element: <JobsScreen />,
},
{
path: "daemon",
element: <DaemonManager />,
},
],
},
]);
return createBrowserRouter([
{
path: "/",
element: <ExplorerLayout />,
children: [
{
index: true,
element: <Overview />,
},
{
path: "explorer",
element: <ExplorerView />,
},
{
path: "location/:locationId",
element: <ExplorerView />,
},
{
path: "location/:locationId/*",
element: <ExplorerView />,
},
{
path: "favorites",
element: (
<div className="flex items-center justify-center h-full text-ink">
Favorites (coming soon)
</div>
),
},
{
path: "recents",
element: (
<div className="flex items-center justify-center h-full text-ink">
Recents (coming soon)
</div>
),
},
{
path: "tag/:tagId",
element: <TagView />,
},
{
path: "search",
element: (
<div className="flex items-center justify-center h-full text-ink">
Search (coming soon)
</div>
),
},
{
path: "jobs",
element: <JobsScreen />,
},
{
path: "daemon",
element: <DaemonManager />,
},
],
},
]);
}

View File

@ -1,206 +1,220 @@
import { useParams } from 'react-router-dom';
import { CaretRight, Funnel } from '@phosphor-icons/react';
import { Fragment } from 'react';
import { useNormalizedQuery } from '../../context';
import { ExplorerView } from '../../components/explorer';
import type { Tag } from '@sd/ts-client';
import { useParams } from "react-router-dom";
import { CaretRight, Funnel } from "@phosphor-icons/react";
import { Fragment } from "react";
import { useNormalizedQuery } from "../../context";
import { ExplorerView } from "../../components/Explorer";
import type { Tag } from "@sd/ts-client";
/**
* Tag Explorer View
* Shows all files tagged with a specific tag, with hierarchy awareness and filtering
*/
export function TagView() {
const { tagId } = useParams<{ tagId: string }>();
const { tagId } = useParams<{ tagId: string }>();
// Fetch the tag details
const { data: tagData, isLoading: tagLoading } = useNormalizedQuery({
wireMethod: 'query:tags.by_id',
input: { tag_id: tagId },
resourceType: 'tag',
resourceId: tagId,
enabled: !!tagId
});
// Fetch the tag details
const { data: tagData, isLoading: tagLoading } = useNormalizedQuery({
wireMethod: "query:tags.by_id",
input: { tag_id: tagId },
resourceType: "tag",
resourceId: tagId,
enabled: !!tagId,
});
// Fetch tag ancestors for breadcrumb
const { data: ancestorsData } = useNormalizedQuery({
wireMethod: 'query:tags.ancestors',
input: { tag_id: tagId },
resourceType: 'tag',
resourceId: tagId,
enabled: !!tagId
});
// Fetch tag ancestors for breadcrumb
const { data: ancestorsData } = useNormalizedQuery({
wireMethod: "query:tags.ancestors",
input: { tag_id: tagId },
resourceType: "tag",
resourceId: tagId,
enabled: !!tagId,
});
// Fetch tag children for quick filters
const { data: childrenData } = useNormalizedQuery({
wireMethod: 'query:tags.children',
input: { tag_id: tagId },
resourceType: 'tag',
resourceId: tagId,
enabled: !!tagId
});
// Fetch tag children for quick filters
const { data: childrenData } = useNormalizedQuery({
wireMethod: "query:tags.children",
input: { tag_id: tagId },
resourceType: "tag",
resourceId: tagId,
enabled: !!tagId,
});
// Fetch related tags for suggestions
const { data: relatedData } = useNormalizedQuery({
wireMethod: 'query:tags.related',
input: { tag_id: tagId },
resourceType: 'tag',
resourceId: tagId,
enabled: !!tagId
});
// Fetch related tags for suggestions
const { data: relatedData } = useNormalizedQuery({
wireMethod: "query:tags.related",
input: { tag_id: tagId },
resourceType: "tag",
resourceId: tagId,
enabled: !!tagId,
});
// Fetch files with this tag
const { data: filesData, isLoading: filesLoading } = useNormalizedQuery({
wireMethod: 'query:files.by_tag',
input: {
tag_id: tagId,
include_children: false, // TODO: Make this toggleable
min_confidence: 0.0
},
resourceType: 'file',
enabled: !!tagId
});
// Fetch files with this tag
const { data: filesData, isLoading: filesLoading } = useNormalizedQuery({
wireMethod: "query:files.by_tag",
input: {
tag_id: tagId,
include_children: false, // TODO: Make this toggleable
min_confidence: 0.0,
},
resourceType: "file",
enabled: !!tagId,
});
const tag = tagData?.tag;
const ancestors = ancestorsData?.ancestors ?? [];
const children = childrenData?.children ?? [];
const related = relatedData?.related ?? [];
const files = filesData?.files ?? [];
const tag = tagData?.tag;
const ancestors = ancestorsData?.ancestors ?? [];
const children = childrenData?.children ?? [];
const related = relatedData?.related ?? [];
const files = filesData?.files ?? [];
if (tagLoading) {
return (
<div className="flex items-center justify-center h-full">
<span className="text-ink-dull">Loading tag...</span>
</div>
);
}
if (tagLoading) {
return (
<div className="flex items-center justify-center h-full">
<span className="text-ink-dull">Loading tag...</span>
</div>
);
}
if (!tag) {
return (
<div className="flex items-center justify-center h-full">
<span className="text-ink-dull">Tag not found</span>
</div>
);
}
if (!tag) {
return (
<div className="flex items-center justify-center h-full">
<span className="text-ink-dull">Tag not found</span>
</div>
);
}
return (
<div className="flex h-full">
{/* Main Content */}
<div className="flex-1 flex flex-col">
{/* Header */}
<div className="px-4 py-3 border-b border-app-line space-y-3">
{/* Breadcrumb */}
<div className="flex items-center gap-2 text-sm">
{ancestors.map((ancestor, i) => (
<Fragment key={ancestor.id}>
<button
onClick={() => window.location.href = `/tag/${ancestor.id}`}
className="text-ink-dull hover:text-ink font-medium transition-colors"
>
{ancestor.canonical_name}
</button>
<CaretRight size={12} className="text-ink-faint" />
</Fragment>
))}
<div className="flex items-center gap-2">
{tag.icon ? (
<span style={{ color: tag.color || '#3B82F6' }}>
{/* TODO: Render icon */}
</span>
) : (
<span
className="size-3 rounded-full"
style={{ backgroundColor: tag.color || '#3B82F6' }}
/>
)}
<span className="text-ink font-semibold">{tag.canonical_name}</span>
</div>
</div>
return (
<div className="flex h-full">
{/* Main Content */}
<div className="flex-1 flex flex-col">
{/* Header */}
<div className="px-4 py-3 border-b border-app-line space-y-3">
{/* Breadcrumb */}
<div className="flex items-center gap-2 text-sm">
{ancestors.map((ancestor, i) => (
<Fragment key={ancestor.id}>
<button
onClick={() => (window.location.href = `/tag/${ancestor.id}`)}
className="text-ink-dull hover:text-ink font-medium transition-colors"
>
{ancestor.canonical_name}
</button>
<CaretRight size={12} className="text-ink-faint" />
</Fragment>
))}
<div className="flex items-center gap-2">
{tag.icon ? (
<span style={{ color: tag.color || "#3B82F6" }}>
{/* TODO: Render icon */}
</span>
) : (
<span
className="size-3 rounded-full"
style={{ backgroundColor: tag.color || "#3B82F6" }}
/>
)}
<span className="text-ink font-semibold">
{tag.canonical_name}
</span>
</div>
</div>
{/* Options Row */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{/* TODO: Add filters button */}
<button className="flex items-center gap-2 px-3 py-1.5 rounded-md bg-app-box border border-app-line text-sm hover:bg-app-hover transition-colors">
<Funnel size={14} />
<span>Filters</span>
</button>
</div>
{/* Options Row */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{/* TODO: Add filters button */}
<button className="flex items-center gap-2 px-3 py-1.5 rounded-md bg-app-box border border-app-line text-sm hover:bg-app-hover transition-colors">
<Funnel size={14} />
<span>Filters</span>
</button>
</div>
{/* File Count */}
<span className="text-sm text-ink-dull">
{filesLoading ? 'Loading...' : `${files.length} ${files.length === 1 ? 'file' : 'files'}`}
</span>
</div>
{/* File Count */}
<span className="text-sm text-ink-dull">
{filesLoading
? "Loading..."
: `${files.length} ${files.length === 1 ? "file" : "files"}`}
</span>
</div>
{/* Child Tag Quick Filters */}
{children.length > 0 && (
<div className="flex items-center gap-2 flex-wrap">
<span className="text-xs font-semibold text-ink-dull">Children:</span>
{children.map((child) => (
<button
key={child.id}
onClick={() => window.location.href = `/tag/${child.id}`}
className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-app-box hover:bg-app-hover border border-app-line text-xs font-medium transition-colors"
style={{ color: child.color || '#3B82F6' }}
>
<span
className="size-1.5 rounded-full"
style={{ backgroundColor: child.color || '#3B82F6' }}
/>
{child.canonical_name}
</button>
))}
</div>
)}
</div>
{/* Child Tag Quick Filters */}
{children.length > 0 && (
<div className="flex items-center gap-2 flex-wrap">
<span className="text-xs font-semibold text-ink-dull">
Children:
</span>
{children.map((child) => (
<button
key={child.id}
onClick={() => (window.location.href = `/tag/${child.id}`)}
className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-app-box hover:bg-app-hover border border-app-line text-xs font-medium transition-colors"
style={{ color: child.color || "#3B82F6" }}
>
<span
className="size-1.5 rounded-full"
style={{ backgroundColor: child.color || "#3B82F6" }}
/>
{child.canonical_name}
</button>
))}
</div>
)}
</div>
{/* Explorer View */}
<div className="flex-1 overflow-auto">
{filesLoading ? (
<div className="flex items-center justify-center h-full">
<span className="text-ink-dull">Loading files...</span>
</div>
) : files.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full gap-2">
<span className="text-ink-dull">No files with this tag</span>
<span className="text-xs text-ink-faint">Files will appear here when you tag them</span>
</div>
) : (
<ExplorerView />
)}
</div>
</div>
{/* Explorer View */}
<div className="flex-1 overflow-auto">
{filesLoading ? (
<div className="flex items-center justify-center h-full">
<span className="text-ink-dull">Loading files...</span>
</div>
) : files.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full gap-2">
<span className="text-ink-dull">No files with this tag</span>
<span className="text-xs text-ink-faint">
Files will appear here when you tag them
</span>
</div>
) : (
<ExplorerView />
)}
</div>
</div>
{/* Sidebar: Related Tags */}
{related.length > 0 && (
<aside className="w-64 border-l border-app-line p-4 space-y-4 overflow-y-auto">
<div>
<h4 className="text-sm font-semibold text-ink-dull mb-2">Related Tags</h4>
<div className="space-y-1">
{related.map((relatedTag) => (
<button
key={relatedTag.id}
onClick={() => window.location.href = `/tag/${relatedTag.id}`}
className="flex items-center justify-between w-full px-2 py-1.5 rounded-md hover:bg-app-hover text-sm transition-colors"
>
<div className="flex items-center gap-2">
<span
className="size-2 rounded-full"
style={{ backgroundColor: relatedTag.color || '#3B82F6' }}
/>
<span className="text-ink">{relatedTag.canonical_name}</span>
</div>
{relatedTag.co_occurrence_count && (
<span className="text-xs text-ink-faint">
{relatedTag.co_occurrence_count}
</span>
)}
</button>
))}
</div>
</div>
</aside>
)}
</div>
);
{/* Sidebar: Related Tags */}
{related.length > 0 && (
<aside className="w-64 border-l border-app-line p-4 space-y-4 overflow-y-auto">
<div>
<h4 className="text-sm font-semibold text-ink-dull mb-2">
Related Tags
</h4>
<div className="space-y-1">
{related.map((relatedTag) => (
<button
key={relatedTag.id}
onClick={() =>
(window.location.href = `/tag/${relatedTag.id}`)
}
className="flex items-center justify-between w-full px-2 py-1.5 rounded-md hover:bg-app-hover text-sm transition-colors"
>
<div className="flex items-center gap-2">
<span
className="size-2 rounded-full"
style={{ backgroundColor: relatedTag.color || "#3B82F6" }}
/>
<span className="text-ink">
{relatedTag.canonical_name}
</span>
</div>
{relatedTag.co_occurrence_count && (
<span className="text-xs text-ink-faint">
{relatedTag.co_occurrence_count}
</span>
)}
</button>
))}
</div>
</div>
</aside>
)}
</div>
);
}

View File

@ -179,8 +179,7 @@ pub fn bundle_libheif_from_homebrew(root: &Path) -> Result<()> {
fs::create_dir_all(&target_dir)?;
let dest = target_dir.join("libheif.1.dylib");
fs::copy(homebrew_libheif, &dest)
.context("Failed to copy libheif from Homebrew")?;
fs::copy(homebrew_libheif, &dest).context("Failed to copy libheif from Homebrew")?;
println!(" ✓ Bundled libheif from Homebrew");
}
@ -189,7 +188,7 @@ pub fn bundle_libheif_from_homebrew(root: &Path) -> Result<()> {
}
/// Create symlinks for shared libraries on Linux
pub fn symlink_libs_linux(_root: &Path, _native_deps: &Path) -> Result<()> {
pub fn symlink_libs_linux(root: &Path, native_deps: &Path) -> Result<()> {
#[cfg(target_os = "linux")]
{
use std::os::unix::fs as unix_fs;