mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
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:
parent
e3ba15632f
commit
f01a2994f1
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -5,7 +5,7 @@
|
|||||||
path = apps/macos
|
path = apps/macos
|
||||||
url = https://github.com/spacedriveapp/macos.git
|
url = https://github.com/spacedriveapp/macos.git
|
||||||
[submodule "workbench"]
|
[submodule "workbench"]
|
||||||
path = workbench
|
path = docs/workbench
|
||||||
url = https://github.com/spacedriveapp/design.git
|
url = https://github.com/spacedriveapp/design.git
|
||||||
[submodule "apps/landing"]
|
[submodule "apps/landing"]
|
||||||
path = apps/landing
|
path = apps/landing
|
||||||
|
|||||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -4909,6 +4909,17 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"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]]
|
[[package]]
|
||||||
name = "hostname-validator"
|
name = "hostname-validator"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@ -10496,6 +10507,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
"hostname",
|
||||||
"hound",
|
"hound",
|
||||||
"if-watch",
|
"if-watch",
|
||||||
"image",
|
"image",
|
||||||
|
|||||||
6338
apps/tauri/src-tauri/gen/schemas/linux-schema.json
Normal file
6338
apps/tauri/src-tauri/gen/schemas/linux-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -58,6 +58,7 @@ specta-swift = { git = "https://github.com/jamiepine/specta", branch = "main" }
|
|||||||
specta-typescript = { git = "https://github.com/jamiepine/specta", branch = "main" }
|
specta-typescript = { git = "https://github.com/jamiepine/specta", branch = "main" }
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
|
hostname = "^0.4"
|
||||||
|
|
||||||
|
|
||||||
# Error handling
|
# Error handling
|
||||||
|
|||||||
@ -284,6 +284,8 @@ fn parse_subvolume_info(output: &str) -> VolumeResult<SubvolumeInfo> {
|
|||||||
|
|
||||||
/// Enhance volume with Btrfs-specific information from mount point
|
/// Enhance volume with Btrfs-specific information from mount point
|
||||||
pub async fn enhance_volume_from_mount(volume: &mut Volume) -> VolumeResult<()> {
|
pub async fn enhance_volume_from_mount(volume: &mut Volume) -> VolumeResult<()> {
|
||||||
|
use super::FilesystemHandler;
|
||||||
|
|
||||||
let handler = BtrfsHandler::new();
|
let handler = BtrfsHandler::new();
|
||||||
handler.enhance_volume(volume).await
|
handler.enhance_volume(volume).await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -373,7 +373,9 @@ fn parse_zfs_size(size_str: &str) -> Option<u64> {
|
|||||||
|
|
||||||
/// Enhance volume with ZFS-specific information from mount point
|
/// Enhance volume with ZFS-specific information from mount point
|
||||||
pub async fn enhance_volume_from_mount(volume: &mut Volume) -> VolumeResult<()> {
|
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
|
handler.enhance_volume(volume).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,12 @@ import { Dialogs } from "@sd/ui";
|
|||||||
import { Inspector, type InspectorVariant } from "./Inspector";
|
import { Inspector, type InspectorVariant } from "./Inspector";
|
||||||
import { TopBarProvider, TopBar } from "./TopBar";
|
import { TopBarProvider, TopBar } from "./TopBar";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { ExplorerProvider, useExplorer, Sidebar, getSpaceItemKeyFromRoute } from "./components/explorer";
|
import {
|
||||||
|
ExplorerProvider,
|
||||||
|
useExplorer,
|
||||||
|
Sidebar,
|
||||||
|
getSpaceItemKeyFromRoute,
|
||||||
|
} from "./components/Explorer";
|
||||||
import {
|
import {
|
||||||
SelectionProvider,
|
SelectionProvider,
|
||||||
useSelection,
|
useSelection,
|
||||||
@ -19,7 +24,10 @@ import {
|
|||||||
import { KeyboardHandler } from "./components/Explorer/KeyboardHandler";
|
import { KeyboardHandler } from "./components/Explorer/KeyboardHandler";
|
||||||
import { TagAssignmentMode } from "./components/Explorer/TagAssignmentMode";
|
import { TagAssignmentMode } from "./components/Explorer/TagAssignmentMode";
|
||||||
import { SpacesSidebar } from "./components/SpacesSidebar";
|
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 { createExplorerRouter } from "./router";
|
||||||
import { useNormalizedCache } from "./context";
|
import { useNormalizedCache } from "./context";
|
||||||
import { usePlatform } from "./platform";
|
import { usePlatform } from "./platform";
|
||||||
@ -49,7 +57,10 @@ export function ExplorerLayout() {
|
|||||||
|
|
||||||
// Sync route with explorer context for view preferences
|
// Sync route with explorer context for view preferences
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const spaceItemKey = getSpaceItemKeyFromRoute(location.pathname, location.search);
|
const spaceItemKey = getSpaceItemKeyFromRoute(
|
||||||
|
location.pathname,
|
||||||
|
location.search,
|
||||||
|
);
|
||||||
setSpaceItemId(spaceItemKey);
|
setSpaceItemId(spaceItemKey);
|
||||||
}, [location.pathname, location.search, setSpaceItemId]);
|
}, [location.pathname, location.search, setSpaceItemId]);
|
||||||
|
|
||||||
@ -129,7 +140,9 @@ export function ExplorerLayout() {
|
|||||||
|
|
||||||
<TopBar
|
<TopBar
|
||||||
sidebarWidth={sidebarVisible ? 224 : 0}
|
sidebarWidth={sidebarVisible ? 224 : 0}
|
||||||
inspectorWidth={inspectorVisible && !isOverview && !isKnowledgeView ? 284 : 0}
|
inspectorWidth={
|
||||||
|
inspectorVisible && !isOverview && !isKnowledgeView ? 284 : 0
|
||||||
|
}
|
||||||
isPreviewActive={isPreviewActive}
|
isPreviewActive={isPreviewActive}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -193,7 +206,9 @@ export function ExplorerLayout() {
|
|||||||
hasPrevious={false}
|
hasPrevious={false}
|
||||||
hasNext={false}
|
hasNext={false}
|
||||||
sidebarWidth={sidebarVisible ? 220 : 0}
|
sidebarWidth={sidebarVisible ? 220 : 0}
|
||||||
inspectorWidth={inspectorVisible && !isOverview && !isKnowledgeView ? 280 : 0}
|
inspectorWidth={
|
||||||
|
inspectorVisible && !isOverview && !isKnowledgeView ? 280 : 0
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createBrowserRouter } from "react-router-dom";
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
import { Overview } from "./routes/overview";
|
import { Overview } from "./routes/overview";
|
||||||
import { ExplorerView } from "./components/explorer";
|
import { ExplorerView } from "./components/Explorer";
|
||||||
import { ExplorerLayout } from "./Explorer";
|
import { ExplorerLayout } from "./Explorer";
|
||||||
import { JobsScreen } from "./components/JobManager";
|
import { JobsScreen } from "./components/JobManager";
|
||||||
import { DaemonManager } from "./routes/DaemonManager";
|
import { DaemonManager } from "./routes/DaemonManager";
|
||||||
@ -10,52 +10,64 @@ import { TagView } from "./routes/tag";
|
|||||||
* Router for the main Explorer interface
|
* Router for the main Explorer interface
|
||||||
*/
|
*/
|
||||||
export function createExplorerRouter() {
|
export function createExplorerRouter() {
|
||||||
return createBrowserRouter([
|
return createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: <ExplorerLayout />,
|
element: <ExplorerLayout />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
index: true,
|
index: true,
|
||||||
element: <Overview />,
|
element: <Overview />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "explorer",
|
path: "explorer",
|
||||||
element: <ExplorerView />,
|
element: <ExplorerView />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "location/:locationId",
|
path: "location/:locationId",
|
||||||
element: <ExplorerView />,
|
element: <ExplorerView />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "location/:locationId/*",
|
path: "location/:locationId/*",
|
||||||
element: <ExplorerView />,
|
element: <ExplorerView />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "favorites",
|
path: "favorites",
|
||||||
element: <div className="flex items-center justify-center h-full text-ink">Favorites (coming soon)</div>,
|
element: (
|
||||||
},
|
<div className="flex items-center justify-center h-full text-ink">
|
||||||
{
|
Favorites (coming soon)
|
||||||
path: "recents",
|
</div>
|
||||||
element: <div className="flex items-center justify-center h-full text-ink">Recents (coming soon)</div>,
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "tag/:tagId",
|
path: "recents",
|
||||||
element: <TagView />,
|
element: (
|
||||||
},
|
<div className="flex items-center justify-center h-full text-ink">
|
||||||
{
|
Recents (coming soon)
|
||||||
path: "search",
|
</div>
|
||||||
element: <div className="flex items-center justify-center h-full text-ink">Search (coming soon)</div>,
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "jobs",
|
path: "tag/:tagId",
|
||||||
element: <JobsScreen />,
|
element: <TagView />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "daemon",
|
path: "search",
|
||||||
element: <DaemonManager />,
|
element: (
|
||||||
},
|
<div className="flex items-center justify-center h-full text-ink">
|
||||||
],
|
Search (coming soon)
|
||||||
},
|
</div>
|
||||||
]);
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "jobs",
|
||||||
|
element: <JobsScreen />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "daemon",
|
||||||
|
element: <DaemonManager />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,206 +1,220 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from "react-router-dom";
|
||||||
import { CaretRight, Funnel } from '@phosphor-icons/react';
|
import { CaretRight, Funnel } from "@phosphor-icons/react";
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from "react";
|
||||||
import { useNormalizedQuery } from '../../context';
|
import { useNormalizedQuery } from "../../context";
|
||||||
import { ExplorerView } from '../../components/explorer';
|
import { ExplorerView } from "../../components/Explorer";
|
||||||
import type { Tag } from '@sd/ts-client';
|
import type { Tag } from "@sd/ts-client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag Explorer View
|
* Tag Explorer View
|
||||||
* Shows all files tagged with a specific tag, with hierarchy awareness and filtering
|
* Shows all files tagged with a specific tag, with hierarchy awareness and filtering
|
||||||
*/
|
*/
|
||||||
export function TagView() {
|
export function TagView() {
|
||||||
const { tagId } = useParams<{ tagId: string }>();
|
const { tagId } = useParams<{ tagId: string }>();
|
||||||
|
|
||||||
// Fetch the tag details
|
// Fetch the tag details
|
||||||
const { data: tagData, isLoading: tagLoading } = useNormalizedQuery({
|
const { data: tagData, isLoading: tagLoading } = useNormalizedQuery({
|
||||||
wireMethod: 'query:tags.by_id',
|
wireMethod: "query:tags.by_id",
|
||||||
input: { tag_id: tagId },
|
input: { tag_id: tagId },
|
||||||
resourceType: 'tag',
|
resourceType: "tag",
|
||||||
resourceId: tagId,
|
resourceId: tagId,
|
||||||
enabled: !!tagId
|
enabled: !!tagId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch tag ancestors for breadcrumb
|
// Fetch tag ancestors for breadcrumb
|
||||||
const { data: ancestorsData } = useNormalizedQuery({
|
const { data: ancestorsData } = useNormalizedQuery({
|
||||||
wireMethod: 'query:tags.ancestors',
|
wireMethod: "query:tags.ancestors",
|
||||||
input: { tag_id: tagId },
|
input: { tag_id: tagId },
|
||||||
resourceType: 'tag',
|
resourceType: "tag",
|
||||||
resourceId: tagId,
|
resourceId: tagId,
|
||||||
enabled: !!tagId
|
enabled: !!tagId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch tag children for quick filters
|
// Fetch tag children for quick filters
|
||||||
const { data: childrenData } = useNormalizedQuery({
|
const { data: childrenData } = useNormalizedQuery({
|
||||||
wireMethod: 'query:tags.children',
|
wireMethod: "query:tags.children",
|
||||||
input: { tag_id: tagId },
|
input: { tag_id: tagId },
|
||||||
resourceType: 'tag',
|
resourceType: "tag",
|
||||||
resourceId: tagId,
|
resourceId: tagId,
|
||||||
enabled: !!tagId
|
enabled: !!tagId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch related tags for suggestions
|
// Fetch related tags for suggestions
|
||||||
const { data: relatedData } = useNormalizedQuery({
|
const { data: relatedData } = useNormalizedQuery({
|
||||||
wireMethod: 'query:tags.related',
|
wireMethod: "query:tags.related",
|
||||||
input: { tag_id: tagId },
|
input: { tag_id: tagId },
|
||||||
resourceType: 'tag',
|
resourceType: "tag",
|
||||||
resourceId: tagId,
|
resourceId: tagId,
|
||||||
enabled: !!tagId
|
enabled: !!tagId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch files with this tag
|
// Fetch files with this tag
|
||||||
const { data: filesData, isLoading: filesLoading } = useNormalizedQuery({
|
const { data: filesData, isLoading: filesLoading } = useNormalizedQuery({
|
||||||
wireMethod: 'query:files.by_tag',
|
wireMethod: "query:files.by_tag",
|
||||||
input: {
|
input: {
|
||||||
tag_id: tagId,
|
tag_id: tagId,
|
||||||
include_children: false, // TODO: Make this toggleable
|
include_children: false, // TODO: Make this toggleable
|
||||||
min_confidence: 0.0
|
min_confidence: 0.0,
|
||||||
},
|
},
|
||||||
resourceType: 'file',
|
resourceType: "file",
|
||||||
enabled: !!tagId
|
enabled: !!tagId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tag = tagData?.tag;
|
const tag = tagData?.tag;
|
||||||
const ancestors = ancestorsData?.ancestors ?? [];
|
const ancestors = ancestorsData?.ancestors ?? [];
|
||||||
const children = childrenData?.children ?? [];
|
const children = childrenData?.children ?? [];
|
||||||
const related = relatedData?.related ?? [];
|
const related = relatedData?.related ?? [];
|
||||||
const files = filesData?.files ?? [];
|
const files = filesData?.files ?? [];
|
||||||
|
|
||||||
if (tagLoading) {
|
if (tagLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<span className="text-ink-dull">Loading tag...</span>
|
<span className="text-ink-dull">Loading tag...</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<span className="text-ink-dull">Tag not found</span>
|
<span className="text-ink-dull">Tag not found</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full">
|
<div className="flex h-full">
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="px-4 py-3 border-b border-app-line space-y-3">
|
<div className="px-4 py-3 border-b border-app-line space-y-3">
|
||||||
{/* Breadcrumb */}
|
{/* Breadcrumb */}
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
{ancestors.map((ancestor, i) => (
|
{ancestors.map((ancestor, i) => (
|
||||||
<Fragment key={ancestor.id}>
|
<Fragment key={ancestor.id}>
|
||||||
<button
|
<button
|
||||||
onClick={() => window.location.href = `/tag/${ancestor.id}`}
|
onClick={() => (window.location.href = `/tag/${ancestor.id}`)}
|
||||||
className="text-ink-dull hover:text-ink font-medium transition-colors"
|
className="text-ink-dull hover:text-ink font-medium transition-colors"
|
||||||
>
|
>
|
||||||
{ancestor.canonical_name}
|
{ancestor.canonical_name}
|
||||||
</button>
|
</button>
|
||||||
<CaretRight size={12} className="text-ink-faint" />
|
<CaretRight size={12} className="text-ink-faint" />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{tag.icon ? (
|
{tag.icon ? (
|
||||||
<span style={{ color: tag.color || '#3B82F6' }}>
|
<span style={{ color: tag.color || "#3B82F6" }}>
|
||||||
{/* TODO: Render icon */}
|
{/* TODO: Render icon */}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
className="size-3 rounded-full"
|
className="size-3 rounded-full"
|
||||||
style={{ backgroundColor: tag.color || '#3B82F6' }}
|
style={{ backgroundColor: tag.color || "#3B82F6" }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="text-ink font-semibold">{tag.canonical_name}</span>
|
<span className="text-ink font-semibold">
|
||||||
</div>
|
{tag.canonical_name}
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Options Row */}
|
{/* Options Row */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* TODO: Add filters button */}
|
{/* 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">
|
<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} />
|
<Funnel size={14} />
|
||||||
<span>Filters</span>
|
<span>Filters</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* File Count */}
|
{/* File Count */}
|
||||||
<span className="text-sm text-ink-dull">
|
<span className="text-sm text-ink-dull">
|
||||||
{filesLoading ? 'Loading...' : `${files.length} ${files.length === 1 ? 'file' : 'files'}`}
|
{filesLoading
|
||||||
</span>
|
? "Loading..."
|
||||||
</div>
|
: `${files.length} ${files.length === 1 ? "file" : "files"}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Child Tag Quick Filters */}
|
{/* Child Tag Quick Filters */}
|
||||||
{children.length > 0 && (
|
{children.length > 0 && (
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<span className="text-xs font-semibold text-ink-dull">Children:</span>
|
<span className="text-xs font-semibold text-ink-dull">
|
||||||
{children.map((child) => (
|
Children:
|
||||||
<button
|
</span>
|
||||||
key={child.id}
|
{children.map((child) => (
|
||||||
onClick={() => window.location.href = `/tag/${child.id}`}
|
<button
|
||||||
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"
|
key={child.id}
|
||||||
style={{ color: child.color || '#3B82F6' }}
|
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"
|
||||||
<span
|
style={{ color: child.color || "#3B82F6" }}
|
||||||
className="size-1.5 rounded-full"
|
>
|
||||||
style={{ backgroundColor: child.color || '#3B82F6' }}
|
<span
|
||||||
/>
|
className="size-1.5 rounded-full"
|
||||||
{child.canonical_name}
|
style={{ backgroundColor: child.color || "#3B82F6" }}
|
||||||
</button>
|
/>
|
||||||
))}
|
{child.canonical_name}
|
||||||
</div>
|
</button>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Explorer View */}
|
{/* Explorer View */}
|
||||||
<div className="flex-1 overflow-auto">
|
<div className="flex-1 overflow-auto">
|
||||||
{filesLoading ? (
|
{filesLoading ? (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<span className="text-ink-dull">Loading files...</span>
|
<span className="text-ink-dull">Loading files...</span>
|
||||||
</div>
|
</div>
|
||||||
) : files.length === 0 ? (
|
) : files.length === 0 ? (
|
||||||
<div className="flex flex-col items-center justify-center h-full gap-2">
|
<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-ink-dull">No files with this tag</span>
|
||||||
<span className="text-xs text-ink-faint">Files will appear here when you tag them</span>
|
<span className="text-xs text-ink-faint">
|
||||||
</div>
|
Files will appear here when you tag them
|
||||||
) : (
|
</span>
|
||||||
<ExplorerView />
|
</div>
|
||||||
)}
|
) : (
|
||||||
</div>
|
<ExplorerView />
|
||||||
</div>
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Sidebar: Related Tags */}
|
{/* Sidebar: Related Tags */}
|
||||||
{related.length > 0 && (
|
{related.length > 0 && (
|
||||||
<aside className="w-64 border-l border-app-line p-4 space-y-4 overflow-y-auto">
|
<aside className="w-64 border-l border-app-line p-4 space-y-4 overflow-y-auto">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-semibold text-ink-dull mb-2">Related Tags</h4>
|
<h4 className="text-sm font-semibold text-ink-dull mb-2">
|
||||||
<div className="space-y-1">
|
Related Tags
|
||||||
{related.map((relatedTag) => (
|
</h4>
|
||||||
<button
|
<div className="space-y-1">
|
||||||
key={relatedTag.id}
|
{related.map((relatedTag) => (
|
||||||
onClick={() => window.location.href = `/tag/${relatedTag.id}`}
|
<button
|
||||||
className="flex items-center justify-between w-full px-2 py-1.5 rounded-md hover:bg-app-hover text-sm transition-colors"
|
key={relatedTag.id}
|
||||||
>
|
onClick={() =>
|
||||||
<div className="flex items-center gap-2">
|
(window.location.href = `/tag/${relatedTag.id}`)
|
||||||
<span
|
}
|
||||||
className="size-2 rounded-full"
|
className="flex items-center justify-between w-full px-2 py-1.5 rounded-md hover:bg-app-hover text-sm transition-colors"
|
||||||
style={{ backgroundColor: relatedTag.color || '#3B82F6' }}
|
>
|
||||||
/>
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-ink">{relatedTag.canonical_name}</span>
|
<span
|
||||||
</div>
|
className="size-2 rounded-full"
|
||||||
{relatedTag.co_occurrence_count && (
|
style={{ backgroundColor: relatedTag.color || "#3B82F6" }}
|
||||||
<span className="text-xs text-ink-faint">
|
/>
|
||||||
{relatedTag.co_occurrence_count}
|
<span className="text-ink">
|
||||||
</span>
|
{relatedTag.canonical_name}
|
||||||
)}
|
</span>
|
||||||
</button>
|
</div>
|
||||||
))}
|
{relatedTag.co_occurrence_count && (
|
||||||
</div>
|
<span className="text-xs text-ink-faint">
|
||||||
</div>
|
{relatedTag.co_occurrence_count}
|
||||||
</aside>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</button>
|
||||||
);
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,8 +179,7 @@ pub fn bundle_libheif_from_homebrew(root: &Path) -> Result<()> {
|
|||||||
fs::create_dir_all(&target_dir)?;
|
fs::create_dir_all(&target_dir)?;
|
||||||
|
|
||||||
let dest = target_dir.join("libheif.1.dylib");
|
let dest = target_dir.join("libheif.1.dylib");
|
||||||
fs::copy(homebrew_libheif, &dest)
|
fs::copy(homebrew_libheif, &dest).context("Failed to copy libheif from Homebrew")?;
|
||||||
.context("Failed to copy libheif from Homebrew")?;
|
|
||||||
|
|
||||||
println!(" ✓ Bundled 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
|
/// 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")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
use std::os::unix::fs as unix_fs;
|
use std::os::unix::fs as unix_fs;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user