mindmime's picture
Upload folder using huggingface_hub
a03b3ba verified
import { writable, type Readable } from "svelte/store";
import { dequal } from "dequal";
export interface Node {
type: "file" | "folder";
path: string;
children?: Node[];
checked: boolean;
children_visible: boolean;
last?: Node | null;
parent: Node | null;
previous?: Node | null;
}
export type SerialisedNode = Omit<
Node,
"checked" | "children_visible" | "children"
> & { children?: SerialisedNode[] };
interface FSStore {
subscribe: Readable<Node[] | null>["subscribe"];
create_fs_graph: (serialised_node: SerialisedNode[]) => void;
set_checked: (
indices: number[],
checked: boolean,
checked_paths: string[][],
file_count: "single" | "multiple"
) => string[][];
set_checked_from_paths: (checked_paths: string[][]) => string[][];
}
export const make_fs_store = (): FSStore => {
const { subscribe, set } = writable<Node[] | null>(null);
let root: Node = {
type: "folder",
path: "",
checked: false,
children_visible: false,
parent: null
};
let tree_updated = false;
function create_fs_graph(serialised_node: SerialisedNode[]): void {
root.children = process_tree(serialised_node);
tree_updated = true;
set(root.children);
}
let old_checked_paths: string[][] = [];
function set_checked_from_paths(checked_paths: string[][]): string[][] {
if (dequal(checked_paths, old_checked_paths) && !tree_updated) {
return checked_paths;
}
old_checked_paths = checked_paths;
check_node_and_children(root.children, false, []);
const new_checked_paths: string[][] = [];
const seen_nodes = new Set();
for (let i = 0; i < checked_paths.length; i++) {
let _node = root;
let _path = [];
for (let j = 0; j < checked_paths[i].length; j++) {
if (!_node?.children) {
continue;
}
_path.push(checked_paths[i][j]);
_node = _node.children!.find((v) => v.path === checked_paths[i][j])!;
}
if (!_node) {
continue;
}
_node.checked = true;
ensure_visible(_node);
const nodes = check_node_and_children(_node.children, true, [_node]);
check_parent(_node);
nodes.forEach((node) => {
const path = get_full_path(node);
const normalized_path = path.join("/");
// let normalized_path = path.join("/");
// normalized_path = normalized_path.endsWith("/") ? normalized_path.slice(0, -1) : normalized_path;
if (node.type === "file") {
if (seen_nodes.has(normalized_path)) {
return;
}
new_checked_paths.push(path);
seen_nodes.add(normalized_path);
}
});
}
set(root.children!);
tree_updated = false;
return new_checked_paths;
}
function set_checked(
indices: number[],
checked: boolean,
checked_paths: string[][],
file_count: "single" | "multiple"
): string[][] {
let _node = root;
if (file_count === "single") {
check_node_and_children(root.children, false, []);
set(root.children!);
}
for (let i = 0; i < indices.length; i++) {
_node = _node.children![indices[i]];
}
_node.checked = checked;
const nodes = check_node_and_children(_node.children, checked, [_node]);
let new_checked_paths = new Map(checked_paths.map((v) => [v.join("/"), v]));
for (let i = 0; i < nodes.length; i++) {
const _path = get_full_path(nodes[i]);
if (!nodes[i].checked) {
new_checked_paths.delete(_path.join("/"));
} else if (nodes[i].checked) {
if (file_count === "single") {
new_checked_paths = new Map();
}
new_checked_paths.set(_path.join("/"), _path);
}
}
check_parent(_node);
set(root.children!);
old_checked_paths = Array.from(new_checked_paths).map((v) => v[1]);
return old_checked_paths;
}
return {
subscribe,
create_fs_graph,
set_checked,
set_checked_from_paths
};
};
function ensure_visible(node: Node): void {
if (node.parent) {
node.parent.children_visible = true;
ensure_visible(node.parent);
}
}
function process_tree(
node: SerialisedNode[],
depth = 0,
path_segments: string[] = [],
parent: Node | null = null
): Node[] {
const folders: Node[] = [];
const files: Node[] = [];
for (let i = 0; i < node.length; i++) {
let n: (typeof node)[number] = node[i];
if (n.type === "file") {
let index = files.findIndex(
(v) => v.path.toLocaleLowerCase() >= n.path.toLocaleLowerCase()
);
const _node: Node = {
children: undefined,
type: "file",
path: n.path,
checked: false,
children_visible: false,
parent: parent
};
files.splice(index === -1 ? files.length : index, 0, _node);
} else {
let index = folders.findIndex(
(v) => v.path.toLocaleLowerCase() >= n.path.toLocaleLowerCase()
);
const _node: Node = {
type: "folder",
path: n.path,
checked: false,
children_visible: false,
parent: parent
};
const children = process_tree(
n.children!,
depth + 1,
[...path_segments, n.path],
_node
);
_node.children = children;
folders.splice(index === -1 ? folders.length : index, 0, _node);
}
}
const last = files[files.length - 1] || folders[folders.length - 1];
for (let i = 0; i < folders.length; i++) {
folders[i].last = last;
folders[i].previous = folders[i - 1] || null;
}
for (let i = 0; i < files.length; i++) {
if (i === 0) {
files[i].previous = folders[folders.length - 1] || null;
} else {
files[i].previous = files[i - 1] || null;
}
files[i].last = last;
}
return Array().concat(folders, files);
}
function get_full_path(node: Node, path: string[] = []): string[] {
const new_path = [node.path, ...path];
if (node.parent) {
return get_full_path(node.parent, new_path);
}
return new_path;
}
function check_node_and_children(
node: Node[] | null | undefined,
checked: boolean,
checked_nodes: Node[]
): Node[] {
if (node === null || node === undefined) return checked_nodes;
for (let i = 0; i < node.length; i++) {
node[i].checked = checked;
checked_nodes.push(node[i]);
if (checked) ensure_visible(node[i]);
checked_nodes.concat(
check_node_and_children(node[i].children, checked, checked_nodes)
);
}
return checked_nodes;
}
function check_parent(node: Node | null | undefined): void {
if (node === null || node === undefined || !node.parent) return;
let _node = node.last;
let nodes_checked = [];
while (_node) {
nodes_checked.push(_node.checked);
_node = _node.previous;
}
if (nodes_checked.every((v) => v === true)) {
node.parent!.checked = true;
check_parent(node?.parent);
} else if (nodes_checked.some((v) => v === false)) {
node.parent!.checked = false;
check_parent(node?.parent);
}
}