mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 11:03:27 +00:00
Rust+Swift bridge
Co-authored-by: Brendonovich <brendonovich@outlook.com>
This commit is contained in:
parent
eb64c90f83
commit
600adc8fa9
19
.TODO
19
.TODO
|
@ -7,6 +7,21 @@
|
||||||
✔ App frame and router set up
|
✔ App frame and router set up
|
||||||
✔ Primative UI components (Button, Input, Shortcut etc)
|
✔ Primative UI components (Button, Input, Shortcut etc)
|
||||||
✔ Render basic file list with database data
|
✔ Render basic file list with database data
|
||||||
☐ Create a global store
|
✔ Create a global store
|
||||||
|
☐ File inspector <- CURRENT
|
||||||
|
☐ Tag creation and assignment
|
||||||
|
☐ Generate buffer hash
|
||||||
|
☐ Right click menu
|
||||||
|
☐ Settings screen
|
||||||
☐ Set up Tauri updater
|
☐ Set up Tauri updater
|
||||||
☐
|
☐ Volume identification
|
||||||
|
☐ Job queue system
|
||||||
|
☐ Native file previews
|
||||||
|
☐ Secret keystore
|
||||||
|
☐ File encryptor
|
||||||
|
☐ File viewer / player
|
||||||
|
☐ Open with
|
||||||
|
☐ Explorer grid view
|
||||||
|
☐ Explorer path viewer / editor
|
||||||
|
☐ Statistic calucations
|
||||||
|
Folder size, Volume size, Total capacity, Total unique etc..
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-dropzone": "^11.3.4",
|
"react-dropzone": "^11.3.4",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
"react-virtualized": "^9.22.3",
|
||||||
"rooks": "^5.7.1",
|
"rooks": "^5.7.1",
|
||||||
"tailwindcss": "^2.2.16",
|
"tailwindcss": "^2.2.16",
|
||||||
"vite": "^2.4.4",
|
"vite": "^2.4.4",
|
||||||
|
|
3
src-tauri/Cargo.lock
generated
3
src-tauri/Cargo.lock
generated
|
@ -10,6 +10,7 @@ dependencies = [
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"cargo-edit",
|
"cargo-edit",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"cocoa",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
@ -18,6 +19,8 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"nix 0.23.0",
|
"nix 0.23.0",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rebind",
|
"rebind",
|
||||||
"refinery",
|
"refinery",
|
||||||
|
|
|
@ -11,6 +11,8 @@ build = "src/build.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.0.0-beta.3" }
|
tauri-build = { version = "1.0.0-beta.3" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "1.0.0-beta.5", features = ["api-all", "menu"] }
|
tauri = { version = "1.0.0-beta.5", features = ["api-all", "menu"] }
|
||||||
|
@ -40,6 +42,9 @@ bytesize = "1.1.0"
|
||||||
libc = "0.2.103"
|
libc = "0.2.103"
|
||||||
nix = "0.23.0"
|
nix = "0.23.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
|
objc = "0.2.7"
|
||||||
|
cocoa = "0.24.0"
|
||||||
|
objc-foundation = "0.1.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "custom-protocol" ]
|
default = [ "custom-protocol" ]
|
||||||
|
|
|
@ -1,3 +1,86 @@
|
||||||
fn main() {
|
use serde::Deserialize;
|
||||||
tauri_build::build()
|
use serde_json;
|
||||||
|
use std::env;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
const MACOS_TARGET_VERSION: &str = "11";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct SwiftTargetInfo {
|
||||||
|
triple: String,
|
||||||
|
unversioned_triple: String,
|
||||||
|
module_triple: String,
|
||||||
|
swift_runtime_compatibility_version: String,
|
||||||
|
#[serde(rename = "librariesRequireRPath")]
|
||||||
|
libraries_require_rpath: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct SwiftPaths {
|
||||||
|
runtime_library_paths: Vec<String>,
|
||||||
|
runtime_library_import_paths: Vec<String>,
|
||||||
|
runtime_resource_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct SwiftTarget {
|
||||||
|
target: SwiftTargetInfo,
|
||||||
|
paths: SwiftPaths,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds mac_ddc library Swift project, sets the library search options right so we link
|
||||||
|
/// against Swift run-time correctly.
|
||||||
|
fn build_swift_natives() {
|
||||||
|
let profile = env::var("PROFILE").unwrap();
|
||||||
|
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||||
|
let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION);
|
||||||
|
|
||||||
|
let swift_target_info_str = Command::new("swift")
|
||||||
|
.args(&["-target", &target, "-print-target-info"])
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout;
|
||||||
|
let swift_target_info: SwiftTarget = serde_json::from_slice(&swift_target_info_str).unwrap();
|
||||||
|
if swift_target_info.target.libraries_require_rpath {
|
||||||
|
panic!("Libraries require RPath! Change minimum MacOS value to fix.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Command::new("swift")
|
||||||
|
.args(&["build", "-c", &profile])
|
||||||
|
.current_dir("./swift")
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success()
|
||||||
|
{
|
||||||
|
panic!("Swift natives compilation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
swift_target_info
|
||||||
|
.paths
|
||||||
|
.runtime_library_paths
|
||||||
|
.iter()
|
||||||
|
.for_each(|path| {
|
||||||
|
println!("cargo:rustc-link-search=native={}", path);
|
||||||
|
});
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-link-search=native=./swift/.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-env=MACOSX_DEPLOYMENT_TARGET={}",
|
||||||
|
MACOS_TARGET_VERSION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// let target = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
|
// if target == "macos" {
|
||||||
|
build_swift_natives();
|
||||||
|
// }
|
||||||
|
|
||||||
|
tauri_build::build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub async fn init_library() -> Result<()> {
|
||||||
|
|
||||||
println!("created library {:?}", &library);
|
println!("created library {:?}", &library);
|
||||||
} else {
|
} else {
|
||||||
println!("library loaded {:?}", library.unwrap());
|
// println!("library loaded {:?}", library.unwrap());
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,76 @@ use env_logger;
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
// use systemstat::{saturating_sub_bytes, Platform, System};
|
// 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() {
|
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();
|
// let mounts = device::volumes_c::get_mounts();
|
||||||
// println!("mounted drives: {:?}", &mounts);
|
// println!("mounted drives: {:?}", &mounts);
|
||||||
env_logger::builder()
|
// env_logger::builder()
|
||||||
.filter_level(log::LevelFilter::Debug)
|
// .filter_level(log::LevelFilter::Debug)
|
||||||
.is_test(true)
|
// .is_test(true)
|
||||||
.init();
|
// .init();
|
||||||
|
|
||||||
// create primary data base if not exists
|
// create primary data base if not exists
|
||||||
block_on(db::connection::create_primary_db()).unwrap();
|
block_on(db::connection::create_primary_db()).unwrap();
|
||||||
|
|
28
src-tauri/src/swift/Package.swift
Normal file
28
src-tauri/src/swift/Package.swift
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// 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"]),
|
||||||
|
]
|
||||||
|
)
|
7
src-tauri/swift/.gitignore
vendored
Normal file
7
src-tauri/swift/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
30
src-tauri/swift/Package.swift
Normal file
30
src-tauri/swift/Package.swift
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// 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: "spacedrive",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v11),
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "spacedrive",
|
||||||
|
type: .static,
|
||||||
|
targets: ["spacedrive"]),
|
||||||
|
],
|
||||||
|
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: "spacedrive",
|
||||||
|
dependencies: [],
|
||||||
|
path: "src")
|
||||||
|
]
|
||||||
|
)
|
3
src-tauri/swift/README.md
Normal file
3
src-tauri/swift/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# spacedive_swift
|
||||||
|
|
||||||
|
A description of this package.
|
62
src-tauri/swift/src/spacedrive.swift
Normal file
62
src-tauri/swift/src/spacedrive.swift
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -53,8 +53,8 @@
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "SpaceDrive",
|
"title": "SpaceDrive",
|
||||||
"width": 1200,
|
"width": 1100,
|
||||||
"height": 700,
|
"height": 600,
|
||||||
"resizable": true,
|
"resizable": true,
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
"alwaysOnTop": false,
|
"alwaysOnTop": false,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
|
import { Route, BrowserRouter as Router, Switch, Redirect } from 'react-router-dom';
|
||||||
import { Sidebar } from './components/file/Sidebar';
|
import { Sidebar } from './components/file/Sidebar';
|
||||||
import { TopBar } from './components/layout/TopBar';
|
import { TopBar } from './components/layout/TopBar';
|
||||||
import { useInputState } from './hooks/useInputState';
|
import { useInputState } from './hooks/useInputState';
|
||||||
|
@ -9,12 +9,15 @@ import { ExplorerScreen } from './screens/Explorer';
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<div className="flex flex-col h-screen rounded-xl border border-gray-200 dark:border-gray-600 bg-white text-gray-900 dark:text-white dark:bg-gray-800 overflow-hidden ">
|
<div className="flex flex-col select-none h-screen rounded-xl border border-gray-200 dark:border-gray-600 bg-white text-gray-900 dark:text-white dark:bg-gray-800 overflow-hidden ">
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<div className="flex flex-row min-h-full">
|
<div className="flex flex-row min-h-full">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div className="w-full flex bg-gray-50 dark:bg-gray-800">
|
<div className="relative w-full flex bg-gray-50 dark:bg-gray-800">
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route exact path="/">
|
||||||
|
<Redirect to="/explorer" />
|
||||||
|
</Route>
|
||||||
<Route path="/settings">
|
<Route path="/settings">
|
||||||
<SettingsScreen />
|
<SettingsScreen />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { DocumentIcon, DotsVerticalIcon, FilmIcon, FolderIcon } from '@heroicons/react/solid';
|
import { DocumentIcon, DotsVerticalIcon, FilmIcon, FolderIcon } from '@heroicons/react/solid';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import { IFile } from '../../types';
|
import { IFile } from '../../types';
|
||||||
import byteSize from 'pretty-bytes';
|
import byteSize from 'pretty-bytes';
|
||||||
import { useKey } from 'rooks';
|
import { useKey } from 'rooks';
|
||||||
|
@ -22,13 +22,16 @@ function ensureIsColumns<T extends Column[]>(data: T) {
|
||||||
const columns = ensureIsColumns([
|
const columns = ensureIsColumns([
|
||||||
{ column: 'Name', key: 'name', width: 280 } as const,
|
{ column: 'Name', key: 'name', width: 280 } as const,
|
||||||
{ column: 'Size', key: 'size_in_bytes', width: 120 } as const,
|
{ column: 'Size', key: 'size_in_bytes', width: 120 } as const,
|
||||||
{ column: 'Checksum', key: 'meta_checksum', width: 120 } as const
|
{ column: 'Type', key: 'extension', width: 100 } as const
|
||||||
|
// { column: 'Checksum', key: 'meta_checksum', width: 120 } as const
|
||||||
// { column: 'Tags', key: 'tags', width: 120 } as const
|
// { column: 'Tags', key: 'tags', width: 120 } as const
|
||||||
]);
|
]);
|
||||||
|
|
||||||
type ColumnKey = typeof columns[number]['key'];
|
type ColumnKey = typeof columns[number]['key'];
|
||||||
|
|
||||||
export const FileList: React.FC<{}> = (props) => {
|
export const FileList: React.FC<{}> = (props) => {
|
||||||
|
const scrollContainer = useRef<null | HTMLDivElement>(null);
|
||||||
|
const [rowHeight, setRowHeight] = useState(0);
|
||||||
// const [selectedRow, setSelectedRow] = useState(0);
|
// const [selectedRow, setSelectedRow] = useState(0);
|
||||||
const [currentDir, activeDirHash, collectDir, selectedRow, setSelectedRow] = useExplorerStore(
|
const [currentDir, activeDirHash, collectDir, selectedRow, setSelectedRow] = useExplorerStore(
|
||||||
(state) => [
|
(state) => [
|
||||||
|
@ -42,18 +45,35 @@ export const FileList: React.FC<{}> = (props) => {
|
||||||
|
|
||||||
useKey('ArrowUp', (e) => {
|
useKey('ArrowUp', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (selectedRow > 1) setSelectedRow(selectedRow - 1);
|
if (!selectedRow || !currentDir?.children) return;
|
||||||
else setSelectedRow(currentDir.children_count);
|
if (selectedRow?.index > 0)
|
||||||
|
// decrement selected index
|
||||||
|
setSelectedRow(selectedRow.index - 1, currentDir.children[selectedRow.index - 1]);
|
||||||
|
// loop to bottom
|
||||||
|
else setSelectedRow(currentDir.children_count, currentDir.children[currentDir.children_count]);
|
||||||
});
|
});
|
||||||
useKey('ArrowDown', (e) => {
|
useKey('ArrowDown', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (selectedRow < currentDir.children_count) setSelectedRow(selectedRow + 1);
|
if (!selectedRow || !currentDir?.children) return;
|
||||||
else setSelectedRow(0);
|
// increment if rows below exist
|
||||||
|
if (selectedRow.index < currentDir.children_count)
|
||||||
|
setSelectedRow(selectedRow.index + 1, currentDir.children[selectedRow.index + 1]);
|
||||||
|
else setSelectedRow(0, currentDir.children[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function isRowOutOfView(rowHeight: number, rowIndex: number) {
|
||||||
|
const scrollTop = scrollContainer.current?.scrollTop || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll() {}
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (
|
() => (
|
||||||
<div className="table-container w-full h-full overflow-scroll bg-white dark:bg-gray-900 p-3 ">
|
<div
|
||||||
|
ref={scrollContainer}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
className="table-container w-full h-full overflow-scroll bg-white dark:bg-gray-900 p-3 cursor-default"
|
||||||
|
>
|
||||||
<div className="table-head">
|
<div className="table-head">
|
||||||
<div className="table-head-row flex flex-row p-2">
|
<div className="table-head-row flex flex-row p-2">
|
||||||
{columns.map((col) => (
|
{columns.map((col) => (
|
||||||
|
@ -68,7 +88,7 @@ export const FileList: React.FC<{}> = (props) => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="table-body">
|
<div className="table-body pb-10">
|
||||||
{currentDir?.children?.map((row, index) => (
|
{currentDir?.children?.map((row, index) => (
|
||||||
<RenderRow key={row.id} row={row} rowIndex={index} />
|
<RenderRow key={row.id} row={row} rowIndex={index} />
|
||||||
))}
|
))}
|
||||||
|
@ -86,13 +106,18 @@ const RenderRow: React.FC<{ row: IFile; rowIndex: number }> = ({ row, rowIndex }
|
||||||
state.setSelected
|
state.setSelected
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isActive = selectedRow === row.id;
|
const isActive = selectedRow?.index === rowIndex;
|
||||||
const isAlternate = rowIndex % 2 == 0;
|
const isAlternate = rowIndex % 2 == 0;
|
||||||
|
|
||||||
|
function selectFile() {
|
||||||
|
if (selectedRow?.index == rowIndex) setSelectedRow(null);
|
||||||
|
else setSelectedRow(rowIndex, row);
|
||||||
|
}
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (
|
() => (
|
||||||
<div
|
<div
|
||||||
onClick={() => setSelectedRow(row.id as number)}
|
onClick={selectFile}
|
||||||
onDoubleClick={() => {
|
onDoubleClick={() => {
|
||||||
if (row.is_dir) {
|
if (row.is_dir) {
|
||||||
invoke<DirectoryResponse>('get_files', { path: row.uri }).then((res) => {
|
invoke<DirectoryResponse>('get_files', { path: row.uri }).then((res) => {
|
||||||
|
@ -106,7 +131,11 @@ const RenderRow: React.FC<{ row: IFile; rowIndex: number }> = ({ row, rowIndex }
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{columns.map((col) => (
|
{columns.map((col) => (
|
||||||
<div key={col.key} className="table-body-cell px-4 py-2" style={{ width: col.width }}>
|
<div
|
||||||
|
key={col.key}
|
||||||
|
className="table-body-cell px-4 py-2 flex items-center pr-2"
|
||||||
|
style={{ width: col.width }}
|
||||||
|
>
|
||||||
<RenderCell row={row} colKey={col?.key} />
|
<RenderCell row={row} colKey={col?.key} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -123,25 +152,28 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; row?: IFile }> = ({ colKey, row
|
||||||
switch (colKey) {
|
switch (colKey) {
|
||||||
case 'name':
|
case 'name':
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center overflow-hidden">
|
||||||
{colKey == 'name' &&
|
{colKey == 'name' &&
|
||||||
(() => {
|
(() => {
|
||||||
switch (row.extension.toLowerCase()) {
|
switch (row.extension.toLowerCase()) {
|
||||||
case 'mov' || 'mp4':
|
case 'mov' || 'mp4':
|
||||||
return <FilmIcon className="w-5 h-5 mr-3 flex-shrink-0" />;
|
return <FilmIcon className="w-5 h-5 mr-3 flex-shrink-0 text-gray-300" />;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (row.is_dir) return <FolderIcon className="w-5 h-5 mr-3 flex-shrink-0" />;
|
if (row.is_dir)
|
||||||
return <DocumentIcon className="w-5 h-5 mr-3 flex-shrink-0" />;
|
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">{row[colKey]}</span>
|
<span className="truncate text-xs">{row[colKey]}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case 'size_in_bytes':
|
case 'size_in_bytes':
|
||||||
return <span>{byteSize(Number(value || 0))}</span>;
|
return <span className="text-xs text-left">{byteSize(Number(value || 0))}</span>;
|
||||||
case 'meta_checksum':
|
case 'extension':
|
||||||
return <span className="truncate">{value}</span>;
|
return <span className="text-xs text-left">{value.toLowerCase()}</span>;
|
||||||
|
// case 'meta_checksum':
|
||||||
|
// return <span className="truncate">{value}</span>;
|
||||||
// case 'tags':
|
// case 'tags':
|
||||||
// return renderCellWithIcon(MusicNoteIcon);
|
// return renderCellWithIcon(MusicNoteIcon);
|
||||||
|
|
||||||
|
|
47
src/components/file/Inspector.tsx
Normal file
47
src/components/file/Inspector.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useExplorerStore } from '../../store/explorer';
|
||||||
|
import { Transition } from '@headlessui/react';
|
||||||
|
|
||||||
|
interface MetaItemProps {
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MetaItem = (props: MetaItemProps) => {
|
||||||
|
return (
|
||||||
|
<div className="meta-item flex flex-col p-3">
|
||||||
|
<h5 className="font-bold text-sm">{props.title}</h5>
|
||||||
|
<p className="break-all text-xs text-gray-300">{props.value}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Divider = () => <div className="w-full my-1 h-[1px] bg-gray-700" />;
|
||||||
|
|
||||||
|
export const Inspector = () => {
|
||||||
|
const [selectedFile] = useExplorerStore((state) => [state.selected?.file]);
|
||||||
|
|
||||||
|
const isOpen = !!selectedFile;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
show={isOpen}
|
||||||
|
enter="transition-translate ease-in-out duration-200"
|
||||||
|
enterFrom="translate-x-64"
|
||||||
|
enterTo="translate-x-0"
|
||||||
|
leave="transition-translate ease-in-out duration-200"
|
||||||
|
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" />
|
||||||
|
<h3 className="font-bold p-3 text-base">{selectedFile?.name}</h3>
|
||||||
|
<MetaItem title="Checksum" value={selectedFile?.meta_checksum as string} />
|
||||||
|
<Divider />
|
||||||
|
<MetaItem title="Uri" value={selectedFile?.uri as string} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
};
|
|
@ -13,22 +13,22 @@ import { Dropdown } from '../primative/Dropdown';
|
||||||
import { DefaultProps } from '../primative/types';
|
import { DefaultProps } from '../primative/types';
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ name: 'Spaces', icon: Planet, uri: '/spaces' },
|
// { name: 'Spaces', icon: Planet, uri: '/spaces' },
|
||||||
{ name: 'Photos', icon: PhotographIcon, uri: '/photos' },
|
|
||||||
{ name: 'Storage', icon: ServerIcon, uri: '/storage' },
|
|
||||||
{ name: 'Explorer', icon: CubeTransparentIcon, uri: '/explorer' },
|
{ name: 'Explorer', icon: CubeTransparentIcon, uri: '/explorer' },
|
||||||
{ name: 'Settings', icon: CogIcon, uri: '/settings' }
|
{ name: 'Photos', icon: PhotographIcon, uri: '/photos' },
|
||||||
|
{ name: 'Storage', icon: ServerIcon, uri: '/settings' }
|
||||||
|
// { name: 'Settings', icon: CogIcon, uri: '/settings' }
|
||||||
];
|
];
|
||||||
|
|
||||||
interface SidebarProps extends DefaultProps {}
|
interface SidebarProps extends DefaultProps {}
|
||||||
|
|
||||||
export const Sidebar: React.FC<SidebarProps> = (props) => {
|
export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-46 flex flex-col flex-wrap flex-shrink-0 min-h-full bg-gray-50 dark:bg-gray-800 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-850 border-gray-100 border-r dark:border-gray-700 px-3 space-y-0.5">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
justifyLeft: true,
|
justifyLeft: true,
|
||||||
className: 'mb-1 flex-shrink-0 w-[175px]',
|
className: 'mb-1 shadow flex-shrink-0 w-[175px] dark:bg-gray-650',
|
||||||
variant: 'gray'
|
variant: 'gray'
|
||||||
}}
|
}}
|
||||||
buttonText="Jamie's Library"
|
buttonText="Jamie's Library"
|
||||||
|
@ -38,7 +38,7 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||||
{tabs.map((button, index) => (
|
{tabs.map((button, index) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
key={index}
|
key={index}
|
||||||
className="max-w rounded px-2 py-1 flex flex-row items-center hover:bg-gray-200 dark:hover:bg-gray-700 text-sm"
|
className="max-w rounded px-2 py-1 flex flex-row items-center hover:bg-gray-200 dark:hover:bg-gray-600 text-sm"
|
||||||
activeClassName="bg-gray-200 hover:bg-gray-200 dark:bg-gray-500 dark:hover:bg-gray-500"
|
activeClassName="bg-gray-200 hover:bg-gray-200 dark:bg-gray-500 dark:hover:bg-gray-500"
|
||||||
to={button.uri}
|
to={button.uri}
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,46 +7,67 @@ import {
|
||||||
ViewGridIcon,
|
ViewGridIcon,
|
||||||
ViewListIcon
|
ViewListIcon
|
||||||
} from '@heroicons/react/outline';
|
} from '@heroicons/react/outline';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { HouseSimple } from 'phosphor-react';
|
import { HouseSimple } from 'phosphor-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useExplorerStore } from '../../store/explorer';
|
||||||
import { TrafficLights } from '../os/TrafficLights';
|
import { TrafficLights } from '../os/TrafficLights';
|
||||||
import { Button, Input } from '../primative';
|
import { Button, ButtonProps, Input } from '../primative';
|
||||||
import { Shortcut } from '../primative/Shortcut';
|
import { Shortcut } from '../primative/Shortcut';
|
||||||
import { DefaultProps } from '../primative/types';
|
import { DefaultProps } from '../primative/types';
|
||||||
|
|
||||||
export interface TopBarProps extends DefaultProps {}
|
export interface TopBarProps extends DefaultProps {}
|
||||||
|
export interface TopBarButtonProps extends ButtonProps {
|
||||||
|
icon: any;
|
||||||
|
group?: boolean;
|
||||||
|
active?: boolean;
|
||||||
|
left?: boolean;
|
||||||
|
right?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TopBarButton: React.FC<TopBarButtonProps> = ({ icon: Icon, ...props }) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...props}
|
||||||
|
className={clsx(
|
||||||
|
'mr-[1px] py-1 px-1 text-md font-medium dark:bg-gray-650 dark:hover:bg-gray-600 dark:active:bg-gray-500 rounded-md transition-colors duration-100',
|
||||||
|
{
|
||||||
|
'rounded-r-none rounded-l-none': props.group && !props.left && !props.right,
|
||||||
|
'rounded-r-none': props.group && props.left,
|
||||||
|
'rounded-l-none': props.group && props.right,
|
||||||
|
'dark:bg-gray-550 dark:hover:bg-gray-550 dark:active:bg-gray-550': props.active
|
||||||
|
},
|
||||||
|
props.className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon className="m-0.5 w-4 h-4 dark:text-white" />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const TopBar: React.FC<TopBarProps> = (props) => {
|
export const TopBar: React.FC<TopBarProps> = (props) => {
|
||||||
|
const [goBack] = useExplorerStore((state) => [state.goBack]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="flex flex-shrink-0 h-10 max-w items-center bg-gray-100 dark:bg-gray-800 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 ">
|
<div className="mr-32 ml-1 ">
|
||||||
<TrafficLights className="p-1.5" />
|
<TrafficLights className="p-1.5" />
|
||||||
</div>
|
</div>
|
||||||
<Button noBorder noPadding className="rounded-r-none mr-[1px]">
|
|
||||||
<ChevronLeftIcon className="m-0.5 w-4 h-4 dark:text-white" />
|
<TopBarButton group left icon={ChevronLeftIcon} onClick={goBack} />
|
||||||
</Button>
|
<TopBarButton group right icon={ChevronRightIcon} />
|
||||||
<Button noBorder noPadding className="rounded-l-none">
|
|
||||||
<ChevronRightIcon className="m-0.5 w-4 h-4 dark:text-white" />
|
|
||||||
</Button>
|
|
||||||
<div className="w-4"></div>
|
<div className="w-4"></div>
|
||||||
<Button variant="selected" noBorder noPadding className="rounded-r-none mr-[1px]">
|
<TopBarButton active group left icon={ViewListIcon} />
|
||||||
<ViewListIcon className="m-0.5 w-4 h-4 dark:text-white" />
|
<TopBarButton group icon={ViewBoardsIcon} />
|
||||||
</Button>
|
<TopBarButton group right icon={ViewGridIcon} />
|
||||||
<Button noBorder noPadding className="rounded-none mr-[1px]">
|
|
||||||
<ViewBoardsIcon className="m-0.5 w-4 h-4 dark:text-white" />
|
|
||||||
</Button>
|
|
||||||
<Button noBorder noPadding className="rounded-l-none">
|
|
||||||
<ViewGridIcon className="m-0.5 w-4 h-4 dark:text-white" />
|
|
||||||
</Button>
|
|
||||||
<div className="w-4"></div>
|
<div className="w-4"></div>
|
||||||
<div className="relative flex h-7">
|
<div className="relative flex h-7">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
className="placeholder-gray-600 bg-gray-50 text-xs w-32 focus:w-52 transition-all"
|
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"
|
||||||
/>
|
/>
|
||||||
<div className="space-x-1 absolute top-[1px] right-1">
|
<div className="space-x-1 absolute top-[1px] right-1">
|
||||||
<Shortcut chars="⌘" />
|
<Shortcut chars="⌘" />
|
||||||
|
@ -54,11 +75,9 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow"></div>
|
<div className="flex-grow"></div>
|
||||||
<Button noBorder noPadding className="mr-2">
|
<TopBarButton className="mr-[8px]" icon={CogIcon} />
|
||||||
<CogIcon className="m-0.5 w-4 h-4 dark:text-white" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="h-[1px] flex-shrink-0 max-w bg-gray-200 dark:bg-gray-700" />
|
{/* <div className="h-[1px] flex-shrink-0 max-w bg-gray-200 dark:bg-gray-700" /> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { emit, listen } from '@tauri-apps/api/event';
|
||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
import { IFile } from '../types';
|
import { IFile } from '../types';
|
||||||
import { useExplorerStore } from '../store/explorer';
|
import { useExplorerStore } from '../store/explorer';
|
||||||
|
import { Inspector } from '../components/file/Inspector';
|
||||||
|
|
||||||
export interface DirectoryResponse {
|
export interface DirectoryResponse {
|
||||||
directory: IFile;
|
directory: IFile;
|
||||||
|
@ -26,8 +27,9 @@ export const ExplorerScreen: React.FC<{}> = () => {
|
||||||
if (!activeDirHash) return <></>;
|
if (!activeDirHash) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="relative w-full flex flex-row bg-white dark:bg-gray-900">
|
||||||
<FileList />
|
<FileList />
|
||||||
|
<Inspector />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,17 +6,18 @@ interface ExplorerStore {
|
||||||
dirs: Record<string, IDirectory>;
|
dirs: Record<string, IDirectory>;
|
||||||
activeDirHash: string;
|
activeDirHash: string;
|
||||||
history: string[];
|
history: string[];
|
||||||
selected: number;
|
selected: null | { index: number; file: IFile };
|
||||||
collectDir: (dirHash: IFile, files: IFile[]) => void;
|
collectDir: (dirHash: IFile, files: IFile[]) => void;
|
||||||
currentDir?: () => IFile[];
|
currentDir?: () => IFile[];
|
||||||
setSelected: (id: number) => void;
|
setSelected: (index: number | null, file?: IFile) => void;
|
||||||
|
goBack: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useExplorerStore = create<ExplorerStore>((set, get) => ({
|
export const useExplorerStore = create<ExplorerStore>((set, get) => ({
|
||||||
dirs: {},
|
dirs: {},
|
||||||
activeDirHash: '',
|
activeDirHash: '',
|
||||||
history: [],
|
history: [],
|
||||||
selected: 0,
|
selected: null,
|
||||||
collectDir: (directory, files) => {
|
collectDir: (directory, files) => {
|
||||||
set((state) =>
|
set((state) =>
|
||||||
produce(state, (draft) => {
|
produce(state, (draft) => {
|
||||||
|
@ -40,10 +41,10 @@ export const useExplorerStore = create<ExplorerStore>((set, get) => ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setSelected: (id) =>
|
setSelected: (index?: number | null, file?: IFile) =>
|
||||||
set((state) =>
|
set((state) =>
|
||||||
produce(state, (draft) => {
|
produce(state, (draft) => {
|
||||||
draft.selected = id;
|
draft.selected = !index || !file ? null : { index, file };
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -33,15 +33,23 @@ module.exports = {
|
||||||
DEFAULT: '#505468',
|
DEFAULT: '#505468',
|
||||||
50: '#F1F1F4',
|
50: '#F1F1F4',
|
||||||
100: '#E8E9ED',
|
100: '#E8E9ED',
|
||||||
200: '#E0E1E6',
|
150: '#E0E1E6',
|
||||||
|
200: '#D8DAE3',
|
||||||
|
250: '#D2D4DC',
|
||||||
300: '#C0C2CE',
|
300: '#C0C2CE',
|
||||||
400: '#6F7590',
|
350: '#A6AABF',
|
||||||
|
400: '#9196A8',
|
||||||
|
450: '#71758A',
|
||||||
500: '#505468',
|
500: '#505468',
|
||||||
600: '#434656',
|
550: '#434656',
|
||||||
700: '#353845',
|
600: '#3E414F',
|
||||||
800: '#282A34',
|
650: '#353845',
|
||||||
850: '#21212B',
|
700: '#333745',
|
||||||
900: '#1B1C23'
|
750: '#282A34',
|
||||||
|
800: '#262832',
|
||||||
|
850: '#30303E',
|
||||||
|
900: '#22242F',
|
||||||
|
950: '#15161D'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fontFamily: { sans: ['Inter', ...defaultTheme.fontFamily.sans] }
|
// fontFamily: { sans: ['Inter', ...defaultTheme.fontFamily.sans] }
|
||||||
|
|
34
yarn.lock
34
yarn.lock
|
@ -213,6 +213,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7":
|
||||||
|
version "7.15.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
||||||
|
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.14.5":
|
"@babel/template@^7.14.5":
|
||||||
version "7.14.5"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
|
||||||
|
@ -1060,7 +1067,7 @@ clone@^1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||||
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
|
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
|
||||||
|
|
||||||
clsx@^1.1.1:
|
clsx@^1.0.4, clsx@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||||
|
@ -1440,6 +1447,14 @@ dlv@^1.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
|
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
|
||||||
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
|
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
|
||||||
|
|
||||||
|
dom-helpers@^5.1.3:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||||
|
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.8.7"
|
||||||
|
csstype "^3.0.2"
|
||||||
|
|
||||||
dot-prop@^5.2.0:
|
dot-prop@^5.2.0:
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
|
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
|
||||||
|
@ -3371,6 +3386,11 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
|
react-lifecycles-compat@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
|
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||||
|
|
||||||
react-refresh@^0.10.0:
|
react-refresh@^0.10.0:
|
||||||
version "0.10.0"
|
version "0.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
|
||||||
|
@ -3405,6 +3425,18 @@ react-router@5.2.0:
|
||||||
tiny-invariant "^1.0.2"
|
tiny-invariant "^1.0.2"
|
||||||
tiny-warning "^1.0.0"
|
tiny-warning "^1.0.0"
|
||||||
|
|
||||||
|
react-virtualized@^9.22.3:
|
||||||
|
version "9.22.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.22.3.tgz#f430f16beb0a42db420dbd4d340403c0de334421"
|
||||||
|
integrity sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.7.2"
|
||||||
|
clsx "^1.0.4"
|
||||||
|
dom-helpers "^5.1.3"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-lifecycles-compat "^3.0.4"
|
||||||
|
|
||||||
react@^17.0.2:
|
react@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|
Loading…
Reference in a new issue