mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-06-30 11:23:33 +00:00
[ENG-1794] Overview Rework (#2555)
* StorageBar implementation * filekindstats reworked into interactive node graph * ui changes to graph * light/dark mode changes + ui edits * missing dependancy in package.json and minor visual improvements * fixed collision physics * d3 force package * fixed nodes going off screen * removed onNodeDragEnd * fix central node and add particle effect * fixed central node and typescript errors * bar graph/storage bar ui improvements * changed icons * totals * made ui changes * ui changes * minor requested ui changes * fixed spacing for ui * Remove extraneous newline in core/src/api/libraries.rs * Fix minor suggestions from code review * Fix typecheck * Auto format * refactor file kind stat card with css grid * ensure unidentified files never negative * FIxing some stats * Counting directories * fixed error with height of bars * updated storage bar * total files update --------- Co-authored-by: ameer2468 <33054370+ameer2468@users.noreply.github.com> Co-authored-by: James Pine <ijamespine@me.com> Co-authored-by: Lynx <141365347+iLynxcat@users.noreply.github.com> Co-authored-by: Vítor Vasconcellos <vasconcellos.dev@gmail.com> Co-authored-by: Lynx <ephemeral.lynx@protonmail.com> Co-authored-by: Ericson Soares <ericson.ds999@gmail.com>
This commit is contained in:
parent
f0bc308653
commit
f7b0e3bd06
|
@ -1,8 +1,8 @@
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useLibraryQuery } from '@sd/client';
|
||||
import { DotsThree } from 'phosphor-react-native';
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { uint32ArrayToBigInt, useLibraryQuery } from '@sd/client';
|
||||
import { tw } from '~/lib/tailwind';
|
||||
import { OverviewStackScreenProps } from '~/navigation/tabs/OverviewStack';
|
||||
|
||||
|
@ -24,12 +24,17 @@ export default function CategoriesScreen() {
|
|||
style={tw`h-8 w-8 rounded-full`}
|
||||
variant="gray"
|
||||
>
|
||||
<DotsThree weight='bold' size={18} color={'white'} />
|
||||
<DotsThree weight="bold" size={18} color={'white'} />
|
||||
</Button>
|
||||
</View>
|
||||
<View style={tw`flex-row flex-wrap gap-2`}>
|
||||
{kinds.data?.statistics
|
||||
?.sort((a, b) => b.count - a.count)
|
||||
?.sort((a, b) => {
|
||||
const aCount = uint32ArrayToBigInt(a.count);
|
||||
const bCount = uint32ArrayToBigInt(b.count);
|
||||
if (aCount === bCount) return 0;
|
||||
return aCount > bCount ? -1 : 1;
|
||||
})
|
||||
.filter((i) => i.kind !== 0)
|
||||
.slice(0, 6)
|
||||
.map((item) => {
|
||||
|
@ -49,7 +54,7 @@ export default function CategoriesScreen() {
|
|||
kind={kind}
|
||||
name={name}
|
||||
icon={icon}
|
||||
items={count}
|
||||
items={uint32ArrayToBigInt(count)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { formatNumber } from '@sd/client';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { ClassInput } from 'twrnc';
|
||||
import { formatNumber } from '@sd/client';
|
||||
import { tw, twStyle } from '~/lib/tailwind';
|
||||
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useSearchStore } from '~/stores/searchStore';
|
||||
|
||||
import { Icon, IconName } from '../icons/Icon';
|
||||
|
||||
interface CategoryItemProps {
|
||||
kind: number;
|
||||
name: string;
|
||||
items: number;
|
||||
items: bigint | number;
|
||||
icon: IconName;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
|
@ -29,14 +29,18 @@ const CategoryItem = ({ name, icon, items, style, kind }: CategoryItemProps) =>
|
|||
style
|
||||
)}
|
||||
onPress={() => {
|
||||
searchStore.updateFilters('kind', {
|
||||
name,
|
||||
icon: icon + '20' as IconName,
|
||||
id: kind
|
||||
}, true);
|
||||
searchStore.updateFilters(
|
||||
'kind',
|
||||
{
|
||||
name,
|
||||
icon: (icon + '20') as IconName,
|
||||
id: kind
|
||||
},
|
||||
true
|
||||
);
|
||||
navigation.navigate('SearchStack', {
|
||||
screen: 'Search',
|
||||
})
|
||||
screen: 'Search'
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon name={icon} size={56} />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useLibraryQuery } from '@sd/client';
|
||||
import { useMemo } from 'react';
|
||||
import { FlatList, View } from 'react-native';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import { uint32ArrayToBigInt, useLibraryQuery } from '@sd/client';
|
||||
import { IconName } from '~/components/icons/Icon';
|
||||
import ScreenContainer from '~/components/layout/ScreenContainer';
|
||||
import CategoryItem from '~/components/overview/CategoryItem';
|
||||
|
@ -22,7 +22,14 @@ const CategoriesScreen = () => {
|
|||
return (
|
||||
<ScreenContainer scrollview={false} style={tw`relative px-6 py-0`}>
|
||||
<FlatList
|
||||
data={filteredKinds?.sort((a, b) => b.count - a.count).filter((i) => i.kind !== 0)}
|
||||
data={filteredKinds
|
||||
?.sort((a, b) => {
|
||||
const aCount = uint32ArrayToBigInt(a.count);
|
||||
const bCount = uint32ArrayToBigInt(b.count);
|
||||
if (aCount === bCount) return 0;
|
||||
return aCount > bCount ? -1 : 1;
|
||||
})
|
||||
.filter((i) => i.kind !== 0)}
|
||||
numColumns={3}
|
||||
contentContainerStyle={tw`py-6`}
|
||||
keyExtractor={(item) => item.name}
|
||||
|
@ -46,7 +53,7 @@ const CategoriesScreen = () => {
|
|||
kind={kind}
|
||||
name={name}
|
||||
icon={icon}
|
||||
items={count}
|
||||
items={uint32ArrayToBigInt(count)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
|
|
@ -6,17 +6,15 @@ use crate::{
|
|||
Node,
|
||||
};
|
||||
|
||||
use futures::StreamExt;
|
||||
use prisma_client_rust::raw;
|
||||
use sd_core_heavy_lifting::JobId;
|
||||
|
||||
use sd_file_ext::kind::ObjectKind;
|
||||
use sd_p2p::RemoteIdentity;
|
||||
use sd_prisma::prisma::{indexer_rule, object, statistics};
|
||||
use tokio_stream::wrappers::IntervalStream;
|
||||
use tracing::{info, warn};
|
||||
use sd_prisma::prisma::{file_path, indexer_rule, object, statistics};
|
||||
use sd_utils::{db::size_in_bytes_from_db, u64_to_frontend};
|
||||
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap},
|
||||
convert::identity,
|
||||
pin::pin,
|
||||
sync::Arc,
|
||||
|
@ -25,8 +23,13 @@ use std::{
|
|||
|
||||
use async_channel as chan;
|
||||
use directories::UserDirs;
|
||||
use futures_concurrency::{future::Join, stream::Merge};
|
||||
use futures::StreamExt;
|
||||
use futures_concurrency::{
|
||||
future::{Join, TryJoin},
|
||||
stream::Merge,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use prisma_client_rust::{and, or, raw};
|
||||
use rspc::{alpha::AlphaRouter, ErrorCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
@ -36,7 +39,8 @@ use tokio::{
|
|||
sync::Mutex,
|
||||
time::{interval, Instant},
|
||||
};
|
||||
use tracing::{debug, error};
|
||||
use tokio_stream::wrappers::IntervalStream;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{utils::library, Ctx, R};
|
||||
|
@ -122,36 +126,125 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
})
|
||||
})
|
||||
.procedure("kindStatistics", {
|
||||
#[derive(Serialize, Deserialize, Type, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, Type, Default)]
|
||||
pub struct KindStatistic {
|
||||
kind: i32,
|
||||
name: String,
|
||||
count: i32,
|
||||
total_bytes: String,
|
||||
count: (u32, u32),
|
||||
total_bytes: (u32, u32),
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Type, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, Type, Default)]
|
||||
pub struct KindStatistics {
|
||||
statistics: Vec<KindStatistic>,
|
||||
total_identified_files: i32,
|
||||
total_unidentified_files: i32,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CountAndSize {
|
||||
count: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
R.with2(library()).query(|(_, library), _: ()| async move {
|
||||
let mut statistics: Vec<KindStatistic> = vec![];
|
||||
for kind in ObjectKind::iter() {
|
||||
let count = library
|
||||
let (total_unidentified_files, total_identified_files) = (
|
||||
library
|
||||
.db
|
||||
.file_path()
|
||||
.count(vec![
|
||||
file_path::is_dir::equals(Some(false)),
|
||||
file_path::cas_id::equals(None),
|
||||
file_path::object_id::equals(None),
|
||||
])
|
||||
.exec(),
|
||||
library
|
||||
.db
|
||||
.file_path()
|
||||
.count(vec![or!(
|
||||
file_path::is_dir::equals(Some(true)),
|
||||
and!(
|
||||
file_path::cas_id::not(None),
|
||||
file_path::object_id::not(None),
|
||||
),
|
||||
)])
|
||||
.exec(),
|
||||
)
|
||||
.try_join()
|
||||
.await?;
|
||||
|
||||
let mut statistics_by_kind = BTreeMap::from_iter(
|
||||
ObjectKind::iter().map(|kind| (kind as i32, CountAndSize::default())),
|
||||
);
|
||||
|
||||
let mut last_object_id = 0;
|
||||
|
||||
loop {
|
||||
let objects = library
|
||||
.db
|
||||
.object()
|
||||
.count(vec![object::kind::equals(Some(kind as i32))])
|
||||
.find_many(vec![object::id::gt(last_object_id)])
|
||||
.take(1000)
|
||||
.select(
|
||||
object::select!({ id kind file_paths: select { size_in_bytes_bytes } }),
|
||||
)
|
||||
.exec()
|
||||
.await?;
|
||||
|
||||
statistics.push(KindStatistic {
|
||||
kind: kind as i32,
|
||||
name: kind.to_string(),
|
||||
count: count as i32,
|
||||
total_bytes: "0".to_string(),
|
||||
});
|
||||
if let Some(last) = objects.last() {
|
||||
last_object_id = last.id;
|
||||
} else {
|
||||
break; // No more objects
|
||||
}
|
||||
|
||||
for object in objects {
|
||||
if let Some(kind) = object.kind {
|
||||
statistics_by_kind.entry(kind).and_modify(|count_and_size| {
|
||||
count_and_size.count += object.file_paths.len() as u64;
|
||||
count_and_size.size += object
|
||||
.file_paths
|
||||
.into_iter()
|
||||
.map(|file_path| {
|
||||
file_path
|
||||
.size_in_bytes_bytes
|
||||
.map(|size| size_in_bytes_from_db(&size))
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.sum::<u64>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(KindStatistics { statistics })
|
||||
// This is a workaround for the fact that we don't assign object to directories yet
|
||||
if let Some(count_and_size) =
|
||||
statistics_by_kind.get_mut(&(ObjectKind::Folder as i32))
|
||||
{
|
||||
count_and_size.count = library
|
||||
.db
|
||||
.file_path()
|
||||
.count(vec![file_path::is_dir::equals(Some(true))])
|
||||
.exec()
|
||||
.await? as u64;
|
||||
}
|
||||
|
||||
Ok(KindStatistics {
|
||||
statistics: ObjectKind::iter()
|
||||
.map(|kind| {
|
||||
let int_kind = kind as i32;
|
||||
let CountAndSize { count, size } =
|
||||
statistics_by_kind.get(&int_kind).expect("can't fail");
|
||||
|
||||
KindStatistic {
|
||||
kind: int_kind,
|
||||
name: kind.to_string(),
|
||||
count: u64_to_frontend(*count),
|
||||
total_bytes: u64_to_frontend(*size),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
total_identified_files: total_identified_files as i32,
|
||||
total_unidentified_files: total_unidentified_files as i32,
|
||||
})
|
||||
})
|
||||
})
|
||||
.procedure("create", {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::{api::utils::get_size, library::Library, volume::get_volumes, Node};
|
||||
|
||||
use sd_prisma::prisma::statistics;
|
||||
use sd_utils::db::size_in_bytes_from_db;
|
||||
|
||||
use chrono::Utc;
|
||||
use tracing::{error, info};
|
||||
|
||||
use super::LibraryManagerError;
|
||||
use tracing::{error, info};
|
||||
|
||||
pub async fn update_library_statistics(
|
||||
node: &Node,
|
||||
|
@ -45,7 +46,7 @@ pub async fn update_library_statistics(
|
|||
.map(|location| {
|
||||
location
|
||||
.size_in_bytes
|
||||
.map(|bytes| bytes.iter().fold(0, |acc, &x| acc * 256 + x as u64))
|
||||
.map(|size| size_in_bytes_from_db(&size))
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.sum::<u64>();
|
||||
|
|
|
@ -72,3 +72,263 @@ export const Component = () => {
|
|||
</ExplorerContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// NOTE -> this is code for the node graph. The plan is to implement this in network (moved from overview page). Jamie asked me to save the code somewhere
|
||||
// so placing it here for now!
|
||||
|
||||
// import { getIcon } from '@sd/assets/util';
|
||||
// import { useLibraryQuery } from '@sd/client';
|
||||
// import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
// import { useIsDark } from '~/hooks';
|
||||
// import ForceGraph2D from 'react-force-graph-2d';
|
||||
// import { useNavigate } from 'react-router';
|
||||
// import * as d3 from 'd3-force';
|
||||
|
||||
// //million-ignore
|
||||
// const canvasWidth = 700
|
||||
// const canvasHeight = 600;
|
||||
|
||||
// interface KindStatistic {
|
||||
// kind: number;
|
||||
// name: string;
|
||||
// count: number;
|
||||
// total_bytes: string;
|
||||
// }
|
||||
|
||||
// interface Node {
|
||||
// id: string | number;
|
||||
// name: string;
|
||||
// val: number;
|
||||
// fx?: number;
|
||||
// fy?: number;
|
||||
// x?: number;
|
||||
// y?: number;
|
||||
// }
|
||||
|
||||
// interface Link {
|
||||
// source: string | number;
|
||||
// target: string | number;
|
||||
// }
|
||||
|
||||
// interface GraphData {
|
||||
// nodes: Node[];
|
||||
// links: Link[];
|
||||
// }
|
||||
|
||||
// const FileKindStatistics: React.FC = () => {
|
||||
// const isDark = useIsDark();
|
||||
// const navigate = useNavigate();
|
||||
// const { data } = useLibraryQuery(['library.kindStatistics']);
|
||||
// const [graphData, setGraphData] = useState<GraphData>({ nodes: [], links: [] });
|
||||
// const iconsRef = useRef<{ [key: string]: HTMLImageElement }>({});
|
||||
// const containerRef = useRef<HTMLDivElement>(null);
|
||||
// const fgRef = useRef<any>(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (data) {
|
||||
// const statistics: KindStatistic[] = data.statistics
|
||||
// .filter((item: KindStatistic) => item.count != 0)
|
||||
// .sort((a: KindStatistic, b: KindStatistic) => b.count - a.count)
|
||||
// // TODO: eventually allow users to select and save which file kinds are shown
|
||||
// .slice(0, 18); // Get the top 18 highest file kinds
|
||||
|
||||
// const totalFilesCount = statistics.reduce((sum, item) => sum + item.count, 0);
|
||||
// const nodes = [
|
||||
// { id: 'center', name: 'Total Files', val: totalFilesCount },
|
||||
// ...statistics.map(item => ({
|
||||
// id: item.kind,
|
||||
// name: item.name,
|
||||
// val: item.count,
|
||||
// }))
|
||||
// ];
|
||||
|
||||
// const links = statistics.map(item => ({
|
||||
// source: 'center',
|
||||
// target: item.kind,
|
||||
// }));
|
||||
|
||||
// setGraphData({ nodes, links });
|
||||
|
||||
// // Preload icons, this is for rendering purposes
|
||||
// statistics.forEach(item => {
|
||||
// const iconName = item.name;
|
||||
// if (!iconsRef.current[iconName]) {
|
||||
// const img = new Image();
|
||||
// img.src = getIcon(iconName, isDark);
|
||||
// iconsRef.current[iconName] = img;
|
||||
// }
|
||||
// });
|
||||
|
||||
// // d3 stuff for changing physics of the nodes
|
||||
// fgRef.current.d3Force('link').distance(110); // Adjust link distance to make links shorter
|
||||
// fgRef.current.d3Force('charge').strength(-50); // how hard the nodes repel
|
||||
// fgRef.current.d3Force('center').strength(10); // Adjust center strength for stability
|
||||
// fgRef.current.d3Force('collision', d3.forceCollide().radius(25)); // Add collision force with radius. Should be a little larger than radius of nodes.
|
||||
|
||||
// fgRef.current.d3Force('y', d3.forceY(canvasHeight / 5).strength((1.2))); // strong force to ensure nodes don't spill out of canvas
|
||||
// }
|
||||
// }, [data, isDark]);
|
||||
|
||||
// const paintNode = useCallback((node: any, ctx: CanvasRenderingContext2D, globalScale: number) => {
|
||||
// const fontSize = 0.6 / globalScale;
|
||||
// ctx.font = `400 ${fontSize}em ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`;
|
||||
// ctx.textAlign = 'center';
|
||||
// ctx.textBaseline = 'middle';
|
||||
|
||||
// const darkColor = 'rgb(34, 34, 45)';
|
||||
// const lightColor = 'rgb(252, 252, 254)';
|
||||
|
||||
// const x = isFinite(node.x) ? node.x : 0;
|
||||
// const y = isFinite(node.y) ? node.y : 0;
|
||||
|
||||
// if (node.name === 'Total Files') {
|
||||
// const radius = 25;
|
||||
// const borderWidth = 0.5;
|
||||
|
||||
// // Create linear gradient for light mode
|
||||
// const lightGradient = ctx.createLinearGradient(x - radius, y - radius, x + radius, y + radius);
|
||||
// lightGradient.addColorStop(0, 'rgb(117, 177, 249)');
|
||||
// lightGradient.addColorStop(1, 'rgb(0, 76, 153)');
|
||||
|
||||
// // Create linear gradient for dark mode
|
||||
// const darkGradient = ctx.createLinearGradient(x - radius, y - radius, x + radius, y + radius);
|
||||
// darkGradient.addColorStop(0, 'rgb(255, 13, 202)');
|
||||
// darkGradient.addColorStop(1, 'rgb(128, 0, 255)');
|
||||
|
||||
// // Draw filled circle with gradient border
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false);
|
||||
// ctx.fillStyle = isDark ? darkGradient : lightGradient;
|
||||
// ctx.fill();
|
||||
|
||||
// // Draw inner circle to create the border effect
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(node.x, node.y, radius - borderWidth, 0, 2 * Math.PI, false);
|
||||
// ctx.fillStyle = isDark ? darkColor : lightColor;
|
||||
// ctx.fill();
|
||||
|
||||
// // Add inner shadow
|
||||
// const shadowGradient = ctx.createRadialGradient(x, y, radius * 0.5, x, y, radius);
|
||||
// shadowGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
|
||||
// shadowGradient.addColorStop(1, isDark ? 'rgba(255, 93, 234, 0.1' : 'rgba(66, 97, 255, 0.05)');
|
||||
|
||||
// ctx.globalCompositeOperation = 'source-atop';
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false);
|
||||
// ctx.fillStyle = shadowGradient;
|
||||
// ctx.fill();
|
||||
|
||||
// // Draw text
|
||||
// ctx.fillStyle = isDark ? 'rgba(255, 255, 255, 1)' : 'rgba(10, 10, 10, 0.8)';
|
||||
// ctx.font = `bold ${fontSize * 2}em ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`;
|
||||
// ctx.fillText(node.val, node.x, node.y - fontSize * 9);
|
||||
|
||||
// ctx.fillStyle = isDark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(10, 10, 10, 0.8)';
|
||||
// ctx.font = `400 ${fontSize * 1.1}em ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`;
|
||||
// ctx.fillText(node.name, node.x, node.y + fontSize * 25);
|
||||
// } else {
|
||||
// const iconName = node.name;
|
||||
// const iconImg = iconsRef.current[iconName];
|
||||
// const iconSize = 25 / globalScale;
|
||||
// const textYPos = node.y + iconSize;
|
||||
|
||||
// // Draw shadow
|
||||
// ctx.shadowColor = isDark ? 'rgb(44, 45, 58)' : 'rgba(0, 0, 0, 0.1)';
|
||||
// ctx.shadowBlur = 0.5;
|
||||
// ctx.shadowOffsetX = -0.5;
|
||||
// ctx.shadowOffsetY = -2;
|
||||
|
||||
// // Draw node circle
|
||||
// const radius = 18;
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false);
|
||||
// ctx.fillStyle = isDark ? darkColor : lightColor;
|
||||
// ctx.fill();
|
||||
// ctx.shadowColor = 'transparent';
|
||||
|
||||
// if (iconImg) {
|
||||
// ctx.drawImage(iconImg, node.x - iconSize / 2, node.y - iconSize, iconSize, iconSize);
|
||||
// }
|
||||
|
||||
// ctx.fillStyle = isDark ? 'white' : 'black';
|
||||
|
||||
// // Truncate node name if it is too long
|
||||
// let truncatedName = node.name;
|
||||
// if (node.name.length > 10) {
|
||||
// truncatedName = node.name.slice(0, 6) + "...";
|
||||
// }
|
||||
// ctx.fillText(truncatedName, node.x, textYPos - 9);
|
||||
|
||||
// ctx.fillStyle = isDark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.5)';
|
||||
// ctx.fillText(node.val, node.x, textYPos - 2);
|
||||
// }
|
||||
// }, [isDark]);
|
||||
|
||||
// const handleNodeClick = useCallback((node: any) => {
|
||||
// if (node.id !== 'center') {
|
||||
// const path = {
|
||||
// pathname: '../search',
|
||||
// search: new URLSearchParams({
|
||||
// filters: JSON.stringify([{ object: { kind: { in: [node.id] } } }])
|
||||
// }).toString()
|
||||
// };
|
||||
// navigate(path);
|
||||
// }
|
||||
// }, [navigate]);
|
||||
|
||||
// const handleEngineTick = () => {
|
||||
// const centerNode = graphData.nodes.find((node: any) => node.id === 'center');
|
||||
// if (centerNode) {
|
||||
// centerNode.fx = 0;
|
||||
// centerNode.fy = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// useEffect(() => {
|
||||
// if (fgRef.current) {
|
||||
// fgRef.current.d3Force('center', d3.forceCenter());
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
// const paintPointerArea = useCallback((node: any, color: string, ctx: CanvasRenderingContext2D, globalScale: number) => {
|
||||
// const size = 30 / globalScale;
|
||||
// ctx.fillStyle = color;
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(node.x, node.y, size, 0, 2 * Math.PI, false);
|
||||
// ctx.fill();
|
||||
// }, []);
|
||||
|
||||
// return (
|
||||
// <div className="relative bottom-48 h-[200px] w-full" ref={containerRef}>
|
||||
// {data ? (
|
||||
// <ForceGraph2D
|
||||
// ref={fgRef}
|
||||
// graphData={graphData}
|
||||
// nodeId="id"
|
||||
// linkSource="source"
|
||||
// linkTarget="target"
|
||||
// width={canvasWidth}
|
||||
// height={canvasHeight}
|
||||
// backgroundColor="transparent"
|
||||
// nodeCanvasObject={paintNode}
|
||||
// linkWidth={0.5}
|
||||
// nodeLabel=""
|
||||
// dagMode="radialout"
|
||||
// linkColor={() => isDark ? '#2C2D3A' : 'rgba(0, 0, 0, 0.2)'}
|
||||
// onNodeClick={handleNodeClick}
|
||||
// enableZoomInteraction={false}
|
||||
// enablePanInteraction={false}
|
||||
// dagLevelDistance={100}
|
||||
// warmupTicks={500}
|
||||
// d3VelocityDecay={0.75}
|
||||
// onEngineTick={handleEngineTick}
|
||||
// nodePointerAreaPaint={paintPointerArea}
|
||||
// />
|
||||
// ) : (
|
||||
// <div>Loading...</div>
|
||||
// )}
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default React.memo(FileKindStatistics);
|
||||
|
|
|
@ -1,99 +1,252 @@
|
|||
import { Info } from '@phosphor-icons/react';
|
||||
import { getIcon } from '@sd/assets/util';
|
||||
import clsx from 'clsx';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { formatNumber, SearchFilterArgs, useLibraryQuery } from '@sd/client';
|
||||
import { Icon } from '~/components';
|
||||
import { useLocale } from '~/hooks';
|
||||
import React, { MouseEventHandler, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { KindStatistic, uint32ArrayToBigInt, useLibraryQuery } from '@sd/client';
|
||||
import { Card, Tooltip } from '@sd/ui';
|
||||
import { useIsDark, useLocale } from '~/hooks';
|
||||
|
||||
import { translateKindName } from '../Explorer/util';
|
||||
const INFO_ICON_CLASSLIST = 'inline size-3 text-ink-faint opacity-0';
|
||||
const TOTAL_FILES_CLASSLIST =
|
||||
'flex items-center justify-between whitespace-nowrap text-sm font-medium text-ink-dull mt-2 px-1';
|
||||
const UNIDENTIFIED_FILES_CLASSLIST = 'relative flex items-center text-xs text-ink-faint';
|
||||
|
||||
export default () => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const kinds = useLibraryQuery(['library.kindStatistics']);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* This is awful, will replace icons accordingly and memo etc */}
|
||||
{kinds.data?.statistics
|
||||
?.sort((a, b) => b.count - a.count)
|
||||
.filter((i) => i.kind !== 0)
|
||||
.map(({ kind, name, count }) => {
|
||||
let icon = name;
|
||||
switch (name) {
|
||||
case 'Code':
|
||||
icon = 'Terminal';
|
||||
break;
|
||||
case 'Unknown':
|
||||
icon = 'Undefined';
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<motion.div
|
||||
viewport={{
|
||||
root: ref,
|
||||
// WARNING: Edge breaks if the values are not postfixed with px or %
|
||||
margin: '0% -120px 0% 0%'
|
||||
}}
|
||||
className={clsx('min-w-fit')}
|
||||
key={kind}
|
||||
>
|
||||
<KindItem
|
||||
kind={kind}
|
||||
name={translateKindName(name)}
|
||||
icon={icon}
|
||||
items={count}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
const mapFractionalValue = (numerator: bigint, denominator: bigint, maxValue: bigint): string => {
|
||||
const result = ((numerator * maxValue) / denominator).toString();
|
||||
return result;
|
||||
};
|
||||
|
||||
interface KindItemProps {
|
||||
kind: number;
|
||||
name: string;
|
||||
items: number;
|
||||
icon: string;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
disabled?: boolean;
|
||||
const formatNumberWithCommas = (number: number | bigint) => number.toLocaleString();
|
||||
|
||||
const interpolateHexColor = (color1: string, color2: string, factor: number): string => {
|
||||
const hex = (color: string) => parseInt(color.slice(1), 16);
|
||||
const r = Math.round((1 - factor) * (hex(color1) >> 16) + factor * (hex(color2) >> 16));
|
||||
const g = Math.round(
|
||||
(1 - factor) * ((hex(color1) >> 8) & 0x00ff) + factor * ((hex(color2) >> 8) & 0x00ff)
|
||||
);
|
||||
const b = Math.round(
|
||||
(1 - factor) * (hex(color1) & 0x0000ff) + factor * (hex(color2) & 0x0000ff)
|
||||
);
|
||||
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
|
||||
};
|
||||
|
||||
interface FileKind {
|
||||
kind: string;
|
||||
count: bigint;
|
||||
id: number;
|
||||
}
|
||||
|
||||
const KindItem = ({ kind, name, icon, items, selected, onClick, disabled }: KindItemProps) => {
|
||||
interface FileKindStatsProps {}
|
||||
|
||||
const FileKindStats: React.FC<FileKindStatsProps> = () => {
|
||||
const isDark = useIsDark();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<Link
|
||||
to={{
|
||||
const { data } = useLibraryQuery(['library.kindStatistics']);
|
||||
const [fileKinds, setFileKinds] = useState<FileKind[]>([]);
|
||||
const [cardWidth, setCardWidth] = useState<number>(0);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const iconsRef = useRef<{ [key: string]: HTMLImageElement }>({});
|
||||
|
||||
const BAR_MAX_HEIGHT = 115n;
|
||||
const BAR_COLOR_START = '#3A7ECC';
|
||||
const BAR_COLOR_END = '#004C99';
|
||||
|
||||
const formatCount = (count: number | bigint): string => {
|
||||
const bigIntCount = typeof count === 'number' ? BigInt(count) : count;
|
||||
|
||||
return bigIntCount >= 1000n ? `${bigIntCount / 1000n}K` : count.toString();
|
||||
};
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (containerRef.current) {
|
||||
const factor = window.innerWidth > 1500 ? 0.35 : 0.4;
|
||||
setCardWidth(window.innerWidth * factor);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
handleResize();
|
||||
|
||||
const containerElement = containerRef.current;
|
||||
if (containerElement) {
|
||||
const observer = new MutationObserver(handleResize);
|
||||
observer.observe(containerElement, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributeFilter: ['style']
|
||||
});
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, [handleResize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const statistics: KindStatistic[] = data.statistics
|
||||
.filter(
|
||||
(item: { kind: number; count: any }) => uint32ArrayToBigInt(item.count) !== 0n
|
||||
)
|
||||
.sort((a: { count: any }, b: { count: any }) => {
|
||||
const aCount = uint32ArrayToBigInt(a.count);
|
||||
const bCount = uint32ArrayToBigInt(b.count);
|
||||
if (aCount === bCount) return 0;
|
||||
return aCount > bCount ? -1 : 1;
|
||||
});
|
||||
|
||||
setFileKinds(
|
||||
statistics.map((item) => ({
|
||||
kind: item.name,
|
||||
count: uint32ArrayToBigInt(item.count),
|
||||
id: item.kind
|
||||
}))
|
||||
);
|
||||
|
||||
statistics.forEach((item) => {
|
||||
const iconName = item.name;
|
||||
if (!iconsRef.current[iconName]) {
|
||||
const img = new Image();
|
||||
img.src = getIcon(iconName + '20', isDark);
|
||||
iconsRef.current[iconName] = img;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [data, isDark]);
|
||||
|
||||
const sortedFileKinds = [...fileKinds].sort((a, b) => {
|
||||
if (a.count === b.count) return 0;
|
||||
return a.count > b.count ? -1 : 1;
|
||||
});
|
||||
|
||||
const maxFileCount = sortedFileKinds && sortedFileKinds[0] ? sortedFileKinds[0].count : 0n;
|
||||
|
||||
const barGap = 12;
|
||||
const barCount = sortedFileKinds.length;
|
||||
const totalGapWidth = barGap * (barCount - 5);
|
||||
const makeBarClickHandler =
|
||||
(fileKind: FileKind): MouseEventHandler<HTMLDivElement> | undefined =>
|
||||
() => {
|
||||
const path = {
|
||||
pathname: '../search',
|
||||
search: new URLSearchParams({
|
||||
filters: JSON.stringify([
|
||||
{ object: { kind: { in: [kind] } } }
|
||||
] as SearchFilterArgs[])
|
||||
filters: JSON.stringify([{ object: { kind: { in: [fileKind.id] } } }])
|
||||
}).toString()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
'flex shrink-0 items-center rounded-lg py-1 text-sm outline-none focus:bg-app-selectedItem/50',
|
||||
selected && 'bg-app-selectedItem',
|
||||
disabled && 'cursor-not-allowed opacity-30'
|
||||
)}
|
||||
};
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card
|
||||
ref={containerRef}
|
||||
className="max-w-1/2 group mx-1 flex h-[220px] w-full min-w-[400px] shrink-0 flex-col gap-2 bg-app-box/50"
|
||||
>
|
||||
<Icon name={icon as any} className="mr-3 size-12" />
|
||||
<div className="pr-5">
|
||||
<h2 className="text-sm font-medium">{name}</h2>
|
||||
{items !== undefined && (
|
||||
<p className="text-xs text-ink-faint">
|
||||
{t('item_with_count', { count: items })}
|
||||
</p>
|
||||
)}
|
||||
<div className={TOTAL_FILES_CLASSLIST}>
|
||||
<Tooltip className="flex items-center" label={t('bar_graph_info')}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={clsx(
|
||||
'text-xl font-black',
|
||||
isDark ? 'text-white' : 'text-black'
|
||||
)}
|
||||
>
|
||||
{data?.total_identified_files
|
||||
? formatNumberWithCommas(data.total_identified_files)
|
||||
: '0'}{' '}
|
||||
</span>
|
||||
<div className="flex items-center">
|
||||
{t('total_files')}
|
||||
<Info
|
||||
weight="fill"
|
||||
className={`ml-1 ${INFO_ICON_CLASSLIST} opacity-0 transition-opacity duration-300 group-hover:opacity-70`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className={UNIDENTIFIED_FILES_CLASSLIST}>
|
||||
<Tooltip label={t('unidentified_files_info')}>
|
||||
<span>
|
||||
{data?.total_unidentified_files
|
||||
? formatNumberWithCommas(data.total_unidentified_files)
|
||||
: '0'}{' '}
|
||||
unidentified files
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="relative mx-2.5 grid grow grid-cols-[repeat(auto-fit,_minmax(0,_1fr))] grid-rows-[136px_12px] items-end justify-items-center gap-x-1.5 gap-y-1 self-stretch">
|
||||
{sortedFileKinds.map((fileKind, index) => {
|
||||
const iconImage = iconsRef.current[fileKind.kind];
|
||||
const barColor = interpolateHexColor(
|
||||
BAR_COLOR_START,
|
||||
BAR_COLOR_END,
|
||||
index / (barCount - 1)
|
||||
);
|
||||
|
||||
const barHeight =
|
||||
mapFractionalValue(fileKind.count, maxFileCount, BAR_MAX_HEIGHT) + 'px';
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
asChild
|
||||
key={fileKind.kind}
|
||||
label={
|
||||
formatNumberWithCommas(fileKind.count) +
|
||||
' ' +
|
||||
fileKind.kind +
|
||||
's'
|
||||
}
|
||||
position="left"
|
||||
>
|
||||
<div
|
||||
className="relative flex w-full min-w-8 max-w-10 grow cursor-pointer flex-col items-center"
|
||||
onDoubleClick={makeBarClickHandler(fileKind)}
|
||||
>
|
||||
{iconImage && (
|
||||
<img
|
||||
src={iconImage.src}
|
||||
alt={fileKind.kind}
|
||||
className="relative mb-1 size-4 duration-500"
|
||||
/>
|
||||
)}
|
||||
<motion.div
|
||||
className="flex w-full flex-col items-center rounded transition-all duration-500"
|
||||
initial={{ height: 0 }}
|
||||
animate={{ height: barHeight }}
|
||||
transition={{ duration: 0.4, ease: [0.42, 0, 0.58, 1] }}
|
||||
style={{
|
||||
height: barHeight,
|
||||
minHeight: '2px',
|
||||
backgroundColor: barColor
|
||||
}}
|
||||
></motion.div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div
|
||||
className="sm col-span-1 row-start-2 row-end-auto text-center text-[10px] font-medium text-ink-faint"
|
||||
style={{
|
||||
borderRadius: '3px'
|
||||
}}
|
||||
>
|
||||
{formatCount(fileKind.count)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileKindStats;
|
||||
|
|
|
@ -2,8 +2,10 @@ import { Info } from '@phosphor-icons/react';
|
|||
import clsx from 'clsx';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { humanizeSize, Statistics, useLibraryContext, useLibraryQuery } from '@sd/client';
|
||||
import { Tooltip } from '@sd/ui';
|
||||
import { useCounter, useLocale } from '~/hooks';
|
||||
import { Card, Tooltip } from '@sd/ui';
|
||||
import { useCounter, useIsDark, useLocale } from '~/hooks';
|
||||
|
||||
import StorageBar from './StorageBar';
|
||||
|
||||
interface StatItemProps {
|
||||
title: string;
|
||||
|
@ -12,15 +14,18 @@ interface StatItemProps {
|
|||
info?: string;
|
||||
}
|
||||
|
||||
interface Section {
|
||||
name: string;
|
||||
value: number;
|
||||
color: string;
|
||||
tooltip: string;
|
||||
}
|
||||
|
||||
let mounted = false;
|
||||
|
||||
const StatItem = (props: StatItemProps) => {
|
||||
const { title, bytes, isLoading } = props;
|
||||
|
||||
// This is important to the counter working.
|
||||
// On first render of the counter this will potentially be `false` which means the counter should the count up.
|
||||
// but in a `useEffect` `mounted` will be set to `true` so that subsequent renders of the counter will not run the count up.
|
||||
// The acts as a cache of the value of `mounted` on the first render of this `StateItem`.
|
||||
const [isMounted] = useState(mounted);
|
||||
|
||||
const size = humanizeSize(bytes);
|
||||
|
@ -36,7 +41,7 @@ const StatItem = (props: StatItemProps) => {
|
|||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'group/stat flex w-32 shrink-0 flex-col duration-75',
|
||||
'group/stat flex w-36 shrink-0 flex-col duration-75',
|
||||
!bytes && 'hidden'
|
||||
)}
|
||||
>
|
||||
|
@ -46,7 +51,7 @@ const StatItem = (props: StatItemProps) => {
|
|||
<Tooltip label={props.info}>
|
||||
<Info
|
||||
weight="fill"
|
||||
className="-mt-0.5 ml-1 inline size-3 text-ink-faint opacity-0 transition-opacity group-hover/stat:opacity-70"
|
||||
className="-mt-0.5 ml-1 inline size-3 text-ink-faint opacity-0 transition-opacity duration-300 group-hover/stat:opacity-70"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
@ -69,42 +74,101 @@ const StatItem = (props: StatItemProps) => {
|
|||
};
|
||||
|
||||
const LibraryStats = () => {
|
||||
const isDark = useIsDark();
|
||||
const { library } = useLibraryContext();
|
||||
|
||||
const stats = useLibraryQuery(['library.statistics']);
|
||||
const storageBarData = useLibraryQuery(['library.kindStatistics']).data?.statistics;
|
||||
console.log(storageBarData);
|
||||
console.log(stats);
|
||||
const { t } = useLocale();
|
||||
|
||||
useEffect(() => {
|
||||
if (!stats.isLoading) mounted = true;
|
||||
});
|
||||
|
||||
const { t } = useLocale();
|
||||
}, [stats.isLoading]);
|
||||
|
||||
const StatItemNames: Partial<Record<keyof Statistics, string>> = {
|
||||
total_local_bytes_capacity: t('total_bytes_capacity'),
|
||||
total_local_bytes_free: t('total_bytes_free'),
|
||||
total_library_bytes: t('library_bytes'),
|
||||
library_db_size: t('library_db_size'),
|
||||
total_local_bytes_capacity: t('total_bytes_capacity'),
|
||||
total_library_preview_media_bytes: t('preview_media_bytes'),
|
||||
total_local_bytes_free: t('total_bytes_free'),
|
||||
total_local_bytes_used: t('total_bytes_used')
|
||||
total_library_preview_media_bytes: t('preview_media_bytes')
|
||||
};
|
||||
|
||||
const StatDescriptions: Partial<Record<keyof Statistics, string>> = {
|
||||
total_local_bytes_capacity: t('total_bytes_capacity_description'),
|
||||
total_library_preview_media_bytes: t('preview_media_bytes_description'),
|
||||
total_local_bytes_free: t('total_bytes_free_description'),
|
||||
total_library_bytes: t('library_bytes_description'),
|
||||
library_db_size: t('library_db_size_description'),
|
||||
total_local_bytes_free: t('total_bytes_free_description'),
|
||||
total_local_bytes_used: t('total_bytes_used_description')
|
||||
total_library_preview_media_bytes: t('preview_media_bytes_description')
|
||||
};
|
||||
|
||||
const displayableStatItems = Object.keys(
|
||||
StatItemNames
|
||||
) as unknown as keyof typeof StatItemNames;
|
||||
|
||||
if (!stats.data || !stats.data.statistics) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const { statistics } = stats.data;
|
||||
const totalSpace = Number(statistics.total_local_bytes_capacity);
|
||||
const totalUsedSpace = Number(statistics.total_local_bytes_used);
|
||||
|
||||
// Define the major categories and aggregate the "Other" category
|
||||
const majorCategories = ['Document', 'Text', 'Image', 'Video'];
|
||||
const aggregatedData = (storageBarData ?? []).reduce(
|
||||
(acc, curr) => {
|
||||
const category = majorCategories.includes(curr.name) ? curr.name : 'Other';
|
||||
if (!acc[category]) {
|
||||
acc[category] = { total_bytes: 0 };
|
||||
}
|
||||
acc[category]!.total_bytes += curr.total_bytes[1];
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, { total_bytes: number }>
|
||||
);
|
||||
|
||||
// Calculate the used space and determine the System Data
|
||||
const usedSpace = Object.values(aggregatedData).reduce(
|
||||
(acc, curr) => acc + curr.total_bytes,
|
||||
0
|
||||
);
|
||||
const systemDataBytes = totalUsedSpace - usedSpace;
|
||||
|
||||
if (!aggregatedData['Other']) {
|
||||
aggregatedData['Other'] = { total_bytes: 0 };
|
||||
}
|
||||
|
||||
const sections: Section[] = Object.entries(aggregatedData).map(([name, data], index) => {
|
||||
const colors = [
|
||||
'#3A7ECC', // Slightly Darker Blue 400
|
||||
'#AAAAAA', // Gray
|
||||
'#004C99', // Tailwind Blue 700
|
||||
'#2563EB', // Tailwind Blue 500
|
||||
'#00274D' // Dark Navy Blue,
|
||||
];
|
||||
|
||||
const color = colors[index % colors.length] || '#8F8F8F'; // Use a default color if colors array is empty
|
||||
return {
|
||||
name,
|
||||
value: data.total_bytes,
|
||||
color,
|
||||
tooltip: `${name}`
|
||||
};
|
||||
});
|
||||
|
||||
// Add System Data section
|
||||
sections.push({
|
||||
name: 'System Data',
|
||||
value: systemDataBytes,
|
||||
color: '#707070', // Gray for System Data
|
||||
tooltip: 'System data that exists outside of your Spacedrive library'
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex w-full">
|
||||
<div className="flex gap-3 overflow-hidden">
|
||||
{Object.entries(stats?.data?.statistics || [])
|
||||
// sort the stats by the order of the displayableStatItems
|
||||
<Card className="flex h-[220px] w-[750px] shrink-0 flex-col bg-app-box/50">
|
||||
<div className="mb-1 flex overflow-hidden p-4">
|
||||
{Object.entries(statistics)
|
||||
.sort(
|
||||
([a], [b]) =>
|
||||
displayableStatItems.indexOf(a) - displayableStatItems.indexOf(b)
|
||||
|
@ -115,14 +179,17 @@ const LibraryStats = () => {
|
|||
<StatItem
|
||||
key={`${library.uuid} ${key}`}
|
||||
title={StatItemNames[key as keyof Statistics]!}
|
||||
bytes={BigInt(value)}
|
||||
bytes={BigInt(value as number)}
|
||||
isLoading={stats.isLoading}
|
||||
info={StatDescriptions[key as keyof Statistics]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<StorageBar sections={sections} totalSpace={totalSpace} />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
129
interface/app/$libraryId/overview/StorageBar.tsx
Normal file
129
interface/app/$libraryId/overview/StorageBar.tsx
Normal file
|
@ -0,0 +1,129 @@
|
|||
import React, { useState } from 'react';
|
||||
import { humanizeSize } from '@sd/client';
|
||||
import { Tooltip } from '@sd/ui';
|
||||
import { useIsDark } from '~/hooks';
|
||||
|
||||
const BARWIDTH = 700;
|
||||
|
||||
const lightenColor = (color: string, percent: number) => {
|
||||
const num = parseInt(color.replace('#', ''), 16);
|
||||
const amt = Math.round(2.55 * percent);
|
||||
const R = (num >> 16) + amt;
|
||||
const G = ((num >> 8) & 0x00ff) + amt;
|
||||
const B = (num & 0x0000ff) + amt;
|
||||
return `#${(
|
||||
0x1000000 +
|
||||
(R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
|
||||
(G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
|
||||
(B < 255 ? (B < 1 ? 0 : B) : 255)
|
||||
)
|
||||
.toString(16)
|
||||
.slice(1)
|
||||
.toUpperCase()}`;
|
||||
};
|
||||
|
||||
interface Section {
|
||||
name: string;
|
||||
value: number;
|
||||
color: string;
|
||||
tooltip: string;
|
||||
}
|
||||
|
||||
interface StorageBarProps {
|
||||
sections: Section[];
|
||||
totalSpace: number;
|
||||
}
|
||||
|
||||
const StorageBar: React.FC<StorageBarProps> = ({ sections, totalSpace }) => {
|
||||
const isDark = useIsDark();
|
||||
const [hoveredSectionIndex, setHoveredSectionIndex] = useState<number | null>(null);
|
||||
|
||||
const getPercentage = (value: number) => {
|
||||
const percentage = value / totalSpace;
|
||||
const pixvalue = BARWIDTH * percentage;
|
||||
return `${pixvalue.toFixed(2)}px`;
|
||||
};
|
||||
|
||||
const usedSpace = sections.reduce((acc, section) => acc + section.value, 0);
|
||||
const unusedSpace = totalSpace - usedSpace;
|
||||
|
||||
const nonSystemSections = sections.filter((section) => section.name !== 'System Data');
|
||||
const systemSection = sections.find((section) => section.name === 'System Data');
|
||||
|
||||
return (
|
||||
<div className="w-auto p-3">
|
||||
<div className="relative mt-1 flex h-6 overflow-hidden rounded">
|
||||
{nonSystemSections.map((section, index) => {
|
||||
const humanizedValue = humanizeSize(section.value);
|
||||
const isHovered = hoveredSectionIndex === index;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={index}
|
||||
label={`${humanizedValue.value} ${humanizedValue.unit}`}
|
||||
position="top"
|
||||
>
|
||||
<div
|
||||
className={`relative h-full`}
|
||||
style={{
|
||||
width: getPercentage(section.value),
|
||||
minWidth: '2px', // Ensure very small sections are visible
|
||||
backgroundColor: isHovered
|
||||
? lightenColor(section.color, 30)
|
||||
: section.color,
|
||||
transition: 'background-color 0.3s ease-in-out'
|
||||
}}
|
||||
onMouseEnter={() => setHoveredSectionIndex(index)}
|
||||
onMouseLeave={() => setHoveredSectionIndex(null)}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
{unusedSpace > 0 && (
|
||||
<div
|
||||
className="relative h-full"
|
||||
style={{
|
||||
width: getPercentage(unusedSpace),
|
||||
backgroundColor: isDark ? '#1C1D25' : '#D3D3D3'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{systemSection && (
|
||||
<Tooltip
|
||||
label={`${humanizeSize(systemSection.value).value} ${humanizeSize(systemSection.value).unit}`}
|
||||
position="top"
|
||||
>
|
||||
<div
|
||||
className="relative h-full rounded-r"
|
||||
style={{
|
||||
width: getPercentage(systemSection.value),
|
||||
minWidth: '2px',
|
||||
backgroundColor: systemSection.color,
|
||||
transition: 'background-color 0.3s ease-in-out'
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className={`mt-6 flex flex-wrap ${isDark ? 'text-ink-dull' : 'text-gray-800'}`}>
|
||||
{sections.map((section, index) => (
|
||||
<Tooltip key={index} label={section.tooltip} position="top">
|
||||
<div
|
||||
className="mb-2 mr-6 flex items-center"
|
||||
onMouseEnter={() => setHoveredSectionIndex(index)}
|
||||
onMouseLeave={() => setHoveredSectionIndex(null)}
|
||||
>
|
||||
<span
|
||||
className="mr-2 inline-block size-2 rounded-full"
|
||||
style={{ backgroundColor: section.color }}
|
||||
/>
|
||||
<span className="text-sm">{section.name}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StorageBar;
|
|
@ -76,10 +76,9 @@ export const Component = () => {
|
|||
<div className="mt-4 flex flex-col gap-3 pt-3">
|
||||
<OverviewSection>
|
||||
<LibraryStatistics />
|
||||
</OverviewSection>
|
||||
<OverviewSection>
|
||||
<FileKindStatistics />
|
||||
</OverviewSection>
|
||||
|
||||
<OverviewSection count={1} title={t('devices')}>
|
||||
{node && (
|
||||
<StatisticItem
|
||||
|
@ -93,46 +92,6 @@ export const Component = () => {
|
|||
connectionType={null}
|
||||
/>
|
||||
)}
|
||||
{/* <StatisticItem
|
||||
name="Jamie's MacBook"
|
||||
icon="Laptop"
|
||||
total_space="4098046511104"
|
||||
free_space="969004651119"
|
||||
color="#0362FF"
|
||||
connection_type="p2p"
|
||||
/>
|
||||
<StatisticItem
|
||||
name="Jamie's iPhone"
|
||||
icon="Mobile"
|
||||
total_space="500046511104"
|
||||
free_space="39006511104"
|
||||
color="#0362FF"
|
||||
connection_type="p2p"
|
||||
/>
|
||||
<StatisticItem
|
||||
name="Titan NAS"
|
||||
icon="Server"
|
||||
total_space="60000046511104"
|
||||
free_space="43000046511104"
|
||||
color="#0362FF"
|
||||
connection_type="p2p"
|
||||
/>
|
||||
<StatisticItem
|
||||
name="Jamie's iPad"
|
||||
icon="Tablet"
|
||||
total_space="1074077906944"
|
||||
free_space="121006553275"
|
||||
color="#0362FF"
|
||||
connection_type="lan"
|
||||
/>
|
||||
<StatisticItem
|
||||
name="Jamie's Air"
|
||||
icon="Laptop"
|
||||
total_space="4098046511104"
|
||||
free_space="969004651119"
|
||||
color="#0362FF"
|
||||
connection_type="p2p"
|
||||
/> */}
|
||||
<NewCard
|
||||
icons={['Laptop', 'Server', 'SilverBox', 'Tablet']}
|
||||
text={t('connect_device_description')}
|
||||
|
@ -164,23 +123,6 @@ export const Component = () => {
|
|||
</OverviewSection>
|
||||
|
||||
<OverviewSection count={0} title={t('cloud_drives')}>
|
||||
{/* <StatisticItem
|
||||
name="James Pine"
|
||||
icon="DriveDropbox"
|
||||
total_space="104877906944"
|
||||
free_space="074877906944"
|
||||
color="#0362FF"
|
||||
connection_type="cloud"
|
||||
/>
|
||||
<StatisticItem
|
||||
name="Spacedrive S3"
|
||||
icon="DriveAmazonS3"
|
||||
total_space="1074877906944"
|
||||
free_space="704877906944"
|
||||
color="#0362FF"
|
||||
connection_type="cloud"
|
||||
/> */}
|
||||
|
||||
<NewCard
|
||||
icons={[
|
||||
'DriveAmazonS3',
|
||||
|
@ -193,22 +135,6 @@ export const Component = () => {
|
|||
// buttonText={t('connect_cloud)}
|
||||
/>
|
||||
</OverviewSection>
|
||||
|
||||
{/* <OverviewSection title="Locations">
|
||||
<div className="flex flex-row gap-2">
|
||||
{locations.map((location) => (
|
||||
<div
|
||||
key={location.id}
|
||||
className="flex w-[100px] flex-col items-center gap-2"
|
||||
>
|
||||
<Icon size={80} name="Folder" />
|
||||
<span className="text-xs font-medium truncate">
|
||||
{location.name}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</OverviewSection> */}
|
||||
</div>
|
||||
</div>
|
||||
</SearchContextProvider>
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"backfill_sync_description": "Library is paused until backfill completes",
|
||||
"backups": "Backups",
|
||||
"backups_description": "Manage your Spacedrive database backups.",
|
||||
"bar_graph_info": "Hover over each bar to see the file type. Double click to navigate.",
|
||||
"bitrate": "Bitrate",
|
||||
"blur_effects": "Blur Effects",
|
||||
"blur_effects_description": "Some components will have a blur effect applied to them.",
|
||||
|
@ -711,10 +712,12 @@
|
|||
"total_bytes_free_description": "Free space available on all nodes connected to the library.",
|
||||
"total_bytes_used": "Total used space",
|
||||
"total_bytes_used_description": "Total space used on all nodes connected to the library.",
|
||||
"total_files": "Total files",
|
||||
"trash": "Trash",
|
||||
"type": "Type",
|
||||
"ui_animations": "UI Animations",
|
||||
"ui_animations_description": "Dialogs and other UI elements will animate when opening and closing.",
|
||||
"unidentified_files_info": "Files that Spacedrive has been unable to identify.",
|
||||
"unknown": "Unknown",
|
||||
"unnamed_location": "Unnamed Location",
|
||||
"update": "Update",
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"crypto-random-string": "^5.0.0",
|
||||
"d3-force": "^3.0.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"framer-motion": "^10.16.4",
|
||||
"i18next": "^23.7.10",
|
||||
|
@ -46,6 +47,7 @@
|
|||
"react-colorful": "^5.6.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react-force-graph-2d": "^1.25.5",
|
||||
"react-hook-form": "^7.47.0",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"react-i18next": "^13.5.0",
|
||||
|
@ -71,6 +73,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@sd/config": "workspace:*",
|
||||
"@types/d3-force": "^3.0.9",
|
||||
"@types/node": ">18.18.x",
|
||||
"@types/react": "^18.2.67",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
|
|
|
@ -383,9 +383,9 @@ export type JobProgressEvent = { id: string; library_id: string; task_count: num
|
|||
|
||||
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key in string]: JsonValue }
|
||||
|
||||
export type KindStatistic = { kind: number; name: string; count: number; total_bytes: string }
|
||||
export type KindStatistic = { kind: number; name: string; count: [number, number]; total_bytes: [number, number] }
|
||||
|
||||
export type KindStatistics = { statistics: KindStatistic[] }
|
||||
export type KindStatistics = { statistics: KindStatistic[]; total_identified_files: number; total_unidentified_files: number }
|
||||
|
||||
export type Label = { id: number; name: string; date_created: string | null; date_modified: string | null }
|
||||
|
||||
|
|
462
pnpm-lock.yaml
462
pnpm-lock.yaml
|
@ -147,7 +147,7 @@ importers:
|
|||
version: 2.16.0
|
||||
'@tauri-apps/cli':
|
||||
specifier: next
|
||||
version: 2.0.0-beta.19
|
||||
version: 2.0.0-beta.20
|
||||
'@types/react':
|
||||
specifier: ^18.2.67
|
||||
version: 18.2.67
|
||||
|
@ -760,6 +760,9 @@ importers:
|
|||
crypto-random-string:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
d3-force:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
dayjs:
|
||||
specifier: ^1.11.10
|
||||
version: 1.11.10
|
||||
|
@ -793,6 +796,9 @@ importers:
|
|||
react-error-boundary:
|
||||
specifier: ^4.0.11
|
||||
version: 4.0.13(react@18.2.0)
|
||||
react-force-graph-2d:
|
||||
specifier: ^1.25.5
|
||||
version: 1.25.5(react@18.2.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.47.0
|
||||
version: 7.51.1(react@18.2.0)
|
||||
|
@ -863,6 +869,9 @@ importers:
|
|||
'@sd/config':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/config
|
||||
'@types/d3-force':
|
||||
specifier: ^3.0.9
|
||||
version: 3.0.9
|
||||
'@types/node':
|
||||
specifier: '>18.18.x'
|
||||
version: 20.11.29
|
||||
|
@ -3560,9 +3569,6 @@ packages:
|
|||
'@radix-ui/number@1.0.1':
|
||||
resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==}
|
||||
|
||||
'@radix-ui/primitive@1.0.0':
|
||||
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
|
||||
|
||||
'@radix-ui/primitive@1.0.1':
|
||||
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
|
||||
|
||||
|
@ -3605,11 +3611,6 @@ packages:
|
|||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-compose-refs@1.0.0':
|
||||
resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
|
||||
'@radix-ui/react-compose-refs@1.0.1':
|
||||
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
|
||||
peerDependencies:
|
||||
|
@ -3663,12 +3664,6 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.0.2':
|
||||
resolution: {integrity: sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.0.4':
|
||||
resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==}
|
||||
peerDependencies:
|
||||
|
@ -3843,12 +3838,6 @@ packages:
|
|||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-primitive@1.0.1':
|
||||
resolution: {integrity: sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
|
||||
'@radix-ui/react-primitive@1.0.3':
|
||||
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
|
||||
peerDependencies:
|
||||
|
@ -3927,11 +3916,6 @@ packages:
|
|||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slot@1.0.1':
|
||||
resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
|
||||
'@radix-ui/react-slot@1.0.2':
|
||||
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
|
||||
peerDependencies:
|
||||
|
@ -3993,11 +3977,6 @@ packages:
|
|||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.0.0':
|
||||
resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.0.1':
|
||||
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
|
||||
peerDependencies:
|
||||
|
@ -4016,11 +3995,6 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.0.2':
|
||||
resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.0.3':
|
||||
resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==}
|
||||
peerDependencies:
|
||||
|
@ -4849,6 +4823,7 @@ packages:
|
|||
|
||||
'@storybook/testing-library@0.2.2':
|
||||
resolution: {integrity: sha512-L8sXFJUHmrlyU2BsWWZGuAjv39Jl1uAqUHdxmN42JY15M4+XCMjGlArdCCjDe1wpTSW6USYISA9axjZojgtvnw==}
|
||||
deprecated: In Storybook 8, this package functionality has been integrated to a new package called @storybook/test, which uses Vitest APIs for an improved experience. When upgrading to Storybook 8 with 'npx storybook@latest upgrade', you will get prompted and will get an automigration for the new package. Please migrate when you can.
|
||||
|
||||
'@storybook/theming@8.0.1':
|
||||
resolution: {integrity: sha512-TUmSHRh3YrpJ25DYjD+9PpJaq9Qf9P1S2xpwfNARM9r2KpkMF1/RgqnnQgZpP9od0Tzvkji7XPzxPU//EmQKEA==}
|
||||
|
@ -5118,68 +5093,68 @@ packages:
|
|||
resolution: {integrity: sha512-Np1opKANzRMF3lgJ9gDquBCB9SxlE2lRmNpVx1+L6RyzAmigkuh0ZulT5jMnDA3JLsuSDU135r/s4t/Pmx4atg==}
|
||||
engines: {node: '>= 18', npm: '>= 6.6.0', yarn: '>= 1.19.1'}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-c4KvyBnQ5C/P3oAyO7WZ71xYxW8yMwDe3I4Ik3Uz6+AXZ2k3xPx19VuxCgTJdJCkxtLvhAGu9Q2IZQuuDoGTsg==}
|
||||
'@tauri-apps/cli-darwin-arm64@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-oCJOCib7GuYkwkBXx+ekamR8NZZU+2i3MLP+DHpDxK5gS2uhCE+CBkamJkNt6y1x6xdVnwyqZOm5RvN4SRtyIA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@tauri-apps/cli-darwin-x64@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-t7rzloJwzgNXm82/w97Tq3RcvX7XmRcaxnu8ujV5SrREFxzLNRpkyzzr/vVthV7FZjKGcQf5QmJ3XeGXUfkCfQ==}
|
||||
'@tauri-apps/cli-darwin-x64@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-lC5QSnRExedYN4Ds6ZlSvC2PxP8qfIYBJQ5ktf+PJI5gQALdNeVtd6YnTG1ODCEklfLq9WKkGwp7JdALTU5wDA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-ZnM596ltSUNeBKH9rMGm1Ch1lCaeb1rW79nP1E6REuu1iOBpVAdkporaMWE7JSpkBZmSZdSuVDRhrMDuG7Uc6A==}
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-nZCeBMHHye5DLOJV5k2w658hnCS+LYaOZ8y/G9l3ei+g0L/HBjlSy6r4simsAT5TG8+l3oCZzLBngfTMdDS/YA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-xHAFx+6EqEKLQMrqQPwnzhygA2b/nn0b7pLF48YBvkDj3KLOmv5cC+K34f2l0KIaLB8B/oVFAQKsfet4XLew+w==}
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-B79ISVLPVBgwnCchVqwTKU+vxnFYqxKomcR4rmsvxfs0NVtT5QuNzE1k4NUQnw3966yjwhYR3mnHsSJQSB4Eyw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-ySRYhIfNDt/VXCycVt7d/dMBXf7L9iWf0SwynZ2nvJU/MaHIfJUgV68/l3RTRooYOCkYN4v/RRcGFD3wRmtE5g==}
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-ojIkv/1uZHhcrgfIN8xgn4BBeo/Xg+bnV0wer6lD78zyxkUMWeEZ+u3mae1ejCJNhhaZOxNaUQ67MvDOiGyr5Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-GEySXBulHQfGr3xuuv2ShnUrQtrWn3ynUtftoMiJNlpa1RTLfzglbUdA7zXag65E8h2jATVYnC/n8/sE5jtSHw==}
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-xBy1FNbHKlc7T6pOmFQQPECxJaI5A9QWX7Kb9N64cNVusoOGlvc3xHYkXMS4PTr7xXOT0yiE1Ww2OwDRJ3lYsg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@tauri-apps/cli-linux-x64-musl@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-gz1x/7EhpMcIhUvR7RhG3D+dwUnXF+MIxPoiuDAKzQAj3i6qacZJvwxyRcpVQ6HaUDpmtaHz0AKpWIMmIFL90g==}
|
||||
'@tauri-apps/cli-linux-x64-musl@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-+O6zq5jmtUxA1FUAAwF2ywPysy4NRo2Y6G+ESZDkY9XosRwdt5OUjqAsYktZA3AxDMZVei8r9buwTqUwi9ny/g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-Zz/UwU+7QQbz9lu9cpLzX/fCgmBG1lX+K5O97kTJVcqgBiS0zUc5q1efYr7ex4c6NLVP7uaUK3IKwctBy2MvEA==}
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-RswgMbWyOQcv53CHvIuiuhAh4kKDqaGyZfWD4VlxqX/XhkoF5gsNgr0MxzrY7pmoL+89oVI+fiGVJz4nOQE5vA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-fdT/u8I31PryeqULgzzUV+bYAlgt9WStJaZWt1/hMDffB9VViL3gO7V67mtNUEhBUMaX/SqItwklbJyy3TKXXg==}
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-5lgWmDVXhX3SBGbiv5SduM1yajiRnUEJClWhSdRrEEJeXdsxpCsBEhxYnUnDCEzPKxLLn5fdBv3VrVctJ03csQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-EHTi4D95mTmPC/MqWU5mBGhwZ0i82iVKEAAGaKDNdwYzibmioeANCzsD8eeyuU0kCE5BCWBYpA+2epGQnfDjMg==}
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-SuSiiVQTQPSzWlsxQp/NMzWbzDS9TdVDOw7CCfgiG5wnT2GsxzrcIAVN6i7ILsVFLxrjr0bIgPldSJcdcH84Yw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@tauri-apps/cli@2.0.0-beta.19':
|
||||
resolution: {integrity: sha512-IHbgyUpnXY5ZEenQUz2Gce7w1Xl1BgLR6Jyf6SN0VbUVr9qJdSRPN7/FK+4JQFt2DC9076NVYTQFLOt03KNbwA==}
|
||||
'@tauri-apps/cli@2.0.0-beta.20':
|
||||
resolution: {integrity: sha512-707q9uIc2oNrYHd2dtMvxTrpZXVpart5EIktnRymNOpphkLlB6WUBjHD+ga45WqTU6cNGKbYvkKqTNfshNul9Q==}
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
|
@ -5403,6 +5378,9 @@ packages:
|
|||
'@types/cross-spawn@6.0.6':
|
||||
resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==}
|
||||
|
||||
'@types/d3-force@3.0.9':
|
||||
resolution: {integrity: sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
|
@ -5866,6 +5844,10 @@ packages:
|
|||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
accessor-fn@1.5.0:
|
||||
resolution: {integrity: sha512-dml7D96DY/K5lt4Ra2jMnpL9Bhw5HEGws4p1OAIxFFj9Utd/RxNfEO3T3f0QIWFNwQU7gNxH9snUfqF/zNkP/w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
acorn-import-assertions@1.9.0:
|
||||
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
|
||||
peerDependencies:
|
||||
|
@ -6023,6 +6005,7 @@ packages:
|
|||
|
||||
are-we-there-yet@1.1.7:
|
||||
resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
|
@ -6275,6 +6258,9 @@ packages:
|
|||
resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
bezier-js@6.1.4:
|
||||
resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==}
|
||||
|
||||
bidi-js@1.0.3:
|
||||
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||
|
||||
|
@ -6499,6 +6485,10 @@ packages:
|
|||
caniuse-lite@1.0.30001599:
|
||||
resolution: {integrity: sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==}
|
||||
|
||||
canvas-color-tracker@1.2.1:
|
||||
resolution: {integrity: sha512-i5clg2pEdaWqHuEM/B74NZNLkHh5+OkXbA/T4iaBiaNDagkOCXkLNrhqUfdUugsRwuaNRU20e/OygzxWRor3yg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
caseless@0.12.0:
|
||||
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
|
||||
|
||||
|
@ -7019,6 +7009,86 @@ packages:
|
|||
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
|
||||
d3-array@3.2.4:
|
||||
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-binarytree@1.0.2:
|
||||
resolution: {integrity: sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==}
|
||||
|
||||
d3-color@3.1.0:
|
||||
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-dispatch@3.0.1:
|
||||
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-drag@3.0.0:
|
||||
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-ease@3.0.1:
|
||||
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-force-3d@3.0.5:
|
||||
resolution: {integrity: sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-force@3.0.0:
|
||||
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-format@3.1.0:
|
||||
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-octree@1.0.2:
|
||||
resolution: {integrity: sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==}
|
||||
|
||||
d3-quadtree@3.0.1:
|
||||
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-scale-chromatic@3.1.0:
|
||||
resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-scale@4.0.2:
|
||||
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-selection@3.0.0:
|
||||
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-time-format@4.1.0:
|
||||
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-time@3.1.0:
|
||||
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-timer@3.0.1:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-transition@3.0.1:
|
||||
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
d3-selection: 2 - 3
|
||||
|
||||
d3-zoom@3.0.0:
|
||||
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
dag-map@1.0.2:
|
||||
resolution: {integrity: sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==}
|
||||
|
||||
|
@ -8100,6 +8170,10 @@ packages:
|
|||
for-each@0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
|
||||
force-graph@1.43.5:
|
||||
resolution: {integrity: sha512-HveLELh9yhZXO/QOfaFS38vlwJZ/3sKu+jarfXzRmbmihSOH/BbRWnUvmg8wLFiYy6h4HlH4lkRfZRccHYmXgA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
foreach@2.0.6:
|
||||
resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==}
|
||||
|
||||
|
@ -8171,6 +8245,9 @@ packages:
|
|||
from@0.1.7:
|
||||
resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
|
||||
|
||||
fromentries@1.3.2:
|
||||
resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==}
|
||||
|
||||
fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
|
||||
|
@ -8233,6 +8310,7 @@ packages:
|
|||
|
||||
gauge@2.7.4:
|
||||
resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
gensequence@7.0.0:
|
||||
resolution: {integrity: sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==}
|
||||
|
@ -8728,6 +8806,10 @@ packages:
|
|||
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
index-array-by@1.4.1:
|
||||
resolution: {integrity: sha512-Zu6THdrxQdyTuT2uA5FjUoBEsFHPzHcPIj18FszN6yXKHxSfGcR4TPLabfuT//E25q1Igyx9xta2WMvD/x9P/g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
indexof@0.0.1:
|
||||
resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==}
|
||||
|
||||
|
@ -8740,6 +8822,7 @@ packages:
|
|||
|
||||
inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
@ -8769,6 +8852,10 @@ packages:
|
|||
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
internmap@2.0.3:
|
||||
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
interpret@1.4.0:
|
||||
resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
@ -9124,6 +9211,10 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
jerrypick@1.1.1:
|
||||
resolution: {integrity: sha512-XTtedPYEyVp4t6hJrXuRKr/jHj8SC4z+4K0b396PMkov6muL+i8IIamJIvZWe3jUspgIJak0P+BaWKawMYNBLg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
jest-environment-node@29.7.0:
|
||||
resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
@ -9274,6 +9365,10 @@ packages:
|
|||
jwt-decode@3.1.2:
|
||||
resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==}
|
||||
|
||||
kapsule@1.14.5:
|
||||
resolution: {integrity: sha512-H0iSpTynUzZw3tgraDmReprpFRmH5oP5GPmaNsurSwLx2H5iCpOMIkp5q+sfhB4Tz/UJd1E1IbEE9Z6ksnJ6RA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
katex@0.16.9:
|
||||
resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
|
||||
hasBin: true
|
||||
|
@ -9462,6 +9557,9 @@ packages:
|
|||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
lodash-es@4.17.21:
|
||||
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||
|
||||
lodash.camelcase@4.3.0:
|
||||
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
||||
|
||||
|
@ -10353,6 +10451,7 @@ packages:
|
|||
|
||||
npmlog@4.1.2:
|
||||
resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
@ -10508,6 +10607,7 @@ packages:
|
|||
|
||||
osenv@0.1.5:
|
||||
resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
ospath@1.2.2:
|
||||
resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
|
||||
|
@ -11145,6 +11245,12 @@ packages:
|
|||
peerDependencies:
|
||||
react: '>=16.13.1'
|
||||
|
||||
react-force-graph-2d@1.25.5:
|
||||
resolution: {integrity: sha512-3u8WjZZorpwZSDs3n3QeOS9ZoxFPM+IR9SStYJVQ/qKECydMHarxnf7ynV/MKJbC6kUsc60soD0V+Uq/r2vz7Q==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
|
||||
react-freeze@1.0.4:
|
||||
resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -11208,6 +11314,12 @@ packages:
|
|||
react: ^17.0.0 || ^16.3.0 || ^15.5.4
|
||||
react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4
|
||||
|
||||
react-kapsule@2.4.0:
|
||||
resolution: {integrity: sha512-w4Yv9CgWdj8kWGQEPNWFGJJ08dYEZHZpiaFR/DgZjCMBNqv9wus2Gy1qvHVJmJbzvAZbq6jdvFC+NYzEqAlNhQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '>=16.13.1'
|
||||
|
||||
react-lifecycles-compat@3.0.4:
|
||||
resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
|
||||
|
||||
|
@ -11715,18 +11827,22 @@ packages:
|
|||
|
||||
rimraf@2.4.5:
|
||||
resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
rimraf@2.6.3:
|
||||
resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
rimraf@2.7.1:
|
||||
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
rimraf@3.0.2:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
ripemd160@2.0.2:
|
||||
|
@ -12475,6 +12591,9 @@ packages:
|
|||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
tinycolor2@1.6.0:
|
||||
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
|
||||
|
||||
tinyspy@2.2.1:
|
||||
resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
@ -16333,10 +16452,6 @@ snapshots:
|
|||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
||||
'@radix-ui/primitive@1.0.0':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
||||
'@radix-ui/primitive@1.0.1':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -16381,11 +16496,6 @@ snapshots:
|
|||
'@types/react': 18.2.67
|
||||
'@types/react-dom': 18.2.22
|
||||
|
||||
'@radix-ui/react-compose-refs@1.0.0(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
react: 18.2.0
|
||||
|
||||
'@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.67)(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -16445,17 +16555,6 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@types/react': 18.2.67
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.0.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@radix-ui/primitive': 1.0.0
|
||||
'@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0)
|
||||
'@radix-ui/react-use-escape-keydown': 1.0.2(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.67)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -16547,7 +16646,7 @@ snapshots:
|
|||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.67)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.67)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.67)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.67)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.67)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.67)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.67)(react@18.2.0)
|
||||
|
@ -16659,13 +16758,6 @@ snapshots:
|
|||
'@types/react': 18.2.67
|
||||
'@types/react-dom': 18.2.22
|
||||
|
||||
'@radix-ui/react-primitive@1.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@radix-ui/react-slot': 1.0.1(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
'@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.67)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -16774,12 +16866,6 @@ snapshots:
|
|||
'@types/react': 18.2.67
|
||||
'@types/react-dom': 18.2.22
|
||||
|
||||
'@radix-ui/react-slot@1.0.1(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
|
||||
react: 18.2.0
|
||||
|
||||
'@radix-ui/react-slot@1.0.2(@types/react@18.2.67)(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -16863,11 +16949,6 @@ snapshots:
|
|||
'@types/react': 18.2.67
|
||||
'@types/react-dom': 18.2.22
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
react: 18.2.0
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.67)(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -16883,12 +16964,6 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@types/react': 18.2.67
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.0.2(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0)
|
||||
react: 18.2.0
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.67)(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
|
@ -18783,48 +18858,48 @@ snapshots:
|
|||
|
||||
'@tauri-apps/api@2.0.0-beta.13': {}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-darwin-arm64@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-darwin-x64@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-darwin-x64@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-linux-arm64-gnu@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-linux-x64-musl@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-linux-x64-musl@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-win32-ia32-msvc@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.0.0-beta.19':
|
||||
'@tauri-apps/cli-win32-x64-msvc@2.0.0-beta.20':
|
||||
optional: true
|
||||
|
||||
'@tauri-apps/cli@2.0.0-beta.19':
|
||||
'@tauri-apps/cli@2.0.0-beta.20':
|
||||
optionalDependencies:
|
||||
'@tauri-apps/cli-darwin-arm64': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-darwin-x64': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-linux-arm64-gnu': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-linux-arm64-musl': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-linux-x64-gnu': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-linux-x64-musl': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-win32-arm64-msvc': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.0.0-beta.19
|
||||
'@tauri-apps/cli-darwin-arm64': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-darwin-x64': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-linux-arm-gnueabihf': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-linux-arm64-gnu': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-linux-arm64-musl': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-linux-x64-gnu': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-linux-x64-musl': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-win32-arm64-msvc': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.0.0-beta.20
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.0.0-beta.3':
|
||||
dependencies:
|
||||
|
@ -19125,6 +19200,8 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 20.11.29
|
||||
|
||||
'@types/d3-force@3.0.9': {}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
'@types/ms': 0.7.34
|
||||
|
@ -19679,6 +19756,8 @@ snapshots:
|
|||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
|
||||
accessor-fn@1.5.0: {}
|
||||
|
||||
acorn-import-assertions@1.9.0(acorn@8.11.3):
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
|
@ -20181,6 +20260,8 @@ snapshots:
|
|||
dependencies:
|
||||
open: 8.4.2
|
||||
|
||||
bezier-js@6.1.4: {}
|
||||
|
||||
bidi-js@1.0.3:
|
||||
dependencies:
|
||||
require-from-string: 2.0.2
|
||||
|
@ -20452,6 +20533,10 @@ snapshots:
|
|||
|
||||
caniuse-lite@1.0.30001599: {}
|
||||
|
||||
canvas-color-tracker@1.2.1:
|
||||
dependencies:
|
||||
tinycolor2: 1.6.0
|
||||
|
||||
caseless@0.12.0: {}
|
||||
|
||||
ccount@2.0.1: {}
|
||||
|
@ -21110,6 +21195,89 @@ snapshots:
|
|||
untildify: 4.0.0
|
||||
yauzl: 2.10.0
|
||||
|
||||
d3-array@3.2.4:
|
||||
dependencies:
|
||||
internmap: 2.0.3
|
||||
|
||||
d3-binarytree@1.0.2: {}
|
||||
|
||||
d3-color@3.1.0: {}
|
||||
|
||||
d3-dispatch@3.0.1: {}
|
||||
|
||||
d3-drag@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
|
||||
d3-ease@3.0.1: {}
|
||||
|
||||
d3-force-3d@3.0.5:
|
||||
dependencies:
|
||||
d3-binarytree: 1.0.2
|
||||
d3-dispatch: 3.0.1
|
||||
d3-octree: 1.0.2
|
||||
d3-quadtree: 3.0.1
|
||||
d3-timer: 3.0.1
|
||||
|
||||
d3-force@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-quadtree: 3.0.1
|
||||
d3-timer: 3.0.1
|
||||
|
||||
d3-format@3.1.0: {}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
|
||||
d3-octree@1.0.2: {}
|
||||
|
||||
d3-quadtree@3.0.1: {}
|
||||
|
||||
d3-scale-chromatic@3.1.0:
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-interpolate: 3.0.1
|
||||
|
||||
d3-scale@4.0.2:
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
d3-format: 3.1.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-time: 3.1.0
|
||||
d3-time-format: 4.1.0
|
||||
|
||||
d3-selection@3.0.0: {}
|
||||
|
||||
d3-time-format@4.1.0:
|
||||
dependencies:
|
||||
d3-time: 3.1.0
|
||||
|
||||
d3-time@3.1.0:
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
|
||||
d3-timer@3.0.1: {}
|
||||
|
||||
d3-transition@3.0.1(d3-selection@3.0.0):
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-dispatch: 3.0.1
|
||||
d3-ease: 3.0.1
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-timer: 3.0.1
|
||||
|
||||
d3-zoom@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
|
||||
dag-map@1.0.2: {}
|
||||
|
||||
damerau-levenshtein@1.0.8: {}
|
||||
|
@ -22637,6 +22805,23 @@ snapshots:
|
|||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
|
||||
force-graph@1.43.5:
|
||||
dependencies:
|
||||
'@tweenjs/tween.js': 23.1.1
|
||||
accessor-fn: 1.5.0
|
||||
bezier-js: 6.1.4
|
||||
canvas-color-tracker: 1.2.1
|
||||
d3-array: 3.2.4
|
||||
d3-drag: 3.0.0
|
||||
d3-force-3d: 3.0.5
|
||||
d3-scale: 4.0.2
|
||||
d3-scale-chromatic: 3.1.0
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
index-array-by: 1.4.1
|
||||
kapsule: 1.14.5
|
||||
lodash-es: 4.17.21
|
||||
|
||||
foreach@2.0.6: {}
|
||||
|
||||
foreground-child@3.1.1:
|
||||
|
@ -22707,6 +22892,8 @@ snapshots:
|
|||
|
||||
from@0.1.7: {}
|
||||
|
||||
fromentries@1.3.2: {}
|
||||
|
||||
fs-constants@1.0.0: {}
|
||||
|
||||
fs-extra@10.1.0:
|
||||
|
@ -23405,6 +23592,8 @@ snapshots:
|
|||
|
||||
indent-string@4.0.0: {}
|
||||
|
||||
index-array-by@1.4.1: {}
|
||||
|
||||
indexof@0.0.1: {}
|
||||
|
||||
infer-owner@1.0.4: {}
|
||||
|
@ -23439,6 +23628,8 @@ snapshots:
|
|||
hasown: 2.0.2
|
||||
side-channel: 1.0.6
|
||||
|
||||
internmap@2.0.3: {}
|
||||
|
||||
interpret@1.4.0: {}
|
||||
|
||||
invariant@2.2.4:
|
||||
|
@ -23725,6 +23916,8 @@ snapshots:
|
|||
filelist: 1.0.4
|
||||
minimatch: 3.1.2
|
||||
|
||||
jerrypick@1.1.1: {}
|
||||
|
||||
jest-environment-node@29.7.0:
|
||||
dependencies:
|
||||
'@jest/environment': 29.7.0
|
||||
|
@ -23935,6 +24128,10 @@ snapshots:
|
|||
|
||||
jwt-decode@3.1.2: {}
|
||||
|
||||
kapsule@1.14.5:
|
||||
dependencies:
|
||||
lodash-es: 4.17.21
|
||||
|
||||
katex@0.16.9:
|
||||
dependencies:
|
||||
commander: 8.3.0
|
||||
|
@ -24138,6 +24335,8 @@ snapshots:
|
|||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
|
||||
lodash-es@4.17.21: {}
|
||||
|
||||
lodash.camelcase@4.3.0: {}
|
||||
|
||||
lodash.castarray@4.4.0: {}
|
||||
|
@ -26358,6 +26557,13 @@ snapshots:
|
|||
'@babel/runtime': 7.24.0
|
||||
react: 18.2.0
|
||||
|
||||
react-force-graph-2d@1.25.5(react@18.2.0):
|
||||
dependencies:
|
||||
force-graph: 1.43.5
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
react-kapsule: 2.4.0(react@18.2.0)
|
||||
|
||||
react-freeze@1.0.4(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
|
@ -26412,6 +26618,12 @@ snapshots:
|
|||
- '@types/react'
|
||||
- encoding
|
||||
|
||||
react-kapsule@2.4.0(react@18.2.0):
|
||||
dependencies:
|
||||
fromentries: 1.3.2
|
||||
jerrypick: 1.1.1
|
||||
react: 18.2.0
|
||||
|
||||
react-lifecycles-compat@3.0.4: {}
|
||||
|
||||
react-loading-icons@1.1.0: {}
|
||||
|
@ -28120,6 +28332,8 @@ snapshots:
|
|||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinycolor2@1.6.0: {}
|
||||
|
||||
tinyspy@2.2.1: {}
|
||||
|
||||
tmp@0.0.33:
|
||||
|
|
Loading…
Reference in a new issue