first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
export type SpaceUnit = 'KB' | 'MB' | 'GB' | 'TB' | 'PB';
export function convertToBytes(value: number, unit: SpaceUnit): number {
if (value == null) return 0;
switch (unit) {
case 'KB':
return value * 1024;
case 'MB':
return value * 1024 ** 2;
case 'GB':
return value * 1024 ** 3;
case 'TB':
return value * 1024 ** 4;
case 'PB':
return value * 1024 ** 5;
default:
return value;
}
}

View File

@@ -0,0 +1,49 @@
import {UploadInputConfig} from '../types/upload-input-config';
export function createUploadInput(
config: UploadInputConfig = {}
): HTMLInputElement {
const old = document.querySelector('#hidden-file-upload-input');
if (old) old.remove();
const input = document.createElement('input');
input.type = 'file';
input.multiple = config.multiple ?? false;
input.classList.add('hidden');
input.style.display = 'none';
input.style.visibility = 'hidden';
input.id = 'hidden-file-upload-input';
input.accept = buildUploadInputAccept(config);
if (config.directory) {
input.webkitdirectory = true;
}
document.body.appendChild(input);
return input;
}
export interface UploadAccentProps {
extensions?: string[];
types?: string[];
}
export function buildUploadInputAccept({
extensions = [],
types = [],
}: UploadAccentProps): string {
const accept = [];
if (extensions?.length) {
extensions = extensions.map(e => {
return e.startsWith('.') ? e : `.${e}`;
});
accept.push(extensions.join(','));
}
if (types?.length) {
accept.push(types.join(','));
}
return accept.join(',');
}

View File

@@ -0,0 +1,8 @@
export function downloadFileFromUrl(url: string, name?: string) {
const link = document.createElement('a');
link.href = url;
if (name) link.download = name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}

View File

@@ -0,0 +1,4 @@
export function extensionFromFilename(fullFileName: string): string {
const re = /(?:\.([^.]+))?$/;
return re.exec(fullFileName)?.[1] || '';
}

View File

@@ -0,0 +1,81 @@
import {UploadedFile} from '../uploaded-file';
export async function getDroppedFiles(
dataTransfer: DataTransfer
): Promise<UploadedFile[]> {
let files: UploadedFile[] = [];
if (dataTransfer.items?.[0] && 'webkitGetAsEntry' in dataTransfer.items[0]) {
// need to make a copy if transfer items and get entry here, it
// might not be available anymore in subsequent loop iterations
const entries = [...dataTransfer.items].map(item => {
return item.webkitGetAsEntry();
});
for (const entry of entries) {
if (entry && !entry.isDirectory) {
files.push(await filesystemEntryToFile(entry as FileSystemFileEntry));
} else if (entry) {
files = [
...files,
...(await readDirRecursive(entry as FileSystemDirectoryEntry)),
];
}
}
} else {
files = [...dataTransfer.files].map(f => new UploadedFile(f));
}
return files;
}
function filesystemEntryToFile(
entry: FileSystemFileEntry
): Promise<UploadedFile> {
return new Promise(resolve => {
entry.file(file => {
resolve(new UploadedFile(file, entry.fullPath));
});
});
}
async function readDirRecursive(
entry: FileSystemDirectoryEntry,
files: UploadedFile[] = []
) {
const entries = await readEntries(entry);
for (const childEntry of entries) {
if (childEntry.isDirectory) {
await readDirRecursive(childEntry as FileSystemDirectoryEntry, files);
} else {
files.push(
await filesystemEntryToFile(childEntry as FileSystemFileEntry)
);
}
}
return files;
}
function readEntries(
dir: FileSystemDirectoryEntry
): Promise<FileSystemEntry[]> {
return new Promise(resolve => {
drainDirReader(dir.createReader(), resolve);
});
}
function drainDirReader(
reader: FileSystemDirectoryReader,
resolve: (value: FileSystemEntry[]) => void,
allEntries: FileSystemEntry[] = []
) {
// directory reader needs to be called repeatedly until it returns an empty array
reader.readEntries(entries => {
if (entries.length) {
allEntries = [...allEntries, ...entries];
drainDirReader(reader, resolve, allEntries);
} else {
resolve(allEntries);
}
});
}

View File

@@ -0,0 +1,30 @@
import {extensionFromFilename} from './extension-from-filename';
export function getFileMime(file: File): string {
const extensionsToMime: Record<string, string> = {
md: 'text/markdown',
markdown: 'text/markdown',
mp4: 'video/mp4',
mp3: 'audio/mp3',
svg: 'image/svg+xml',
jpg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
yaml: 'text/yaml',
yml: 'text/yaml',
};
const fileExtension = file.name ? extensionFromFilename(file.name) : null;
// check if mime type is set in the file object
if (file.type) {
return file.type;
}
// see if we can map extension to a mime type
if (fileExtension && fileExtension in extensionsToMime) {
return extensionsToMime[fileExtension];
}
return 'application/octet-stream';
}

View File

@@ -0,0 +1,31 @@
import {UploadInputConfig} from '../types/upload-input-config';
import {UploadedFile} from '../uploaded-file';
import {createUploadInput} from './create-upload-input';
/**
* Open browser dialog for uploading files and
* resolve promise with uploaded files.
*/
export function openUploadWindow(
config: UploadInputConfig = {}
): Promise<UploadedFile[]> {
return new Promise(resolve => {
const input = createUploadInput(config);
input.onchange = e => {
const fileList = (e.target as HTMLInputElement).files;
if (!fileList) {
return resolve([]);
}
const uploads = Array.from(fileList)
.filter(f => f.name !== '.DS_Store')
.map(file => new UploadedFile(file));
resolve(uploads);
input.remove();
};
document.body.appendChild(input);
input.click();
});
}

View File

@@ -0,0 +1,30 @@
// Adapted from https://github.com/Flet/prettier-bytes/
// Changing 1000 bytes to 1024, so we can keep uppercase KB vs kB
// ISC License (c) Dan Flettre https://github.com/Flet/prettier-bytes/blob/master/LICENSE
export function prettyBytes(num?: number, fractionDigits = 1): string {
if (num == null || Number.isNaN(num)) return '';
const neg = num < 0;
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (neg) {
num = -num;
}
if (num < 1) {
return `${(neg ? '-' : '') + num} B`;
}
const exponent = Math.min(
Math.floor(Math.log(num) / Math.log(1024)),
units.length - 1
);
num = Number(num / Math.pow(1024, exponent));
const unit = units[exponent];
if (num >= 10 || num % 1 === 0) {
// Do not show decimals when the number is two-digit, or if the number has no
// decimal component.
return `${(neg ? '-' : '') + num.toFixed(0)} ${unit}`;
}
return `${(neg ? '-' : '') + num.toFixed(fractionDigits)} ${unit}`;
}

View File

@@ -0,0 +1 @@
export const spaceUnits = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];