file explorer working

but can't open files externally
This commit is contained in:
Florian Bouillon 2019-03-10 01:13:57 +01:00
parent 9628c2cd42
commit efc0a0e1b9
38 changed files with 7540 additions and 350 deletions

8
.gitignore vendored
View File

@ -1 +1,7 @@
node_modules
.DS_Store
dist/
node_modules/
thumbs.db
yarn-error.log
.yarnclean
.vscode

14
CHANGELOG.md Normal file
View File

@ -0,0 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security

View File

@ -1 +1,14 @@
Gestionnaire de fichier en construction!
# Delta File Manager
A WIP project By [Avior](https://delta-wings.net) <florian.bouillon@delta-wings.net> (Gitlab account: [Gitlab](https://gitlab.com/aviortheking))
this project is licensed under the [MIT License](./LICENSE.md)
## Icons
I used the icons from [Feather icons](https://feathericons.com) (Github link: [Github](https://github.com/feathericons/feather)) licensed under the [MIT License](https://github.com/feathericons/feather/blob/master/LICENSE)
## issues
- Actually we need to manually delete three lines from the package nunjuck loader to be able to run the dev server (index.js #21-23)

View File

@ -1,84 +0,0 @@
# import Gitlab from 'gitlab'
{ ProjectBundle } = require('gitlab')
options = {
url: 'https://gitlab.com'
token: ''
}
gitlab = new ProjectBundle(options)
# this is actually a test
forceRefresh = true
user = undefined
getUser = () ->
if user is undefined or forceRefresh
user = await gitlab.Users.current()
user
projects = undefined
getProjects = () ->
console.log "loading Projects!"
if projects is undefined or forceRefresh
console.log "loading Projects!"
projects = await gitlab.Projects.all { membership: true }
console.log projects
projects
project = {}
getProject = (projectId) ->
if projectId isnt undefined
if project[projectId] is undefined or forceRefresh
project[projectId] = await (gitlab.Projects).show projectId
return project[projectId]
return undefined
projectTree = {}
getProjectTree = (projectId) ->
if projectId isnt undefined
if projectTree[projectId] is undefined or forceRefresh
projectTree[projectId] = await gitlab.Repositories.tree(projectId, { recursive: true }).catch (e) ->
console.log "pouet"
console.log ed
return projectTree[projectId]
return undefined
loadProjects = (parent) ->
for el in await getProjects()
tr = document.createElement 'tr'
name = el.name_with_namespace.split '/'
name.pop()
td = "<td>" + el.id + "</td><td>" + el.name + "</td><td>" + name.join(" -> ") + "</td>"
tr.innerHTML = td
tr.setAttribute 'data-id', el.id
tr.addEventListener 'click', (event) ->
console.log event
loadProjectTree document.querySelector(".tree"), event.target.parentNode.getAttribute("data-id")
return
parent.appendChild tr
return
loadProjectTree = (parent, projectId) ->
parent.innerHTML = ''
for el in await getProjectTree projectId
path = el.path.split "/"
path.pop()
tempParent = parent
if el.type is "tree" then element = "<ul data-path=\"" + el.name + "\"></ul>"
else element = "<li>" + el.name + "</li>"
if path.length > 1
tempParent = parent.querySelector "[data-path=\"" + path.pop() + "\"]"
tempParent.innerHTML += element
for el in parent.querySelectorAll("[data-path]")
li = document.createElement "li"
text = document.createTextNode el.getAttribute("data-path") + "/"
li.appendChild text
li.classList.add "folder"
el.parentNode.insertBefore(li, el)
return
loadProjects(document.querySelector('.projectList'))
# console.log projects

View File

@ -1,31 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<main>
<table>
<thead>
<tr>
<th>File Name</th>
<th>Modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</main>
<script>
// You can also require other files to run in this process
require('./renderer.js');
// getProjects();
</script>
</body>
</html>

56
main.js
View File

@ -1,56 +0,0 @@
// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

View File

@ -1,18 +1,64 @@
{
"name": "delta-file-manager",
"version": "0.1.0",
"description": "A customizable fiel manager",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "Avior",
"license": "MIT",
"devDependencies": {
"electron": "^4.0.6"
"homepage": "https://delta-wings.net/dfm",
"author": {
"name": "Florian bouillon",
"email": "florian.bouillon@delta-wings.net",
"url": "https://delta-wings.net/"
},
"scripts": {
"dev": "electron-webpack dev",
"compile": "electron-webpack",
"dist": "yarn compile && electron-builder",
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
},
"build": {
"appId": "net.deltawings.deltafilemanager",
"productName": "Delta File Manager",
"copyright": "Copyright @ 2019 ${author}",
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64",
"ia32"
]
}
]
},
"nsis": {
"oneClick": false
},
"linux": {
"target": [
{
"target": "deb"
},
{
"target": "AppImage"
}
]
}
},
"dependencies": {
"ts-node": "^8.0.2",
"typescript": "^3.3.3333"
"@types/nunjucks": "^3.1.1",
"@types/sharp": "^0.21.3",
"source-map-support": "^0.5.10"
},
"devDependencies": {
"electron": "4.0.1",
"electron-builder": "20.38.4",
"electron-webpack": "^2.6.2",
"electron-webpack-ts": "^3.1.1",
"node-sass": "^4.11.0",
"nunjucks": "^3.2.0",
"nunjucks-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"typescript": "^3.3.3333",
"webpack": "4.28.4",
"webpack-build-notifier": "^0.1.30"
}
}

View File

@ -1,2 +0,0 @@
require("ts-node").register();
require("./src/renderer");

30
src/common/Actions.ts Normal file
View File

@ -0,0 +1,30 @@
import DeltaFile from "./DeltaFile";
import {
sep
} from 'path';
import {
getDataFolder
} from "./Functions";
import fs from "fs";
class Actions { //WIP
public deleteFile(file: DeltaFile) {
let res = getDataFolder() + sep + "trash" + sep + "files" + sep + file.name;
if(fs.existsSync(res)) {
//rename and add it
} else {
//move the folder/file in the trash
//write to infos.json about the deleted file
}
}
public emptyBin() {
//delete all files in the trash
}
//file is the file name in the trash
public restoreDeletedFile(file: string) {
}
}

112
src/common/DeltaFile.ts Normal file
View File

@ -0,0 +1,112 @@
import fs from 'fs';
import {
sep
} from 'path';
const icons = require("./icons.json");
//TODO handle file redirection when no link is given
export default class DeltaFile {
_name: string = "";
_modetime: Date | string = "--";
_size: number | string = "--";
_path: string = "";
_type: number = 0; // 0 = directory, 1 = file;
constructor() {}
public static loadFileFromElements(name: string, path: string, type: boolean = false, modetime?: Date, size?: number): DeltaFile {
let file = new DeltaFile();
if(type) file.modetime = (modetime != undefined) ? modetime : "--";
file.name = name;
file.path = path;
if(type) file.type = type ? 1 : 0;
if(type) file.size = (size != undefined) ? size : "--";
return file;
}
public static loadFileFromPath(path: string): DeltaFile | undefined {
let file = new DeltaFile();
try {
var stat = fs.statSync(path);
} catch (error) {
return undefined;
}
let name = path.split(sep).pop();
if(name == undefined) return undefined;
file.type = stat.isDirectory() ? 0 : 1;
if(file.type != 0) {
file.modetime = stat.mtime;
file.size = stat.size;
}
file.path = path;
file.name = name;
return file;
}
public get name() : string {return this._name}
public set name(v : string) {this._name = v}
public get modetime(): Date | string {return this._modetime}
public set modetime(v: Date | string) {this._modetime = v}
public formatTime(): string {
if(typeof this.modetime === "string") return this.modetime;
return this.modetime.toISOString()
.replace(/T/, ' ')
.replace(/\..+/, '')
.replace(":", "/");
}
// 2019/03/09 04:14:01
public get size(): number | string {return this._size}
public set size(v: number | string) {this._size = v}
public formatSize(): string {
if(typeof this.size === "string") return this.size;
if(this.size === 0) return "0 bytes";
let sep = 1024, d = 2;
let ent = ["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"];
let f = Math.floor(Math.log(this.size)/Math.log(sep));
return parseFloat((this.size / Math.pow(sep, f)).toFixed(d)) + " " + ent[f];
}
public get path(): string {return this._path}
public set path(v: string) {this._path = v}
public get type(): number {return this._type}
public set type(v: number) {this._type = v}
/**
* getThumbnail
*/
public getThumbnail(): string | undefined {
//if file load picture
//if folder see location(/home/___/pictures, /home) or name (like git, node_modules)
if(this.type == 0) {
for (const el in icons.folder.name) {
if (icons.folder.name.hasOwnProperty(el)) {
const element = icons.folder.name[el];
if(el == this.name.toLowerCase()) return element;
}
}
if(icons.folder.path[this.path] != undefined) return icons.folder.path[this.path];
return undefined;
} else {
let ext = this.path.split("/").pop();
if(ext == undefined) return undefined;
ext = ext.split(".").pop();
console.log(ext);
if(ext == undefined) return undefined;
let imgExt = ["jpg", "jpeg", "png"];
if(imgExt.includes(ext) ) return "file://" + this.path;
if(icons.file.extension[ext] != undefined) return icons.file.extension[ext];
return undefined;
}
}
}

16
src/common/Functions.ts Normal file
View File

@ -0,0 +1,16 @@
import {
sep
} from 'path';
const folderName = "delta-file-manager";
export function getDataFolder() {
let platform = process.platform;
if(platform == "win32") {
return process.env.APP_DATA + sep + folderName;
} else if(platform == "darwin") {
return process.env.HOME + sep + "Library/Preferences" + sep + folderName;
} else if(platform == "linux") {
return process.env.HOME + sep + ".config" + sep + folderName;
} else return undefined;
}

31
src/common/icons.json Normal file
View File

@ -0,0 +1,31 @@
{
"folder": {
"name": {
"..": "icons/chevron-left.svg",
"git": "icons/github.svg",
".git": "icons/github.svg",
"pictures": "icons/image.svg",
"music": "icons/music.svg",
"videos": "icons/video.svg",
"public": "icons/users.svg",
"downloads": "icons/download.svg",
".vscode": "icons/code.svg",
"node_modules": "icons/package.svg",
".config": "icons/settings.svg",
"src": "icons/code.svg"
},
"path": {
"/home": "icons/home.svg"
}
},
"file": {
"name": {
".gitlab-ci.yaml": "icons/gitlab.svg"
},
"extension": {
"code-workspace": "icons/code.svg",
"coffee": "icons/coffee.svg"
}
}
}

66
src/main/index.js Normal file
View File

@ -0,0 +1,66 @@
'use strict'
import { app, BrowserWindow } from 'electron'
import * as path from 'path'
import { format as formatUrl } from 'url'
const isDevelopment = process.env.NODE_ENV !== 'production'
// global reference to mainWindow (necessary to prevent window from being garbage collected)
let mainWindow
function createMainWindow() {
const window = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
})
if (isDevelopment) {
window.webContents.openDevTools()
}
if (isDevelopment) {
window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`)
}
else {
window.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}))
}
window.on('closed', () => {
mainWindow = null
})
window.webContents.on('devtools-opened', () => {
window.focus()
setImmediate(() => {
window.focus()
})
})
return window
}
// quit application when all windows are closed
app.on('window-all-closed', () => {
// on macOS it is common for applications to stay open until the user explicitly quits
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// on macOS it is common to re-create a window even after all windows have been closed
if (mainWindow === null) {
mainWindow = createMainWindow()
}
})
// create main BrowserWindow when electron is ready
app.on('ready', () => {
mainWindow = createMainWindow()
})

View File

@ -1,69 +0,0 @@
import { sep } from 'path';
import { Stats } from "fs";
const fs = require("fs");
import { shell } from 'electron';
const loader = document.querySelector("tbody");
const rootPath = '/';
const loadFolder = (path: string) => {
loader.innerHTML = "";
if(path != rootPath) {
let pt = path.split("/");
pt.pop();
console.log(rootPath);
console.log(pt.join("/"));
let pouet = "" == pt.join("/") ? sep : pt.join("/");
addElement("..", pouet);
}
fs.readdir(path, (err, items) => {
for (const item of items) {
console.log(path);
let it = path == "/" ? path + item : path + sep + item;
fs.stat(it, (err, stat) => {
// console.log(err);
let pth = path == "/" ? path + item : path + sep + item;
addElement(item, pth, stat);
});
}
});
}
function addElement(filename: string, path: string, stat: Stats = null) {
let mtime = stat != null ? stat.mtime : null;
let classe = stat != null ? (stat.isDirectory() ? "folder" : "file") : "folder";
if(stat != null) var month = (mtime.getUTCMonth() + 1) > 10 ? "0" + (mtime.getUTCMonth() + 1) : "" + (mtime.getUTCMonth() + 1);
else var month = "0";
let time = stat == null || stat.isDirectory() ? "--" : mtime.getUTCFullYear() + "/" + month + "/" + mtime.getUTCDate();
let bytes = stat == null || stat.isDirectory() ? "--" : formatBytes(stat.size);
let tr = document.createElement("tr");
tr.classList.add(classe);
tr.setAttribute("data-file", path);
tr.addEventListener("click", function() {
if(this.classList.contains("folder")) loadFolder(this.getAttribute("data-file"));
else loadFile(this.getAttribute("data-file"));
});
for (const el of [filename, time, bytes]) {
let td = document.createElement("td");
td.innerText = el;
tr.appendChild(td);
}
loader.appendChild(tr);
}
function formatBytes(integ: number): string {
if(integ === 0) return "0 bytes";
let sep = 1024, d = 2;
let ent = ["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"];
let f = Math.floor(Math.log(integ)/Math.log(sep));
return parseFloat((integ / Math.pow(sep, f)).toFixed(d)) + " " + ent[f];
}
function loadFile(file: string) {
shell.openItem(file);
}
loadFolder("/");

View File

@ -0,0 +1,38 @@
{% extends "../index.njk" %}
{% block filelist %}
<div class="grid">
{% for file in files %}
{% if file.type == "0" %}
{% set thumb = file.getThumbnail() %}
{% if thumb != undefined %}
<div class="grid-item folder" data-file={{ file.path }} style="background-image: url({{ thumb }})">
{% else %}
<div class="grid-item folder" data-file={{ file.path }}>
{% endif %}
{{file.name}}
</div>
{% endif %}
{% endfor %}
</div>
<div class="grid">
{% for file in files %}
{% if file.type == "1" %}
<div class="grid-item file" data-file="{{ file.path }}">
{% if file.getThumbnail() != undefined %}
<div class="item-preview" style='background-image: url("{{ file.getThumbnail() }}")'>
{% else %}
<div class="item-preview null">
{% endif %}
</div>
<div class="item-name">{{ file.name }}</div>
<div class="item-size">{{ file.formatSize() }}</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "../index.njk" %}
{% block filelist %}
<table>
<thead>
<tr>
<th>Name</th>
<th>Last Time modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr data-file="{{ file.path }}">
<td>{{ file.name }}</td>
<td>{{ file.formatTime() }}</td>
<td>{{ file.formatSize() }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

5
src/renderer/index.njk Normal file
View File

@ -0,0 +1,5 @@
<aside>
</aside>
<main>
{% block filelist %}{% endblock %}
</main>

52
src/renderer/index.ts Normal file
View File

@ -0,0 +1,52 @@
// import { render as nunjuckRender} from 'nunjucks';
import fs from 'fs';
import { sep } from 'path';
import DeltaFile from '../common/DeltaFile';
import './style.scss';
import { Template } from 'nunjucks';
const tpl: Template = require("./fileRenderers/blocks.njk");
const rootPath = "/";
const loadFolder = (path: string) => {
let results = [];
if(path != rootPath) {
let pt = path.split("/");
pt.pop();
let pouet = "" == pt.join("/") ? sep : pt.join("/");
results.push(DeltaFile.loadFileFromElements("..", pouet, false));
}
let items = fs.readdirSync(path);
for (const item of items) {
let pth = path == "/" ? path + item : path + sep + item;
let file = DeltaFile.loadFileFromPath(pth);
if(file != undefined)results.push(file);
}
return results;
}
const showFolder = (folder: string) => {
let app = document.getElementById("app");
if(app == undefined) app = document.body;
var els = loadFolder(folder);
app.innerHTML = tpl.render({files: els});
app.querySelectorAll("[data-file]").forEach((el) => {
el.addEventListener("click", function(this: HTMLElement) {
// console.log(this);
let folder = this.getAttribute("data-file");
if(folder != null) showFolder(folder);
});
});
}
showFolder("/");
// function loadFile(file: string) {
// shell.openItem(file);
// }
// console.log(render("index.njk"));
// console.log(loadFolder("/"));

110
src/renderer/style.scss Normal file
View File

@ -0,0 +1,110 @@
html, body {
margin: 0;
}
.grid {
display: grid;
width: calc(100% - 60px);
grid-template-columns: repeat(5, 1fr);
grid-gap: 30px;
place-items: stretch;
// justify-content: center;
padding: 30px;
@media (max-width: 1190px) {
grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 960px) {
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 721px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 502px) {
grid-template-columns: 1fr;
}
> .grid-item {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space:nowrap;
background-repeat: no-repeat;
justify-self: stretch;
min-width: 150px;
cursor: pointer;
&.folder {
background-image: url(../../static/icons/folder.svg);
background-size: 30px;
background-position-y: center;
background-position-x: 10px;
// min-width: 150px;
height: 45px;
line-height: 45px;
border: 1px solid rgba(0,0,0,.1);
padding: 10px .5%;
padding-left: calc(.5% + 45px);
border-radius: 10px;
color: rgba(0, 0, 0, .8);
transition-property: box-shadow, transform, background-color;
transition-duration: 200ms;
transition-timing-function: ease-in-out;
&:hover {
box-shadow: 0px 5px 0px 0px rgba(0, 0, 0, 0.3);
transform: translateY(-5px);
}
&:active {
box-shadow: 0px -0px 0px 0px rgba(0, 0, 0, 0.3);
background-color: rgba(0, 0, 0, 0.2);
transform: translateY(0px);
}
}
&.file {
border-radius: 10px;
transition-property: box-shadow, transform, background-color;
transition-duration: 200ms;
transition-timing-function: ease-in-out;
&:hover {
box-shadow: 0px 5px 0px 0px rgba(0, 0, 0, 0.3);
transform: translateY(-5px);
}
&:active {
box-shadow: 0px -0px 0px 0px rgba(0, 0, 0, 0.3);
background-color: rgba(0, 0, 0, 0.05);
transform: translateY(0px);
}
.item-preview {
width: 100%;
border-radius: 10px;
height: 150px;
box-shadow: inset 0 0 20px 0 rgba(0, 0, 0, .1);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
&.null {
background-image: url(../../static/icons/file.svg);
background-size: contain;
}
}
.item-name {
margin: 10px;
font-size: 20px;
color: rgba(0, 0, 0, .8)
}
.item-size {
color: rgba(0, 0, 0, .5);
margin: 10px;
}
}
}
}
* {
user-select: none;
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-left"><polyline points="15 18 9 12 15 6"></polyline></svg>

After

Width:  |  Height:  |  Size: 270 B

1
static/icons/code.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-code"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>

After

Width:  |  Height:  |  Size: 307 B

1
static/icons/coffee.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-coffee"><path d="M18 8h1a4 4 0 0 1 0 8h-1"></path><path d="M2 8h16v9a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V8z"></path><line x1="6" y1="1" x2="6" y2="4"></line><line x1="10" y1="1" x2="10" y2="4"></line><line x1="14" y1="1" x2="14" y2="4"></line></svg>

After

Width:  |  Height:  |  Size: 447 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>

After

Width:  |  Height:  |  Size: 370 B

1
static/icons/file.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>

After

Width:  |  Height:  |  Size: 337 B

1
static/icons/folder.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>

After

Width:  |  Height:  |  Size: 311 B

1
static/icons/github.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-github"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>

After

Width:  |  Height:  |  Size: 527 B

1
static/icons/gitlab.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-gitlab"><path d="M22.65 14.39L12 22.13 1.35 14.39a.84.84 0 0 1-.3-.94l1.22-3.78 2.44-7.51A.42.42 0 0 1 4.82 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.49h8.1l2.44-7.51A.42.42 0 0 1 18.6 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.51L23 13.45a.84.84 0 0 1-.35.94z"></path></svg>

After

Width:  |  Height:  |  Size: 490 B

1
static/icons/home.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>

After

Width:  |  Height:  |  Size: 332 B

1
static/icons/icons.md Normal file
View File

@ -0,0 +1 @@
source : https://feathericons.com/

1
static/icons/image.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-image"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>

After

Width:  |  Height:  |  Size: 369 B

1
static/icons/music.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-music"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>

After

Width:  |  Height:  |  Size: 327 B

1
static/icons/package.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-package"><path d="M12.89 1.45l8 4A2 2 0 0 1 22 7.24v9.53a2 2 0 0 1-1.11 1.79l-8 4a2 2 0 0 1-1.79 0l-8-4a2 2 0 0 1-1.1-1.8V7.24a2 2 0 0 1 1.11-1.79l8-4a2 2 0 0 1 1.78 0z"></path><polyline points="2.32 6.16 12 11 21.68 6.16"></polyline><line x1="12" y1="22.76" x2="12" y2="11"></line><line x1="7" y1="3.5" x2="17" y2="8.5"></line></svg>

After

Width:  |  Height:  |  Size: 538 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-settings"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>

After

Width:  |  Height:  |  Size: 1011 B

1
static/icons/users.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>

After

Width:  |  Height:  |  Size: 400 B

1
static/icons/video.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-video"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@ -14,13 +14,18 @@ table th {
}
table tr {
border-bottom: 1px solid #212121;
border-bottom: 1px solid #aaa;
height: 50px;
}
table tr td {
border-left: 1px solid #eee;
}
table tr td:first-child {
padding-left: 60px;
background-repeat: no-repeat;
border-left: none;
}
table tr.folder td:first-child {

3
tsconfig.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "./node_modules/electron-webpack/tsconfig-base.json"
}

7036
yarn.lock

File diff suppressed because it is too large Load Diff