diff --git a/src/app/file-tree/file-tree.component.html b/src/app/file-tree/file-tree.component.html
index de5fcd3..1062e22 100644
--- a/src/app/file-tree/file-tree.component.html
+++ b/src/app/file-tree/file-tree.component.html
@@ -1,11 +1,19 @@
- @if(workspaceName()) { {{ workspaceName() }} } @else { No Worspace Selected}
+ @if(workspaceName()) { {{ workspaceName() }} } @else if(!isFileSelected()) {
+ No Worspace Selected}
-@if(!selectedDirectory()) {
+@if(!isFileSelected()) {
+
}@else {
diff --git a/src/app/file-tree/file-tree.component.scss b/src/app/file-tree/file-tree.component.scss
index 33f31b9..25abf41 100644
--- a/src/app/file-tree/file-tree.component.scss
+++ b/src/app/file-tree/file-tree.component.scss
@@ -22,3 +22,7 @@ h2 {
overflow: hidden;
white-space: nowrap;
}
+
+input {
+ display: none;
+}
diff --git a/src/app/file-tree/file-tree.component.ts b/src/app/file-tree/file-tree.component.ts
index a821863..5207318 100644
--- a/src/app/file-tree/file-tree.component.ts
+++ b/src/app/file-tree/file-tree.component.ts
@@ -2,11 +2,13 @@ import { FlatTreeControl } from '@angular/cdk/tree';
import { CommonModule } from '@angular/common';
import {
Component,
+ ElementRef,
OnDestroy,
OnInit,
computed,
output,
signal,
+ viewChild,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
@@ -24,7 +26,7 @@ export interface FileOrFolder {
isDirectory: boolean;
name: string;
children?: FileOrFolder[];
- path: string;
+ path?: string;
}
interface FileNode {
@@ -43,6 +45,9 @@ interface FileNode {
export class FileTreeComponent implements OnInit, OnDestroy {
fileSelected = output();
+ protected fileSelector =
+ viewChild>('fileSelector');
+
// File tree
protected hasChild = (_: number, node: FileNode) => node.expandable;
@@ -77,6 +82,7 @@ export class FileTreeComponent implements OnInit, OnDestroy {
// Folder selection
protected selectedDirectory = signal(null);
protected files = signal([]);
+ protected isFileSelected = signal(false);
protected workspaceName = computed(() => {
const directory = this.selectedDirectory();
if (directory) {
@@ -104,6 +110,15 @@ export class FileTreeComponent implements OnInit, OnDestroy {
}
async selectDirectory() {
+ try {
+ await this.selectDirectoryNative();
+ } catch (err) {
+ // Tauri failed, try using browser to select files
+ this.fileSelector()?.nativeElement.click();
+ }
+ }
+
+ private async selectDirectoryNative() {
const selectedDirectory = await open({
directory: true,
multiple: false,
@@ -118,26 +133,69 @@ export class FileTreeComponent implements OnInit, OnDestroy {
const entries = await readDir(selectedDirectory, {
recursive: true,
});
- const splitNumbers = /(\d)+|(\D)+/;
this.files.set(
- entries
- .sort(this.sortFiles(splitNumbers))
- .map((entry) => this.mapEntry(entry, splitNumbers))
+ entries.sort(this.sortFiles).map((entry) => this.mapEntry(entry))
);
+ this.isFileSelected.set(true);
}
}
- private mapEntry(entry: FileEntry, splitNumbers: RegExp): FileOrFolder {
+ protected selectFilesBrowser() {
+ const files = this.fileSelector()?.nativeElement.files;
+ if (files && files.length > 0) {
+ const mappedFiles: FileOrFolder[] = [];
+ for (let i = 0; i < files.length; i++) {
+ const file = files[i];
+ if (file.webkitRelativePath?.includes('/')) {
+ // Got a file in a folder, so put it into the appropriate folder in the tree
+ const splitFilePath = file.webkitRelativePath.split('/');
+ let currentChildren: FileOrFolder[] | undefined = mappedFiles;
+ for (let j = 0; j < splitFilePath.length - 1; j++) {
+ const relativePath = splitFilePath[j];
+ let matchingChild: FileOrFolder | undefined = currentChildren?.find(
+ (mappedFile) => mappedFile.name === relativePath
+ );
+ if (!matchingChild) {
+ matchingChild = {
+ isDirectory: true,
+ name: relativePath,
+ children: [],
+ };
+ currentChildren?.push(matchingChild);
+ }
+ currentChildren = matchingChild.children;
+ }
+ currentChildren?.push({ isDirectory: false, name: file.name });
+ } else {
+ mappedFiles.push({ isDirectory: false, name: file.name });
+ }
+ }
+ this.recursiveSort(mappedFiles);
+ this.files.set(mappedFiles);
+ this.isFileSelected.set(true);
+ }
+ }
+
+ private mapEntry(entry: FileEntry): FileOrFolder {
return {
isDirectory: entry.children != null,
name: entry.name || '',
children: entry.children
- ?.sort(this.sortFiles(splitNumbers))
- .map((entry) => this.mapEntry(entry, splitNumbers)),
+ ?.sort(this.sortFiles)
+ .map((entry) => this.mapEntry(entry)),
path: entry.path,
};
}
- private sortFiles = (splitNumbers: RegExp) => (a: FileEntry, b: FileEntry) =>
- a.name?.localeCompare(b.name ?? '') ?? 0;
+ private sortFiles = (a: { name?: string }, b: { name?: string }) =>
+ new Intl.Collator(undefined, { numeric: true }).compare(a.name!, b.name!);
+
+ private recursiveSort(files: FileOrFolder[]) {
+ for (const file of files) {
+ if (file.children) {
+ this.recursiveSort(file.children);
+ }
+ }
+ files.sort(this.sortFiles);
+ }
}