Fix tailwind config and add sass dependency

This commit is contained in:
Jamie Pine 2025-12-02 14:26:26 -08:00
parent dbd6c61621
commit 792d112d1c
11 changed files with 118 additions and 15669 deletions

@ -1 +1 @@
Subproject commit 85732cc95de98440f9c066042dfbbb809a235936
Subproject commit 25bf180122c207661a174b823d2c0f2fa84af550

View File

@ -1,10 +1,13 @@
import AppKit
import SwiftRs
// Forward declaration of Rust callback
// Forward declarations of Rust callbacks
@_silgen_name("rust_drag_ended_callback")
func rust_drag_ended_callback(_ sessionId: UnsafePointer<CChar>, _ wasDropped: Bool)
@_silgen_name("rust_drag_moved_callback")
func rust_drag_moved_callback(_ sessionId: UnsafePointer<CChar>, _ x: Double, _ y: Double)
private var activeDragSources: [String: NativeDragSource] = [:]
private var dragSourcesLock = NSLock()
@ -282,15 +285,10 @@ class NativeDragSource: NSObject, NSDraggingSource, NSFilePromiseProviderDelegat
)
overlay.setFrameOrigin(centeredPoint)
NotificationCenter.default.post(
name: Notification.Name("spacedrive.drag.moved"),
object: nil,
userInfo: [
"sessionId": sessionId,
"x": screenPoint.x,
"y": screenPoint.y
]
)
// Call Rust callback to emit drag:moved event
sessionId.withCString { cString in
rust_drag_moved_callback(cString, screenPoint.x, screenPoint.y)
}
}
func draggingSession(

View File

@ -41,8 +41,9 @@ swift!(pub fn begin_native_drag(
swift!(pub fn end_native_drag(session_id: &SRString));
swift!(pub fn update_drag_overlay_position(session_id: &SRString, x: f64, y: f64));
// Callback from Swift when drag session ends
// Callbacks from Swift
static mut DRAG_ENDED_CALLBACK: Option<Box<dyn Fn(&str, bool) + Send + Sync>> = None;
static mut DRAG_MOVED_CALLBACK: Option<Box<dyn Fn(&str, f64, f64) + Send + Sync>> = None;
pub fn set_drag_ended_callback<F>(callback: F)
where
@ -53,6 +54,15 @@ where
}
}
pub fn set_drag_moved_callback<F>(callback: F)
where
F: Fn(&str, f64, f64) + Send + Sync + 'static,
{
unsafe {
DRAG_MOVED_CALLBACK = Some(Box::new(callback));
}
}
#[no_mangle]
pub extern "C" fn rust_drag_ended_callback(session_id: *const std::ffi::c_char, was_dropped: Bool) {
let session_id_str = unsafe {
@ -68,3 +78,19 @@ pub extern "C" fn rust_drag_ended_callback(session_id: *const std::ffi::c_char,
}
}
}
#[no_mangle]
pub extern "C" fn rust_drag_moved_callback(session_id: *const std::ffi::c_char, x: f64, y: f64) {
let session_id_str = unsafe {
std::ffi::CStr::from_ptr(session_id)
.to_string_lossy()
.into_owned()
};
unsafe {
let callback_ptr = &raw const DRAG_MOVED_CALLBACK;
if let Some(callback) = (*callback_ptr).as_ref() {
callback(&session_id_str, x, y);
}
}
}

View File

@ -1246,8 +1246,19 @@ fn main() {
}
}
// Setup drag ended callback
// Setup drag callbacks
let app_handle = app.handle().clone();
// Drag moved callback
let app_for_moved = app_handle.clone();
sd_desktop_macos::set_drag_moved_callback(
move |_session_id: &str, x: f64, y: f64| {
let coordinator = app_for_moved.state::<drag::DragCoordinator>();
coordinator.update_position(&app_for_moved, x, y);
}
);
// Drag ended callback
sd_desktop_macos::set_drag_ended_callback(
move |session_id: &str, was_dropped: bool| {
tracing::info!(

15569
bun.lock

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
# @jamiepine/assets
# @spacedriveapp/assets
Shared assets for Spacedrive applications.

View File

@ -1,9 +1,28 @@
{
"name": "@sd/assets",
"version": "1.0.0",
"name": "@spacedriveapp/assets",
"version": "1.0.1",
"license": "GPL-3.0-only",
"private": true,
"sideEffects": false,
"description": "Shared assets for Spacedrive applications",
"repository": {
"type": "git",
"url": "https://github.com/spacedriveapp/spacedrive.git",
"directory": "packages/assets"
},
"publishConfig": {
"access": "public"
},
"files": [
"icons",
"images",
"lottie",
"svgs",
"util",
"videos"
],
"exports": {
"./*": "./*"
},
"scripts": {
"gen": "node ./scripts/generate.mjs"
},

View File

@ -1,19 +1,18 @@
import { CaretRight } from '@phosphor-icons/react';
import clsx from 'clsx';
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect } from 'react';
import type {
SpaceGroup as SpaceGroupType,
SpaceItem as SpaceItemType,
} from '@sd/ts-client';
import { useSidebarStore, useLibraryMutation } from '@sd/ts-client';
import { useSidebarStore } from '@sd/ts-client';
import { SpaceItem } from './SpaceItem';
import { DeviceGroup } from './DeviceGroup';
import { DevicesGroup } from './DevicesGroup';
import { LocationsGroup } from './LocationsGroup';
import { VolumesGroup } from './VolumesGroup';
import { TagsGroup } from './TagsGroup';
import { getDragData, clearDragData, subscribeToDragState } from './dnd';
import { usePlatform } from '../../platform';
import { subscribeToDragState } from './dnd';
interface SpaceGroupProps {
group: SpaceGroupType;
@ -23,15 +22,11 @@ interface SpaceGroupProps {
export function SpaceGroup({ group, items, spaceId }: SpaceGroupProps) {
const { collapsedGroups, toggleGroup } = useSidebarStore();
const platform = usePlatform();
// Use backend's is_collapsed value as the source of truth, fallback to local state
const isCollapsed = group.is_collapsed ?? collapsedGroups.has(group.id);
// Drag-drop state for custom groups
const [isDragging, setIsDragging] = useState(false);
const [isHovering, setIsHovering] = useState(false);
const addItem = useLibraryMutation("spaces.add_item");
const groupRef = useRef<HTMLDivElement>(null);
// Only QuickAccess and Custom groups can accept drops
const canAcceptDrop = group.group_type === 'QuickAccess' || group.group_type === 'Custom';
@ -42,51 +37,7 @@ export function SpaceGroup({ group, items, spaceId }: SpaceGroupProps) {
return subscribeToDragState(setIsDragging);
}, [canAcceptDrop]);
// Listen for native drag events to track position and handle drop
useEffect(() => {
if (!platform.onDragEvent || !canAcceptDrop) return;
const unlisteners: Array<() => void> = [];
// Track drag position to detect when over this group
platform.onDragEvent("moved", (payload: { x: number; y: number }) => {
if (!groupRef.current) return;
const rect = groupRef.current.getBoundingClientRect();
const isOver = (
payload.x >= rect.left &&
payload.x <= rect.right &&
payload.y >= rect.top &&
payload.y <= rect.bottom
);
setIsHovering(isOver);
}).then(fn => unlisteners.push(fn));
// Handle drag end - check if dropped on this group
platform.onDragEvent("ended", async (payload: { result?: { type: string } }) => {
if (payload.result?.type === "Dropped" && isHovering && spaceId) {
const dragData = getDragData();
if (dragData) {
try {
await addItem.mutateAsync({
space_id: spaceId,
group_id: group.id,
item_type: { Path: { sd_path: dragData.sdPath } },
});
console.log("[SpaceGroup] Added item to group:", group.name);
} catch (err) {
console.error("Failed to add item to group:", err);
}
}
}
setIsDragging(false);
setIsHovering(false);
}).then(fn => unlisteners.push(fn));
return () => {
unlisteners.forEach(fn => fn());
};
}, [platform, canAcceptDrop, spaceId, group.id, group.name, addItem, isHovering]);
// TODO: Drop handling for groups - need to coordinate with parent to avoid duplicates
// Device groups are special - they show device info with children
if (typeof group.group_type === 'object' && 'Device' in group.group_type) {
@ -123,11 +74,9 @@ export function SpaceGroup({ group, items, spaceId }: SpaceGroupProps) {
// QuickAccess and Custom groups render stored items
return (
<div
ref={groupRef}
className={clsx(
"rounded-lg transition-colors",
isDragging && canAcceptDrop && "bg-accent/10 ring-2 ring-accent/50 ring-inset",
isDragging && isHovering && canAcceptDrop && "bg-accent/20 ring-accent"
"rounded-lg transition-colors"
// TODO: Add drop zone styling when group-level drops are implemented
)}
>
{/* Group Header */}
@ -149,15 +98,6 @@ export function SpaceGroup({ group, items, spaceId }: SpaceGroupProps) {
{items.map((item) => (
<SpaceItem key={item.id} item={item} />
))}
{/* Drop hint */}
{isDragging && canAcceptDrop && (
<div className={clsx(
"flex items-center justify-center py-2 text-xs font-medium transition-colors",
isHovering ? "text-accent" : "text-accent/70"
)}>
{isHovering ? "Release to add" : "Drop here"}
</div>
)}
</div>
)}
</div>

View File

@ -81,16 +81,19 @@ export function SpacesSidebar({ isPreviewActive = false }: SpacesSidebarProps) {
// Subscribe to drag state changes (from setDragData)
useEffect(() => {
return subscribeToDragState(setIsDragging);
return subscribeToDragState((dragging) => {
console.log("[Sidebar] Drag state changed:", dragging);
setIsDragging(dragging);
});
}, []);
// Listen for native drag events to track position and handle drop
// Listen for drag events to track position and handle drop
useEffect(() => {
if (!platform.onDragEvent) return;
const unlisteners: Array<() => void> = [];
// Track drag position to detect when over sidebar
// Track cursor position during drag to detect hover
platform.onDragEvent("moved", (payload: { x: number; y: number }) => {
if (!dropZoneRef.current) return;
@ -101,18 +104,28 @@ export function SpacesSidebar({ isPreviewActive = false }: SpacesSidebarProps) {
payload.y >= rect.top &&
payload.y <= rect.bottom
);
setIsHovering(isOver);
if (isOver !== isHovering) {
console.log("[Sidebar] Hover state changed:", isOver);
setIsHovering(isOver);
}
}).then(fn => unlisteners.push(fn));
// Handle drag end - check if dropped on sidebar
platform.onDragEvent("ended", async (payload: { result?: { type: string } }) => {
const dragData = getDragData(); // Get BEFORE clearing
console.log("[Sidebar] drag:ended", {
wasDropped: payload.result?.type?.toLowerCase() === "dropped",
isHovering,
hasSpace: !!currentSpace,
hasDragData: !!dragData
});
// Check for "dropped" (lowercase from backend)
const wasDropped = payload.result?.type?.toLowerCase() === "dropped";
// If dropped and we have drag data from our app, add it to the space
if (wasDropped && currentSpace && dragData) {
// Only add if dropped AND hovering over the sidebar
if (wasDropped && isHovering && currentSpace && dragData) {
try {
await addItem.mutateAsync({
space_id: currentSpace.id,
@ -155,8 +168,7 @@ export function SpacesSidebar({ isPreviewActive = false }: SpacesSidebarProps) {
ref={dropZoneRef}
className={clsx(
"no-scrollbar mt-3 mask-fade-out flex grow flex-col space-y-5 overflow-x-hidden overflow-y-scroll pb-10 transition-colors rounded-lg",
isDragging && "bg-accent/10 ring-2 ring-accent/50 ring-inset",
isDragging && isHovering && "bg-accent/20 ring-accent"
isDragging && isHovering && "bg-accent/10 ring-2 ring-accent/50 ring-inset"
)}
>
{/* Space-level items (pinned shortcuts) */}

View File

@ -1,16 +1,26 @@
{
"name": "@sd/ui",
"version": "0.0.0",
"name": "@spacedriveapp/ui",
"version": "1.0.2",
"license": "GPL-3.0-only",
"main": "src/index.ts",
"types": "src/index.ts",
"description": "React component library for Spacedrive applications",
"main": "dist/index.mjs",
"module": "dist/index.mjs",
"sideEffects": [
"./style/index.js",
"./style/style.scss"
],
"repository": {
"type": "git",
"url": "https://github.com/spacedriveapp/spacedrive.git",
"directory": "packages/ui"
},
"publishConfig": {
"access": "public"
},
"exports": {
".": "./src/index.ts",
"./src/forms": "./src/forms/index.ts",
".": "./dist/index.mjs",
"./forms": "./dist/forms/index.mjs",
"./src/forms": "./dist/forms/index.mjs",
"./postcss": "./style/postcss.config.js",
"./tailwind": "./style/tailwind.js",
"./style": "./style/index.js",
@ -19,10 +29,12 @@
},
"files": [
"dist",
"style"
"style",
"src"
],
"scripts": {
"build": "tsup",
"prepublishOnly": "bun run build",
"lint": "eslint src --cache",
"typecheck": "tsc -b"
},
@ -44,7 +56,7 @@
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@react-spring/web": "9.7.3",
"@sd/assets": "workspace:*",
"@spacedriveapp/assets": "^1.0.1",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2",

View File

@ -9,7 +9,7 @@ export default defineConfig({
'react',
'react-dom',
'react-router-dom',
'@jamiepine/assets',
'@spacedriveapp/assets',
'zod',
'clsx',
'class-variance-authority',