mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-02 10:03:28 +00:00
raw images :D
This commit is contained in:
parent
600adc8fa9
commit
4aaef94a5b
|
@ -49,7 +49,7 @@ fn build_swift_natives() {
|
|||
|
||||
if !Command::new("swift")
|
||||
.args(&["build", "-c", &profile])
|
||||
.current_dir("./swift")
|
||||
.current_dir("./swift-lib")
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
|
@ -65,11 +65,11 @@ fn build_swift_natives() {
|
|||
println!("cargo:rustc-link-search=native={}", path);
|
||||
});
|
||||
println!(
|
||||
"cargo:rustc-link-search=native=./swift/.build/{}/{}",
|
||||
"cargo:rustc-link-search=native=./swift-lib/.build/{}/{}",
|
||||
swift_target_info.target.unversioned_triple, profile
|
||||
);
|
||||
println!("cargo:rustc-link-lib=static=spacedrive");
|
||||
println!("cargo:rerun-if-changed=swift/src/*.swift");
|
||||
println!("cargo:rustc-link-lib=static=swift-lib");
|
||||
println!("cargo:rerun-if-changed=swift-lib/src/*.swift");
|
||||
println!(
|
||||
"cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={}",
|
||||
MACOS_TARGET_VERSION
|
||||
|
@ -77,10 +77,6 @@ fn build_swift_natives() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
// let target = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
// if target == "macos" {
|
||||
build_swift_natives();
|
||||
// }
|
||||
|
||||
tauri_build::build();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::db::entity::file;
|
||||
use crate::filesystem::retrieve::Directory;
|
||||
use crate::swift::get_file_thumbnail_base64;
|
||||
use crate::{db, filesystem};
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
@ -32,6 +33,13 @@ pub async fn scan_dir(path: String) -> Result<(), String> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
#[tauri::command(async)]
|
||||
pub async fn get_file_thumb(path: &str) -> Result<String, String> {
|
||||
let path = &path.to_string();
|
||||
let thumbnail_b46 = get_file_thumbnail_base64(path.into()).to_string();
|
||||
|
||||
Ok(thumbnail_b46)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub async fn get_files(path: String) -> Result<Directory, String> {
|
||||
|
|
|
@ -8,76 +8,14 @@ mod crypto;
|
|||
mod db;
|
||||
mod device;
|
||||
mod filesystem;
|
||||
mod swift;
|
||||
mod util;
|
||||
use crate::app::menu;
|
||||
use env_logger;
|
||||
use futures::executor::block_on;
|
||||
// use systemstat::{saturating_sub_bytes, Platform, System};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct FFIString {
|
||||
// _phantom: u8,
|
||||
data: *const u8,
|
||||
length: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct FFIData {
|
||||
// _phantom: u8,
|
||||
data: *const u8,
|
||||
length: u64,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn get_file_thumbnail(ptr: *const u8, length: u64) -> FFIData;
|
||||
fn test(ptr: *const u8, length: u64) -> FFIData;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let path = "/Users/jamie/Downloads/Audio Hijack.app";
|
||||
// let string = FFIString {
|
||||
// data: path.as_ptr(),
|
||||
// length: path.len(),
|
||||
// };
|
||||
// let thumbnail = unsafe {
|
||||
// println!("{:?}", string);
|
||||
// get_file_thumbnail(&string)
|
||||
// };
|
||||
|
||||
println!("Struct Data: {:?}", unsafe {
|
||||
let res = test(path.as_ptr(), path.len() as u64);
|
||||
|
||||
let mut vec = Vec::<u8>::new();
|
||||
|
||||
let mut pointer_str = String::new();
|
||||
|
||||
for j in 8..16 {
|
||||
let num = *(res.data.add(j));
|
||||
vec.push(num);
|
||||
|
||||
pointer_str = format!("{:x}", num) + &pointer_str;
|
||||
}
|
||||
|
||||
let pointer_num = u64::from_str_radix(&pointer_str, 16).unwrap();
|
||||
let pointer = pointer_num as *const u8;
|
||||
|
||||
let mut length = 0;
|
||||
for j in 16..24 {
|
||||
let num = *(res.data.add(j));
|
||||
length += num as usize * 256_usize.pow(j as u32 - 16_u32)
|
||||
}
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
for i in 0..length {
|
||||
data.push(*pointer.add(i));
|
||||
}
|
||||
|
||||
res
|
||||
});
|
||||
// unsafe { get_icon("/Users/jamie/Downloads/Audio Hijack.app") }
|
||||
// let mounts = device::volumes_c::get_mounts();
|
||||
// println!("mounted drives: {:?}", &mounts);
|
||||
// env_logger::builder()
|
||||
|
@ -95,7 +33,8 @@ fn main() {
|
|||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::scan_dir,
|
||||
commands::get_files
|
||||
commands::get_files,
|
||||
commands::get_file_thumb
|
||||
])
|
||||
.menu(menu::get_menu())
|
||||
.run(tauri::generate_context!())
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
// swift-tools-version:5.5
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "drive_mac",
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "swift",
|
||||
targets: ["swift"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
// .package(url: /* package url */, from: "1.0.0"),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "swift",
|
||||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "swiftTests",
|
||||
dependencies: ["swift"]),
|
||||
]
|
||||
)
|
59
src-tauri/src/swift/mod.rs
Normal file
59
src-tauri/src/swift/mod.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct SRArray<T> {
|
||||
_nsobject_offset: u8,
|
||||
data: *mut T,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl<T> SRArray<T> {
|
||||
fn into_slice(&self) -> &'static [T] {
|
||||
unsafe { std::slice::from_raw_parts(self.data, self.length) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct SRData {
|
||||
_nsobject_offset: u8,
|
||||
data: *mut SRArray<u8>,
|
||||
}
|
||||
|
||||
impl SRData {
|
||||
pub fn into_slice(&self) -> &'static [u8] {
|
||||
unsafe { (*self.data).into_slice() }
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &SRArray<u8> {
|
||||
unsafe { &*(self.data) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct SRString(SRData);
|
||||
|
||||
impl SRString {
|
||||
pub fn to_string(&self) -> String {
|
||||
unsafe { std::str::from_utf8_unchecked(self.0.into_slice()) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for &SRString {
|
||||
fn from(string: &String) -> &'static SRString {
|
||||
unsafe { allocate_string(string.as_ptr(), string.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn return_data() -> &'static SRData;
|
||||
fn return_string() -> &'static SRString;
|
||||
fn echo_string(string: &SRString);
|
||||
fn allocate_string(data: *const u8, size: usize) -> &'static SRString;
|
||||
#[link_name = "get_file_thumbnail_base64"]
|
||||
fn get_file_thumbnail_base64_(path: &SRString) -> &'static SRString;
|
||||
}
|
||||
|
||||
pub fn get_file_thumbnail_base64(path: &SRString) -> &'static SRString {
|
||||
unsafe { get_file_thumbnail_base64_(path) }
|
||||
}
|
|
@ -4,16 +4,16 @@
|
|||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "spacedrive",
|
||||
name: "swift-lib",
|
||||
platforms: [
|
||||
.macOS(.v11),
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "spacedrive",
|
||||
name: "swift-lib",
|
||||
type: .static,
|
||||
targets: ["spacedrive"]),
|
||||
targets: ["swift-lib"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
|
@ -23,7 +23,7 @@ let package = Package(
|
|||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "spacedrive",
|
||||
name: "swift-lib",
|
||||
dependencies: [],
|
||||
path: "src")
|
||||
]
|
78
src-tauri/swift-lib/src/lib.swift
Normal file
78
src-tauri/swift-lib/src/lib.swift
Normal file
|
@ -0,0 +1,78 @@
|
|||
import Foundation
|
||||
import AppKit
|
||||
|
||||
// Size: 24 bytes
|
||||
public class SRArray<T>: NSObject {
|
||||
var pointer: UnsafePointer<T>
|
||||
var length: Int
|
||||
|
||||
init(_ data: [T]) {
|
||||
let mut_data = UnsafeMutablePointer<T>.allocate(capacity: data.count)
|
||||
mut_data.initialize(from: data, count: data.count)
|
||||
|
||||
self.pointer = UnsafePointer(mut_data)
|
||||
self.length = data.count
|
||||
}
|
||||
}
|
||||
|
||||
// Size: 16 bytes
|
||||
public class SRData: NSObject {
|
||||
var data: SRArray<UInt8>
|
||||
|
||||
init(_ data: [UInt8]) {
|
||||
self.data = SRArray(data)
|
||||
}
|
||||
|
||||
func to_data() -> Data {
|
||||
return Data(bytes: self.data.pointer, count: self.data.length)
|
||||
}
|
||||
}
|
||||
|
||||
// Size: 16 bytes
|
||||
public class SRString: SRData {
|
||||
init(_ string: String) {
|
||||
super.init(Array(string.utf8))
|
||||
}
|
||||
|
||||
func to_string() -> String {
|
||||
return String(bytes: self.to_data(), encoding: .utf8)!
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("return_data")
|
||||
public func returnData() -> SRData {
|
||||
return SRData([1,2,3])
|
||||
}
|
||||
|
||||
|
||||
@_cdecl("return_string")
|
||||
public func returnString() -> SRString {
|
||||
return SRString("123456")
|
||||
}
|
||||
|
||||
@_cdecl("echo_string")
|
||||
public func echoString(string: SRString) {
|
||||
print(string.to_string())
|
||||
}
|
||||
|
||||
// SRstring pointer is passed to rust correctly
|
||||
// data pointer is passed to rust correctly
|
||||
// guessing that the type of SRArray isn't the same
|
||||
@_cdecl("allocate_string")
|
||||
public func allocate_string(data: UnsafePointer<UInt8>, size: Int) -> SRString {
|
||||
let buffer = UnsafeBufferPointer(start: data, count: size)
|
||||
let string = String(bytes: buffer, encoding: .utf8)!;
|
||||
let SRstring = SRString(string);
|
||||
return SRstring
|
||||
}
|
||||
|
||||
@_cdecl("get_file_thumbnail_base64")
|
||||
public func getFileThumbnailBase64(path: SRString) -> SRString {
|
||||
let path = path.to_string();
|
||||
|
||||
let image = NSWorkspace.shared.icon(forFile: path)
|
||||
let bitmap = NSBitmapImageRep(data: image.tiffRepresentation!)!.representation(using: .png, properties: [:])!
|
||||
|
||||
return SRString(bitmap.base64EncodedString())
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# spacedive_swift
|
||||
|
||||
A description of this package.
|
|
@ -1,62 +0,0 @@
|
|||
import Foundation
|
||||
import AppKit
|
||||
|
||||
public class FFIString: NSObject {
|
||||
var data: UnsafePointer<CChar>
|
||||
var length: UInt64
|
||||
|
||||
init(data: UnsafePointer<CChar>, length: UInt64) {
|
||||
self.data = data
|
||||
self.length = length
|
||||
}
|
||||
}
|
||||
|
||||
public class FFIData: NSObject {
|
||||
var data: UnsafePointer<UInt8>
|
||||
var length: UInt64
|
||||
|
||||
init(data: UnsafePointer<UInt8>, length: UInt64) {
|
||||
self.data = data
|
||||
self.length = length
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("get_file_thumbnail")
|
||||
public func getFileThumbnail(path_ptr: UnsafePointer<CChar>, path_length: UInt64) -> FFIData {
|
||||
print(path_length)
|
||||
let path = String(data: Data(bytes: path_ptr, count: Int(path_length)), encoding: String.Encoding.utf8)!
|
||||
print(path)
|
||||
let image = NSWorkspace.shared.icon(forFile: path)
|
||||
let bitmap = NSBitmapImageRep(data: image.tiffRepresentation!)!.representation(using: .png, properties: [:])!
|
||||
|
||||
let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmap.count)
|
||||
bitmap.copyBytes(to: pointer, count: bitmap.count)
|
||||
|
||||
return FFIData(data: UnsafePointer(pointer), length: UInt64(bitmap.count))
|
||||
}
|
||||
|
||||
@_cdecl("test")
|
||||
public func test(path_ptr: UnsafePointer<CChar>, path_length: UInt64) -> FFIString {
|
||||
let path = String(data: Data(bytes: path_ptr, count: Int(path_length)), encoding: String.Encoding.utf8)!
|
||||
print(path)
|
||||
let image = NSWorkspace.shared.icon(forFile: path)
|
||||
let bitmap = NSBitmapImageRep(data: image.tiffRepresentation!)!.representation(using: .png, properties: [:])!
|
||||
|
||||
let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmap.count)
|
||||
bitmap.copyBytes(to: pointer, count: bitmap.count)
|
||||
|
||||
let data = UnsafePointer<UInt8>(pointer);
|
||||
let length = UInt64(bitmap.count)
|
||||
|
||||
print(data)
|
||||
print(length)
|
||||
|
||||
let ret = FFIString(
|
||||
data: UnsafePointer<CChar>(strdup(path)!),
|
||||
length: UInt64(path.lengthOfBytes(using: .utf8))
|
||||
)
|
||||
|
||||
print(ret)
|
||||
|
||||
return ret
|
||||
}
|
|
@ -5,6 +5,7 @@ import { TopBar } from './components/layout/TopBar';
|
|||
import { useInputState } from './hooks/useInputState';
|
||||
import { SettingsScreen } from './screens/Settings';
|
||||
import { ExplorerScreen } from './screens/Explorer';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { DocumentIcon, DotsVerticalIcon, FilmIcon, FolderIcon } from '@heroicons/react/solid';
|
||||
import clsx from 'clsx';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { IFile } from '../../types';
|
||||
import byteSize from 'pretty-bytes';
|
||||
import { useKey } from 'rooks';
|
||||
|
@ -100,6 +100,16 @@ export const FileList: React.FC<{}> = (props) => {
|
|||
};
|
||||
|
||||
const RenderRow: React.FC<{ row: IFile; rowIndex: number }> = ({ row, rowIndex }) => {
|
||||
const [image, setImage] = useState<string | undefined>();
|
||||
useEffect(() => {
|
||||
if (row.uri)
|
||||
invoke('get_file_thumb', { path: row.uri }).then((imageData) => {
|
||||
console.log({ imageData: (imageData as string).slice(1, 50) });
|
||||
|
||||
setImage(imageData as string);
|
||||
});
|
||||
}, [row.uri]);
|
||||
|
||||
const [collectDir, selectedRow, setSelectedRow] = useExplorerStore((state) => [
|
||||
state.collectDir,
|
||||
state.selected,
|
||||
|
@ -136,16 +146,20 @@ const RenderRow: React.FC<{ row: IFile; rowIndex: number }> = ({ row, rowIndex }
|
|||
className="table-body-cell px-4 py-2 flex items-center pr-2"
|
||||
style={{ width: col.width }}
|
||||
>
|
||||
<RenderCell row={row} colKey={col?.key} />
|
||||
<RenderCell image={image} row={row} colKey={col?.key} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
[isActive]
|
||||
[isActive, image]
|
||||
);
|
||||
};
|
||||
|
||||
const RenderCell: React.FC<{ colKey?: ColumnKey; row?: IFile }> = ({ colKey, row }) => {
|
||||
const RenderCell: React.FC<{ colKey?: ColumnKey; row?: IFile; image?: string }> = ({
|
||||
colKey,
|
||||
row,
|
||||
image
|
||||
}) => {
|
||||
if (!row || !colKey || !row[colKey]) return <></>;
|
||||
const value = row[colKey];
|
||||
|
||||
|
@ -153,7 +167,8 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; row?: IFile }> = ({ colKey, row
|
|||
case 'name':
|
||||
return (
|
||||
<div className="flex flex-row items-center overflow-hidden">
|
||||
{colKey == 'name' &&
|
||||
{!!image && <img src={'data:image/png;base64, ' + image} className="w-6 h-6 mr-2" />}
|
||||
{/* {colKey == 'name' &&
|
||||
(() => {
|
||||
switch (row.extension.toLowerCase()) {
|
||||
case 'mov' || 'mp4':
|
||||
|
@ -164,7 +179,7 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; row?: IFile }> = ({ colKey, row
|
|||
return <FolderIcon className="w-5 h-5 mr-3 flex-shrink-0 text-gray-300" />;
|
||||
return <DocumentIcon className="w-5 h-5 mr-3 flex-shrink-0 text-gray-300" />;
|
||||
}
|
||||
})()}
|
||||
})()} */}
|
||||
<span className="truncate text-xs">{row[colKey]}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -33,9 +33,9 @@ export const Inspector = () => {
|
|||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-64"
|
||||
>
|
||||
<div className="h-full w-52 absolute right-0 top-0 m-1">
|
||||
<div className="flex flex-col overflow-hidden h-full rounded-md bg-gray-800 shadow-xl select-text">
|
||||
<div className="h-32 bg-gray-950 w-full" />
|
||||
<div className="h-full w-60 absolute right-0 top-0 m-2">
|
||||
<div className="flex flex-col overflow-hidden h-full rounded-lg bg-gray-700 shadow-lg select-text">
|
||||
<div className="h-32 bg-gray-750 w-full" />
|
||||
<h3 className="font-bold p-3 text-base">{selectedFile?.name}</h3>
|
||||
<MetaItem title="Checksum" value={selectedFile?.meta_checksum as string} />
|
||||
<Divider />
|
||||
|
|
|
@ -24,11 +24,12 @@ interface SidebarProps extends DefaultProps {}
|
|||
|
||||
export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
return (
|
||||
<div className="w-46 flex flex-col flex-wrap flex-shrink-0 min-h-full bg-gray-50 dark:bg-gray-850 border-gray-100 border-r dark:border-gray-700 px-3 space-y-0.5">
|
||||
<div className="w-46 flex flex-col flex-wrap flex-shrink-0 min-h-full bg-gray-50 dark:bg-gray-650 border-gray-100 border-r dark:border-gray-700 px-3 space-y-0.5">
|
||||
<Dropdown
|
||||
buttonProps={{
|
||||
justifyLeft: true,
|
||||
className: 'mb-1 shadow flex-shrink-0 w-[175px] dark:bg-gray-650',
|
||||
className:
|
||||
'mb-1 shadow flex-shrink-0 w-[175px] dark:bg-gray-550 dark:hover:!bg-gray-500 dark:hover:!border-gray-450',
|
||||
variant: 'gray'
|
||||
}}
|
||||
buttonText="Jamie's Library"
|
||||
|
|
|
@ -51,7 +51,7 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
<>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="flex flex-shrink-0 h-10 max-w items-center border-b bg-gray-100 dark:bg-gray-650 border-gray-100 dark:border-gray-900 shadow-sm "
|
||||
className="flex flex-shrink-0 h-10 max-w items-center border-b bg-gray-100 dark:bg-gray-650 border-gray-100 dark:border-gray-900 shadow-sm"
|
||||
>
|
||||
<div className="mr-32 ml-1 ">
|
||||
<TrafficLights className="p-1.5" />
|
||||
|
@ -67,7 +67,7 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
|||
<div className="relative flex h-7">
|
||||
<Input
|
||||
placeholder="Search"
|
||||
className="placeholder-gray-600 bg-gray-50 zdark:bg-gray-600 dark:hover:bg-gray-600 text-xs w-32 focus:w-52 transition-all"
|
||||
className="placeholder-gray-600 bg-gray-50 dark:bg-gray-600 dark:hover:!bg-gray-600 dark:focus:hover:!bg-gray-800 text-xs w-32 focus:w-52 transition-all"
|
||||
/>
|
||||
<div className="space-x-1 absolute top-[1px] right-1">
|
||||
<Shortcut chars="⌘" />
|
||||
|
|
Loading…
Reference in a new issue