diff --git a/bin/files.js b/bin/files.js
new file mode 100644
index 0000000..71b5918
--- /dev/null
+++ b/bin/files.js
@@ -0,0 +1,53 @@
+const { LogType } = require("loguix")
+const fs = require("fs")
+const path = require("path")
+const { __glob } = require("./global-variables")
+const clog = new LogType("Fichier")
+const os = require("os");
+const uuid = require('uuid');
+var mime = require('mime-types');
+
+module.exports.getFiles = function(root) {
+
+ var response = new Object()
+ response.content = new Array()
+ response.root = root
+ response.parent = path.dirname(root)
+
+
+ try{
+
+ if(root == "homepath") {
+
+ root = os.homedir()
+ response.root = root
+
+ }
+
+
+ if(!fs.existsSync(root)) {
+ response.content = "NOT_EXIST"
+
+ } else {
+
+ for(var file of fs.readdirSync(root)) {
+
+ const stat = fs.statSync(root + path.sep + file)
+
+ response.content.push({"name":file, id: uuid.v4().toString() ,"fileDirectory" : root + path.sep + file , "type":mime.lookup(file), "size":stat.size, "lastedition":stat.mtimeMs, "directory":stat.isDirectory()})
+ }
+
+ }
+
+
+
+
+
+ }catch(err) {
+
+ response.content = "NOT_PERMITTED"
+ }
+
+ return response
+
+}
\ No newline at end of file
diff --git a/bin/server.js b/bin/server.js
index 921d23a..8a702a0 100644
--- a/bin/server.js
+++ b/bin/server.js
@@ -3,6 +3,7 @@ const fs = require("fs")
const path = require("path")
const { __glob } = require("./global-variables")
const auth = require("./auth")
+const files = require("./files")
const plog = new LogType("Web")
const cook = require("cookie")
const http = require("http")
@@ -39,7 +40,10 @@ module.exports.serverIO = function(server) {
GetAnswer("USERINFO", {username: user.username, display_name: user.display_name ,picture: user.picture, permission: user.permission})
})
-
+ PostRequest("FX_GET", (root) => {
+
+ PostAnswer("FX_GET", files.getFiles(root))
+ })
socket.on("disconnect", () => {
@@ -48,7 +52,7 @@ module.exports.serverIO = function(server) {
function GetRequest(GQname, GQcallback) {
socket.on("GET/" + GQname, () => {
-
+
plog.log(user.username + " - Socket : " + socket.id + " - GET/" + GQname + " - [RECIEVED]")
GQcallback()
})
@@ -62,6 +66,21 @@ module.exports.serverIO = function(server) {
}
+ function PostRequest(GQname, GQcallback) {
+ socket.on("POST/" + GQname, (value) => {
+ plog.log(user.username + " - Socket : " + socket.id + " - POST/" + GQname + " - [RECIEVED]")
+ GQcallback(value)
+ })
+
+ }
+
+ function PostAnswer(GRname, GRvalue) {
+
+ plog.log(user.username + " - Socket : " + socket.id + " - POST/" + GRname + " - [ANSWERED]")
+ socket.emit("ANSWER/POST/" + GRname, GRvalue)
+
+ }
+
} else {
socket.disconnect()
diff --git a/main.js b/main.js
index 2834bf8..4c09c35 100644
--- a/main.js
+++ b/main.js
@@ -2,7 +2,7 @@ const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
-const favicon = require('express-favicon');
+var favicon = require('serve-favicon');
const app = express();
const fs = require("fs");
const log = require("loguix")
@@ -48,7 +48,7 @@ function setup() {
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
- app.use(favicon(__glob.ICON));
+ app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
getRouters()
users.fetchUsers()
diff --git a/package-lock.json b/package-lock.json
index 32aed08..b7c4b2f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,10 +14,10 @@
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
- "express-favicon": "^2.0.4",
"http-errors": "~1.6.3",
"loguix": "1.4.1",
"nodemon": "^3.0.1",
+ "serve-favicon": "^2.0.4",
"socket.io": "^4.7.2",
"uuid": "^9.0.1"
}
@@ -384,14 +384,6 @@
"node": ">= 0.10.0"
}
},
- "node_modules/express-favicon": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/express-favicon/-/express-favicon-2.0.4.tgz",
- "integrity": "sha512-JDGzumJdwF+WcJf+qwyhdpF1yzducuMCxZa+G6hxR3hor7ae/1CqpAPj8FXCGaGtqBA6ExDMfeszjuYRw5GUuQ==",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/express/node_modules/cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
@@ -855,6 +847,31 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/serve-favicon": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
+ "integrity": "sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==",
+ "dependencies": {
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "ms": "2.1.1",
+ "parseurl": "~1.3.2",
+ "safe-buffer": "5.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/serve-favicon/node_modules/ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "node_modules/serve-favicon/node_modules/safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+ },
"node_modules/serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
@@ -1385,11 +1402,6 @@
}
}
},
- "express-favicon": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/express-favicon/-/express-favicon-2.0.4.tgz",
- "integrity": "sha512-JDGzumJdwF+WcJf+qwyhdpF1yzducuMCxZa+G6hxR3hor7ae/1CqpAPj8FXCGaGtqBA6ExDMfeszjuYRw5GUuQ=="
- },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1719,6 +1731,30 @@
"statuses": "~1.4.0"
}
},
+ "serve-favicon": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
+ "integrity": "sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==",
+ "requires": {
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "ms": "2.1.1",
+ "parseurl": "~1.3.2",
+ "safe-buffer": "5.1.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+ }
+ }
+ },
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
diff --git a/package.json b/package.json
index 464af28..2203eec 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
- "express-favicon": "^2.0.4",
+ "serve-favicon": "^2.0.4",
"http-errors": "~1.6.3",
"loguix": "1.4.1",
"nodemon": "^3.0.1",
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..87d1dc9
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/javascripts/basics.js b/public/javascripts/basics.js
index 0d60ba2..f7162ac 100644
--- a/public/javascripts/basics.js
+++ b/public/javascripts/basics.js
@@ -1,5 +1,8 @@
+
+
// Get Document ID
+
/**
* Récupère depuis le document l'identifiant de l'élément
* @param {string} id Identifiant de l'élément `id='identifiant'`
@@ -43,3 +46,223 @@ class InfoPop {
}
+
+const viewsAccessible = new Map()
+
+var xMousePos = null;
+var yMousePos = null;
+
+document.onmousemove = function(e) {
+ xMousePos = e.clientX + window.scrollX;
+ yMousePos = e.clientY + window.scrollY;
+
+}
+
+document.oncontextmenu = function(e) {
+
+ xMousePos = e.clientX + window.scrollX;
+ yMousePos = e.clientY + window.scrollY;
+
+}
+
+
+class ViewWindow {
+ ViewHTML = null
+ ViewProperties =null
+ ViewSpanInteger = null
+ constructor(properties) {
+ if(!viewsAccessible.has(properties.title)) {
+
+ this.ViewProperties = properties
+ viewsAccessible.set(properties.title, true)
+
+ this.ViewHTML = `
+
+ `
+ this.ViewSpanInteger = document.createElement("div")
+ document.body.appendChild(this.ViewSpanInteger);
+ this.ViewSpanInteger.outerHTML = this.ViewHTML
+
+ const closeWindow = document.getElementById(properties.title + "_close")
+ closeWindow.addEventListener("click", () => {
+
+ this.destroy()
+
+ })
+
+ const header = document.getElementById(properties.title + "_header")
+ const windowDiv = document.getElementById(properties.title + "_window")
+
+ let isDragging = false;
+ let offsetX, offsetY;
+
+ header.addEventListener('mousedown', (e) => {
+ isDragging = true;
+
+ // Enregistrez la position de la souris par rapport à la fenêtre
+ offsetX = e.clientX - windowDiv.getBoundingClientRect().left;
+ offsetY = e.clientY - windowDiv.getBoundingClientRect().top;
+ });
+
+ // Gérer le déplacement
+ document.addEventListener('mousemove', (e) => {
+ if (isDragging) {
+
+ header.style.cursor = "grab"
+
+ // Calculez la nouvelle position de la fenêtre en fonction de la position de la souris
+ const newLeft = e.clientX - offsetX;
+ const newTop = e.clientY - offsetY;
+
+ // Limitation de la position pour éviter un débordement
+ const maxX = window.innerWidth - windowDiv.offsetWidth;
+ const maxY = window.innerHeight - windowDiv.offsetHeight;
+
+ windowDiv.style.left = Math.min(Math.max(newLeft, 0), maxX) + 'px';
+ windowDiv.style.top = Math.min(Math.max(newTop, 0), maxY) + 'px';
+ }
+ });
+
+ // Gérer la fin du glisser-déposer
+ document.addEventListener('mouseup', () => {
+ header.style.cursor = "unset"
+ isDragging = false;
+ });
+
+
+
+ }
+
+ }
+
+ destroy() {
+ const win = getID(`${this.ViewProperties.title}_window`)
+ win.outerHTML = ""
+ viewsAccessible.delete(this.ViewProperties.title)
+ }
+
+ setContent(text) {
+ const contentDiv = document.getElementById(this.ViewProperties.title + "_content")
+ contentDiv.innerHTML = text
+ }
+
+ getViewTitle() {
+
+ return this.ViewProperties.title
+ }
+
+}
+
+createView("files_explorer")
+
+function createView(viewType) {
+ if(viewType == 'files_explorer') {
+
+ generateFileExplorer()
+
+
+ }
+ if(viewType == 'service') {
+
+ const View = new ViewWindow({
+ title: "Gestionnaire des services",
+ width: "1000px",
+ height: "600px"
+ })
+ }
+
+}
+
+
+class DroppableMenu {
+ options = new Array()
+ id = null
+ DMSpanInteger = null
+ constructor(properties) {
+
+ this.id = properties.id
+ this.options.push(``)
+
+ if(xMousePos && yMousePos) {
+
+ this.DMSpanInteger = document.createElement("span")
+ document.body.appendChild(this.DMSpanInteger);
+ this.DMSpanInteger.outerHTML = this.options.join('')
+
+ const menu = getID(`dm-${this.id}`)
+
+ menu.style.left = (xMousePos - 40) + "px"
+ menu.style.top = (yMousePos - 40) + "px"
+
+ menu.addEventListener('mouseleave', () => {
+
+ menu.outerHTML = ""
+
+ })
+
+
+ }
+
+
+
+
+ }
+
+}
+
+
+
+function getFormattedDate(GetDate) {
+
+ const date = new Date(GetDate)
+
+ var gmonth = date.getMonth() + 1
+ var gday = date.getDate()
+ var gHour = date.getHours()
+ var gMinute = date.getMinutes()
+ var gSecondes = date.getSeconds()
+
+
+ if(date.getMonth() + 1 < 10) {
+ gmonth = "0" + (date.getMonth() + 1)
+ }
+
+ if(date.getDate() + 1 <= 10) {
+ gday = "0" + date.getDate()
+ }
+
+ if(date.getHours() + 1 <= 10) {
+ gHour = "0" + date.getHours()
+ }
+
+ if(date.getMinutes() + 1 <= 10) {
+ gMinute = "0" + date.getMinutes()
+ }
+
+ if(date.getSeconds() + 1 <= 10) {
+ gSecondes = "0" + date.getSeconds()
+ }
+
+ return gday + "/" + gmonth + "/" + date.getFullYear() + " - " + gHour + ":" + gMinute
+}
\ No newline at end of file
diff --git a/public/javascripts/filexplorer.js b/public/javascripts/filexplorer.js
new file mode 100644
index 0000000..bbe22bf
--- /dev/null
+++ b/public/javascripts/filexplorer.js
@@ -0,0 +1,232 @@
+document.addEventListener("contextmenu", (e) => {
+
+ e.preventDefault()
+})
+
+function generateFileExplorer() {
+
+ const View = new ViewWindow({
+ title: ` Gestionnaire de fichiers`,
+ width: "1000px",
+ height: "600px"
+ })
+
+ goHomePath()
+ function goHomePath() {
+ const rFiles = post("FX_GET", "homepath")
+
+ rFiles.then((result) => {
+ loadFiles(result)
+ })
+ }
+
+
+
+ View.setContent(`
+
+
+
+
+
+ Nouv. dossier
+ Upload
+
+
+
+
+
+
+ `)
+
+ const rootInput = getID(View.getViewTitle() + '_rootInput')
+ const explorer = getID(View.getViewTitle() + '_explorer')
+
+ rootInput.addEventListener("change", () => {
+
+ const reqFiles = post("FX_GET", rootInput.value)
+ reqFiles.then((result) => {
+ loadFiles(result)
+
+ })
+ })
+
+ function loadFiles(files) {
+
+ rootInput.value = files.root
+ var fileElements = new Array()
+
+ if(files.content == "NOT_PERMITTED") {
+ fileElements.unshift(` Revenir au dossier parent
`)
+ fileElements.push(" Vous n'avez pas les permissions pour accéder à ce dossier.
")
+
+ } else if(files.content == "NOT_EXIST") {
+ fileElements.unshift(` Revenir au dossier parent
`)
+ explorer.innerHTML = " Ce dossier n'existe pas.
"
+ } else {
+
+ fileElements.unshift(` Revenir au dossier parent
`)
+ for(const file of files.content) {
+
+ // Convert all file.size to a human readable format with units
+
+ file.size = bytesToSize(file.size)
+
+ //Tell if the file is a directory or not
+
+ if(file.directory) {
+ file.size = "Dossier"
+ }
+
+ fileElements.push(`
+
+ ${getIcon(file)}
+
${file.name}
+
+
Taille : ${file.size}
+
Date de modification : ${getFormattedDate(file.lastedition)}
+
`)
+ }
+
+
+ }
+
+
+ explorer.innerHTML = fileElements.join("")
+
+ const parent = document.getElementById("fx-parent")
+ const home = document.getElementById("fx-home")
+
+ home.addEventListener("click", () => {
+
+ goHomePath()
+
+ })
+
+ parent.addEventListener("click", () => {
+
+ const reqFiles = post("FX_GET", files.parent)
+ reqFiles.then((result) => {
+ loadFiles(result)
+ })
+
+ })
+
+ if(files.content != "NOT_PERMITTED" && files.content != "NOT_EXIST") {
+
+ // If it's a directory, get the file directory and make the request to get the files in it and loadIt
+
+ for(const file of files.content) {
+
+ if(file.directory) {
+
+ const element = document.getElementById(file.id)
+ element.addEventListener("click", () => {
+
+ const reqFiles = post("FX_GET", file.fileDirectory)
+ reqFiles.then((result) => {
+ loadFiles(result)
+ })
+ })
+ }
+
+ }
+
+
+ }
+
+ }
+
+
+
+}
+
+function createFileMenu(idCreated) {
+
+ const dropMenu = new DroppableMenu({
+ "id": idCreated
+ })
+
+ dropMenu.add("rename", " Renommer")
+ dropMenu.add("share", " Partager")
+ dropMenu.add("delete", " Supprimer")
+
+
+ dropMenu.show()
+}
+
+function getIcon(file) {
+ if(file.type == "application/json") {
+
+ return ' '
+ }
+ if(file.type == "application/msword" | file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
+
+ return ' '
+ }
+ if(file.type == "application/vnd.ms-powerpoint") {
+
+ return ' '
+ }
+ if(file.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | file.type == "application/vnd.ms-excel") {
+
+ return ' '
+ }
+
+ if(file.type == "application/java-archive") {
+
+ return ' '
+ }
+
+ if(file.type == "application/x-sh") {
+
+ return ' '
+ }
+
+ if(file.type == "application/x-msdos-program" | file.type == "application/x-msdownload") {
+
+ return' '
+ }
+ if(file.type == "application/javascript") {
+
+ return ''
+ }
+ if(file.type == "image/png" | file.type == "image/jpeg") {
+
+ return ' '
+ }
+ if(file.type == "text/html") {
+
+ return ' '
+ }
+ if(file.type == "text/css") {
+
+ return ' '
+ }
+ if(file.type == "application/zip") {
+
+ return ' '
+ }
+ if(file.type == "audio/mpeg") {
+
+ return ' '
+ }
+ if(file.type == "application/pdf") {
+
+ return ' '
+ }
+ if(file.directory) {
+
+ return ' '
+ } else {
+
+ return ' '
+ }
+
+}
+
+function bytesToSize(bytes) {
+ var sizes = ['o', 'Ko', 'Mo', 'Go', 'To'];
+ if (bytes == 0) return '0 o';
+ var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
+ return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
+}
\ No newline at end of file
diff --git a/public/javascripts/indexscript.js b/public/javascripts/indexscript.js
index 7859b77..a833d32 100644
--- a/public/javascripts/indexscript.js
+++ b/public/javascripts/indexscript.js
@@ -19,13 +19,13 @@ REQ_user.then((ANS_user) => {
permissions.forEach((permValue) => {
if(permValue == "FILES_EXPLORER") {
- AvailableViews.push(`
+ AvailableViews.push(`
`)
}
if(permValue == "SERVICES") {
- AvailableViews.push(`
+ AvailableViews.push(`
`)
diff --git a/public/javascripts/io.js b/public/javascripts/io.js
index 8da2865..0adf2f3 100644
--- a/public/javascripts/io.js
+++ b/public/javascripts/io.js
@@ -36,6 +36,27 @@ function get(request) {
}
+function post(request, value) {
+
+ return new Promise((resolve, reject) => {
+
+ socket.emit("POST/" + request, value)
+ console.log("Envoi de la requête POST : " + request)
+
+ socket.once("ANSWER/POST/" + request, (answer) => {
+
+ console.log("Réponse pour la requête : " + request)
+
+ resolve(answer)
+
+ })
+
+ })
+
+}
+
+
+
function getSocket() {
return socket
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index ec4a17d..1e2046b 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -6,7 +6,7 @@ body {
color: white;
font-size: 20px;
font-family: 'Roboto', sans-serif !important;
-
+ overflow: hidden;
}
html {
@@ -58,6 +58,21 @@ html {
text-decoration: none;
}
+.btn-cover {
+ color: white;
+ transition: 0.1s;
+ cursor: pointer;
+}
+
+.btn-cover:hover {
+ color: rgba(255, 255, 255, 0.675);
+
+}
+
+.btn-cover:active {
+
+ color: rgba(255, 255, 255, 0.575);
+}
/* Miniaturiez Button */
.min {
@@ -404,4 +419,160 @@ a {
.view-image {
font-size: 72px;
-}
\ No newline at end of file
+}
+
+
+/*ViewWindow*/
+
+.view-window {
+
+ position: absolute;
+ z-index: 1;
+ background-color: #605e58c1;
+ backdrop-filter: blur(10px);
+ border-radius: 0.5vw;
+ -webkit-user-drag: auto;
+}
+
+.view-window-header {
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px;
+ background-color: rgba(33, 32, 33, 0.753);
+ border-top-right-radius: 0.5vw;
+ border-top-left-radius: 0.5vw;
+ user-select: none;
+}
+
+
+/* Files Explorer */
+
+.fx-window {
+
+ display: flex;
+ flex-direction: column;
+
+}
+
+.fx-root-input {
+ background-color: #323031;
+ border: solid 1px #323031;
+ border-radius: 0.5vw;
+ padding: 1%;
+ color: white;
+ outline:0px;
+ width: 100%;
+
+}
+
+.fx-explorer {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto ;
+ height: 450px;
+ gap: 20px
+}
+
+.fx-element {
+
+ display: grid;
+ grid-template-columns: 3fr 1fr 2fr;
+ grid-column-gap: 10px;
+ align-items: center;
+ padding: 5px;
+ padding-left: 30px;
+ padding-right: 30px;
+ font-size: 14px;
+ transition: 0.1s;
+}
+
+.fx-element div {
+ display: flex;
+ justify-content: start;
+ gap: 10px;
+ align-items: center;
+}
+
+.fx-element:hover {
+
+ background-color: rgba(44, 40, 42, 0.614);
+
+}
+
+.fx-actions {
+ justify-content: end !important;
+ width: 100%;
+}
+
+.fx-bar {
+
+ margin: 1%;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+
+}
+
+.fx-bar-actions {
+
+ width: 30%;
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+
+}
+
+/* ===== Scrollbar CSS ===== */
+ /* Firefox */
+ * {
+ scrollbar-width: auto;
+ scrollbar-color: #ff5d51 #ffffff;
+ }
+
+ /* Chrome, Edge, and Safari */
+ *::-webkit-scrollbar {
+ width: 13px;
+ }
+
+ *::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ *::-webkit-scrollbar-thumb {
+ background-color: #ff5d51;
+ border-radius: 17px;
+ border: 2px solid transparent;
+ }
+
+/* Dropable Menu */
+
+.dm-menu {
+ position: absolute;
+ display: flex;
+ flex-direction: column;
+ width: 150px;
+ border-radius: 0.5vw;
+ z-index: 3;
+ height: auto;
+}
+
+
+.dm-element {
+
+ background-color: #1a1819c9;
+ padding: 10px;
+ cursor: pointer;
+ font-size: 14px;
+ display: grid;
+ grid-template-columns: 0.2fr 0.8fr;
+
+}
+
+.dm-element:hover {
+ background-color: #ffffffc9;
+ color:black
+
+}
+
diff --git a/views/index.ejs b/views/index.ejs
index 49a6df2..6ccb27b 100644
--- a/views/index.ejs
+++ b/views/index.ejs
@@ -56,8 +56,11 @@
-
+
+
+
+