From 6e5940d3f043bbfdeff8d8a1f9eab85ea097149c Mon Sep 17 00:00:00 2001 From: Kat21 <49850194+datkat21@users.noreply.github.com> Date: Sun, 14 Jan 2024 13:48:14 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Pluto=20v1.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Large file support (finally) - New custom resize cursors - Theme transition should work better on WebKit browsers - App store caching issue fixed - Added AudioPlayer and VideoPlayer as modified versions of ImageViewer - Loading screen can be stopped if it doesn't finish in time - Proper file mappings for MP4, MOV, MKV, AVI, WebM, WAV, MPEG and MP3. Co-authored-by: Lap Co-authored-by: tuck --- README.md | 2 +- assets/icons.js | 12 +- core.js | 66 ++++---- pkgs/apps/AppStore.js | 8 +- pkgs/apps/AudioPlayer.js | 106 +++++++++++++ pkgs/apps/FileManager.js | 91 +++++++---- pkgs/apps/ImageViewer.js | 2 +- pkgs/apps/Settings.js | 69 ++++++--- pkgs/apps/VideoPlayer.js | 107 +++++++++++++ pkgs/lib/FileMappings.js | 48 +++++- pkgs/lib/ThemeLib.js | 20 +++ pkgs/lib/VirtualFS.js | 110 +++++++++++-- pkgs/system/BootLoader.js | 2 +- pkgs/ui/Desktop.js | 1 - pkgs/ui/LoadingScreen.js | 47 +++++- style.css | 314 +++++++++++++++++++++++++++----------- 16 files changed, 810 insertions(+), 195 deletions(-) create mode 100644 pkgs/apps/AudioPlayer.js create mode 100644 pkgs/apps/VideoPlayer.js diff --git a/README.md b/README.md index f2066fe..2d709ac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Pluto v1.4 +# Pluto v1.5 ![Pluto banner](assets/images/banner.svg) diff --git a/assets/icons.js b/assets/icons.js index 1865296..fa7074b 100644 --- a/assets/icons.js +++ b/assets/icons.js @@ -23,11 +23,15 @@ export default { '', clock: '', - lock: '', + lock: '', image: '', + fileAudio: + '', fileImage: '', + fileVideo: + '', fileImport: '', listTree: @@ -83,11 +87,13 @@ export default { circleCheck: '', warning: - '', + '', circleExclamation: '', refresh: '', download: - '', + '', + upload: + '', }; diff --git a/core.js b/core.js index f3d2f76..e343e71 100644 --- a/core.js +++ b/core.js @@ -2,8 +2,8 @@ (async () => { try { const coreDetails = { - version: 1.45, - versionString: (1.4).toFixed(1), + version: 1.5, + versionString: (1.5).toFixed(1), codename: "Elysium", }; const knownLibraries = []; @@ -282,6 +282,31 @@ } } }, + randomString: (_) => { + if (crypto && crypto.randomUUID) return crypto.randomUUID(); + else { + var d = new Date().getTime(); + var d2 = + (typeof performance !== "undefined" && + performance.now && + performance.now() * 1000) || + 0; + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( + /[xy]/g, + function (c) { + var r = Math.random() * 16; + if (d > 0) { + r = (d + r) % 16 | 0; + d = Math.floor(d / 16); + } else { + r = (d2 + r) % 16 | 0; + d2 = Math.floor(d2 / 16); + } + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); + } + ); + } + }, loadLibrary: async function (lib) { if (lib.includes(":")) return false; knownLibraries.push(lib); @@ -343,16 +368,24 @@ }; this.html = GlobalLib.html; + this.randomString = GlobalLib.randomString; this.icons = GlobalLib.icons; this.systemInfo = coreDetails; this.langs = supportedLangs; this.launch = async (app, parent = "body") => { + let appName = ""; + + if (Core.processList[Pid].proc !== null) { + appName = Core.processList[Pid].proc.name; + } else { + appName = "???"; + } if ( (await Modal.prompt( getString("notice"), getString("core_appLaunch_notification", { - suspectedApp: Core.processList[Pid].proc.name, + suspectedApp: appName, targetApp: app.split(":").pop(), }), parent @@ -457,31 +490,6 @@ Core.processList[pid] = null; console.groupEnd(); }, - randomString: (_) => { - if (crypto && crypto.randomUUID) return crypto.randomUUID(); - else { - var d = new Date().getTime(); - var d2 = - (typeof performance !== "undefined" && - performance.now && - performance.now() * 1000) || - 0; - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( - /[xy]/g, - function (c) { - var r = Math.random() * 16; - if (d > 0) { - r = (d + r) % 16 | 0; - d = Math.floor(d / 16); - } else { - r = (d2 + r) % 16 | 0; - d2 = Math.floor(d2 / 16); - } - return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); - } - ); - } - }, }; let Modal; @@ -587,7 +595,7 @@ pid: PID, proc: null, }; - const Token = ProcLib.randomString(); + const Token = GlobalLib.randomString(); const newLib = new processLib(url, PID, Token, pkg.strings); if (Core.processList[PID]) Core.processList[PID].token = Token; let result; diff --git a/pkgs/apps/AppStore.js b/pkgs/apps/AppStore.js index ff84e1d..ecf93db 100644 --- a/pkgs/apps/AppStore.js +++ b/pkgs/apps/AppStore.js @@ -75,7 +75,7 @@ export default { let host = "https://zeondev.github.io/Pluto-AppStore/"; - const appStoreModule = (await import(`${host}import.js`)).default; + const appStoreModule = (await import(`${host}import.js?t=` + performance.now())).default; // Check if this is the right app store module if (appStoreModule.init) { @@ -117,7 +117,7 @@ export default { } async function installApp(pkg, app, force = false) { - await fetch(`${host}pkgs/${pkg}/${app.assets.path}`) + await fetch(`${host}pkgs/${pkg}/${app.assets.path}?t=` + performance.now()) .then(async (e) => { console.log(await vfs.whatIs(`Root/Pluto/apps/${app.name}.app`)); if ( @@ -194,7 +194,7 @@ export default { for (let pkg of packageList) { const app = await appStoreModule.fetch(pkg); - console.log("pkgResult", app); + console.log(pkg, "pkgResult", app); const { appCompatibleColor, appCompatibleIcon } = getAppCompatibility(app.compatibleWith, sysInfo.version); @@ -274,7 +274,7 @@ export default { await vfs.readFile(`Root/Pluto/apps/${app.name}.app`) ); const appHash = await fetch( - `${host}pkgs/${pkg}/${app.assets.path}` + `${host}pkgs/${pkg}/${app.assets.path}?t=` + performance.now() ).then(async (e) => { return new Hashes.MD5().hex(await e.text()); }); diff --git a/pkgs/apps/AudioPlayer.js b/pkgs/apps/AudioPlayer.js new file mode 100644 index 0000000..cf373e5 --- /dev/null +++ b/pkgs/apps/AudioPlayer.js @@ -0,0 +1,106 @@ +export default { + name: "Audio Player", + description: "Listen to your music in this app.", + ver: 1, // Compatible with core v1 + type: "process", + exec: async function (Root) { + let wrapper; // Lib.html | undefined + let MyWindow; + + console.log("Hello from example package", Root.Lib); + + Root.Lib.setOnEnd(function () { + MyWindow.close(); + }); + const Win = (await Root.Lib.loadLibrary("WindowSystem")).win; + + MyWindow = new Win({ + title: "Audio Player", + pid: Root.PID, + onclose: () => { + Root.Lib.onEnd(); + }, + }); + + // initializing wrappers and vfs + wrapper = MyWindow.window.querySelector(".win-content"); + + const vfs = await Root.Lib.loadLibrary("VirtualFS"); + const FileDialog = await Root.Lib.loadLibrary("FileDialog"); + + await vfs.importFS(); + + wrapper.classList.add("with-sidebar", "row", "o-h", "h-100"); + + const Sidebar = await Root.Lib.loadComponent("Sidebar"); + + // this function opens the file and changes the title to the file name, + // we load the file into a buffer + async function openFile(path) { + let file; + if (path) file = path; + else file = await FileDialog.pickFile("Root"); + if (file === false) return; + let result = updateAudio(await vfs.readFile(file)); + if (result === false) return; + MyWindow.window.querySelector(".win-titlebar .title").innerText = + "Audio Player - " + file.split("/").pop(); + MyWindow.focus(); + } + + // creates sidebar + Sidebar.new(wrapper, [ + { + onclick: async (_) => { + openFile(); + }, + html: Root.Lib.icons.fileAudio, + title: "Select Audio...", + }, + { + style: { + "margin-top": "auto", + }, + onclick: (_) => { + alert("Not implemented"); + }, + html: Root.Lib.icons.help, + title: "Help", + }, + ]); + + // creates the wrapper that the image is in + let vidWrapper = new Root.Lib.html("div") + .class("ovh", "fg", "fc", "row") + .appendTo(wrapper); + + // creates the actual img element + let img = new Root.Lib.html("audio") + .appendTo(vidWrapper) + .style({ + width: "100%", + "object-fit": "contain", + border: "none", + }) + .attr({ draggable: "false", controls: 'on' }); + + // updates the video on the next load + function updateAudio(content) { + if (!content.startsWith("data:audio/") && !content.startsWith("blob:")) { + Root.Modal.alert("Error", "This does not look like an audio file").then( + (_) => { + MyWindow.focus(); + } + ); + return false; + } + img.elm.src = content; + } + + return Root.Lib.setupReturns((m) => { + if (typeof m === "object" && m.type && m.type === "loadFile" && m.path) { + openFile(m.path); + } + }); + }, +}; diff --git a/pkgs/apps/FileManager.js b/pkgs/apps/FileManager.js index d3e348b..ac5b4e3 100644 --- a/pkgs/apps/FileManager.js +++ b/pkgs/apps/FileManager.js @@ -66,6 +66,7 @@ export default { if (result === false) return; result = result.replace(/\//g, ""); await vfs.createFolder(path + "/" + result); + renderFileList(path); }, html: L.icons.createFolder, title: "Create Folder", @@ -80,6 +81,7 @@ export default { if (result === false) return; result = result.replace(/\//g, ""); await vfs.writeFile(path + "/" + result, ""); + renderFileList(path); }, html: L.icons.createFile, title: "Create File", @@ -101,6 +103,34 @@ export default { html: L.icons.dir, title: "Go to Folder", }, + { + onclick: async (_) => { + if (!selectedItem) return; + let i = await vfs.whatIs(selectedItem); + if (i === "dir") + return Root.Modal.alert( + "Error", + "Folder download is not yet supported.", + wrapper + ); + let text = await vfs.readFile(selectedItem); + + // boilerplate download code + var element = document.createElement("a"); + element.setAttribute( + "href", + "data:text/plain;charset=utf-8," + encodeURIComponent(text) + ); + element.setAttribute("download", selectedItem.split("/").pop()); + element.style.display = "none"; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + renderFileList(path); + }, + html: L.icons.download, + title: "Download File", + }, { onclick: (_) => { var input = new Root.Lib.html("input").elm; @@ -116,13 +146,26 @@ export default { file.type.startsWith("audio") || file.type.startsWith("video") ) { + console.log(file); // read as arraybuffer; store as base64 - reader.readAsDataURL(file); + // reader.readAsDataURL(file); + reader.readAsArrayBuffer(file); // here we tell the reader what to do when it's done reading... reader.onload = async (readerEvent) => { - var content = readerEvent.target.result; // this is the content! - await vfs.writeFile(`Root/${file.name}`, content); + // var content = readerEvent.target.result; // this is the content! + const blob = new Blob([readerEvent.target.result], { type: file.type }); + + const filePath = `${Root.Lib.randomString()}-${file.name}`; + + await localforage.setItem(filePath, blob); + + await vfs.writeFile( + `${path}/${file.name}`, + `vfsImport:${filePath}` + ); + + renderFileList(path); }; } else { // read as text @@ -131,38 +174,25 @@ export default { // here we tell the reader what to do when it's done reading... reader.onload = async (readerEvent) => { var content = readerEvent.target.result; // this is the content! - await vfs.writeFile(`${path}/${file.name}`, content); + + const filePath = `${Root.Lib.randomString()}-${file.name}`; + + await localforage.setItem(filePath, content); + + await vfs.writeFile( + `${path}/${file.name}`, + `vfsImport:${filePath}` + ); + + renderFileList(path); }; } }; input.click(); }, - html: L.icons.import, - title: "Import file from your system", - }, - { - onclick: async (_) => { - if (!selectedItem) return; - let i = await vfs.whatIs(selectedItem); - if (i === "dir") - return Root.Modal.alert("Cannot download folders at the moment."); - let text = await vfs.readFile(selectedItem); - - // boilerplate download code - var element = document.createElement("a"); - element.setAttribute( - "href", - "data:text/plain;charset=utf-8," + encodeURIComponent(text) - ); - element.setAttribute("download", selectedItem.split('/').pop()); - element.style.display = "none"; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - }, - html: L.icons.download, - title: "Download File", + html: L.icons.upload, + title: "Upload File from Host", }, { onclick: async (_) => { @@ -176,6 +206,7 @@ export default { ); if (result === true) { await vfs.delete(selectedItem); + renderFileList(path); } }, html: L.icons.delete, @@ -230,7 +261,7 @@ export default { if (isFolder !== "dir") { path = "Root/"; - return renderFileList(); + return renderFileList(path); } // return renderFileList(await vfs.getParentFolder(folder)); diff --git a/pkgs/apps/ImageViewer.js b/pkgs/apps/ImageViewer.js index f7a6f81..8ea9dcd 100644 --- a/pkgs/apps/ImageViewer.js +++ b/pkgs/apps/ImageViewer.js @@ -87,7 +87,7 @@ export default { // updates the image on the next load function updateImage(content) { - if (!content.startsWith("data:image/")) { + if (!content.startsWith("data:image/") && !content.startsWith("blob:")) { Root.Modal.alert("Error", "This does not look like an image").then( (_) => { MyWindow.focus(); diff --git a/pkgs/apps/Settings.js b/pkgs/apps/Settings.js index 54d21d8..ccb8232 100644 --- a/pkgs/apps/Settings.js +++ b/pkgs/apps/Settings.js @@ -312,7 +312,7 @@ export default { Root.Modal.alert( "Oops", "Something went wrong while logging in:\n\n" + - JSON.stringify(result, null, 2), + JSON.stringify(result, null, 2), settingsWin ); } @@ -385,9 +385,32 @@ export default { .class("card-box", "max") .appendTo(container); - const filesystemSize = - ((await localforage.getItem("fs")).length / 1024).toFixed(0) + - " KB"; + let allKeys = await localforage.keys(); + let totalStorage = 0; + for (let i = 0; i < allKeys.length; i++) { + let value = await localforage.getItem(allKeys[i]); + + if (typeof value === 'string') { + totalStorage += value.length; + } else if (value instanceof Blob) { + totalStorage += value.size; + } + } + + console.log(totalStorage); + + let filesystemSize; + + if (totalStorage < 1024) { + filesystemSize = totalStorage + " B"; + } else if (totalStorage < 1024 * 1024) { + filesystemSize = (totalStorage / 1024).toFixed(2) + " KB"; + } else if (totalStorage < 1024 * 1024 * 1024) { + filesystemSize = (totalStorage / 1024 / 1024).toFixed(1) + " MB"; + } else { + filesystemSize = + (totalStorage / 1024 / 1024 / 1024).toFixed(1) + " GB"; + } makeHeading("h2", Root.Lib.getString("yourDevice")); @@ -445,7 +468,9 @@ export default { os.version = userAgent.match(/Windows NT ([\d.]+)/)[1]; } else if (userAgent.indexOf("Mac") > -1) { os.name = "macOS"; - os.version = userAgent.match(/Mac OS X ([\d_.]+)/)[1].replace(/_/g, ".") + os.version = userAgent + .match(/Mac OS X ([\d_.]+)/)[1] + .replace(/_/g, "."); } else if (userAgent.indexOf("Android") > -1) { os.name = "Android"; os.version = userAgent.match(/Android ([\d.]+)/)[1]; @@ -461,8 +486,8 @@ export default { os.version = parseFloat(os.version); - if (os.name === 'macOS' && os.version === '10.15') { - os.version = 'X'; + if (os.name === "macOS" && os.version === "10.15") { + os.version = "X"; } if (isNaN(os.version)) os.version = ""; @@ -614,7 +639,7 @@ export default { .filter((r) => r.type === "file" && r.item.endsWith(".theme")) .map((r) => r.item); - await themeFileList.forEach(async (itm) => { + await Promise.all(themeFileList.map(async (itm) => { const theme = await vfs.readFile( `Root/Pluto/config/themes/${itm}` ); @@ -630,9 +655,12 @@ export default { } else { alert("failed parsing theme data due to " + result.message); } - }); + console.log("theme parsing", itm, result); + })); } + console.log(JSON.parse(JSON.stringify(themes))); + new Html("select") .appendMany(...themes) .on("input", (e) => { @@ -733,8 +761,7 @@ export default { .appendMany( new Html("option").text("Full").attr({ value: "full", - selected: - desktopConfig.dockStyle === "full" ? true : null, + selected: desktopConfig.dockStyle === "full" ? true : null, }), new Html("option").text("Compact").attr({ value: "compact", @@ -856,9 +883,9 @@ export default { Root.Modal.alert( "Failed", "Network is not working. Status code: " + - req1.status + - ", " + - req2.status + req1.status + + ", " + + req2.status ); } }) @@ -940,14 +967,14 @@ export default { new Html("td").appendMany( dc[i].dangerous === true ? new Html("button") - .text("Delete") - .on("click", async (_) => { - await dc[i].delete(); - await performSecurityScan(); - }) + .text("Delete") + .on("click", async (_) => { + await dc[i].delete(); + await performSecurityScan(); + }) : new Html("button") - .attr({ disabled: true }) - .text("Delete") + .attr({ disabled: true }) + .text("Delete") ) ) ) diff --git a/pkgs/apps/VideoPlayer.js b/pkgs/apps/VideoPlayer.js new file mode 100644 index 0000000..27a5679 --- /dev/null +++ b/pkgs/apps/VideoPlayer.js @@ -0,0 +1,107 @@ +export default { + name: "Video Player", + description: "View your videos in this app.", + ver: 1, // Compatible with core v1 + type: "process", + exec: async function (Root) { + let wrapper; // Lib.html | undefined + let MyWindow; + + console.log("Hello from example package", Root.Lib); + + Root.Lib.setOnEnd(function () { + MyWindow.close(); + }); + const Win = (await Root.Lib.loadLibrary("WindowSystem")).win; + + MyWindow = new Win({ + title: "Video Player", + pid: Root.PID, + onclose: () => { + Root.Lib.onEnd(); + }, + }); + + // initializing wrappers and vfs + wrapper = MyWindow.window.querySelector(".win-content"); + + const vfs = await Root.Lib.loadLibrary("VirtualFS"); + const FileDialog = await Root.Lib.loadLibrary("FileDialog"); + + await vfs.importFS(); + + wrapper.classList.add("with-sidebar", "row", "o-h", "h-100"); + + const Sidebar = await Root.Lib.loadComponent("Sidebar"); + + // this function opens the file and changes the title to the file name, + // we load the file into a buffer + async function openFile(path) { + let file; + if (path) file = path; + else file = await FileDialog.pickFile("Root"); + if (file === false) return; + let result = updateVideo(await vfs.readFile(file)); + if (result === false) return; + MyWindow.window.querySelector(".win-titlebar .title").innerText = + "Video Player - " + file.split("/").pop(); + MyWindow.focus(); + } + + // creates sidebar + Sidebar.new(wrapper, [ + { + onclick: async (_) => { + openFile(); + }, + html: Root.Lib.icons.fileVideo, + title: "Select Video...", + }, + { + style: { + "margin-top": "auto", + }, + onclick: (_) => { + alert("Not implemented"); + }, + html: Root.Lib.icons.help, + title: "Help", + }, + ]); + + // creates the wrapper that the image is in + let vidWrapper = new Root.Lib.html("div") + .class("ovh", "fg", "fc", "row") + .appendTo(wrapper); + + // creates the actual img element + let img = new Root.Lib.html("video") + .appendTo(vidWrapper) + .style({ + width: "100%", + height: "100%", + "object-fit": "contain", + border: "none", + }) + .attr({ draggable: "false", controls: 'on' }); + + // updates the video on the next load + function updateVideo(content) { + if (!content.startsWith("data:video/") && !content.startsWith("blob:")) { + Root.Modal.alert("Error", "This does not look like a video file").then( + (_) => { + MyWindow.focus(); + } + ); + return false; + } + img.elm.src = content; + } + + return Root.Lib.setupReturns((m) => { + if (typeof m === "object" && m.type && m.type === "loadFile" && m.path) { + openFile(m.path); + } + }); + }, +}; diff --git a/pkgs/lib/FileMappings.js b/pkgs/lib/FileMappings.js index bafd649..ac7769c 100644 --- a/pkgs/lib/FileMappings.js +++ b/pkgs/lib/FileMappings.js @@ -74,10 +74,52 @@ export default { icon: "fileImage", }, mp4: { - type: "image", + type: "video", label: "MP4 video", - opensWith: "apps:ImageViewer", - icon: "fileImage", + opensWith: "apps:VideoPlayer", + icon: "fileVideo", + }, + mov: { + type: "video", + label: "MOV video", + opensWith: "apps:VideoPlayer", + icon: "fileVideo", + }, + mkv: { + type: "video", + label: "MKV video", + opensWith: "apps:VideoPlayer", + icon: "fileVideo", + }, + avi: { + type: "video", + label: "AVI video", + opensWith: "apps:VideoPlayer", + icon: "fileVideo", + }, + webm: { + type: "video", + label: "WebM video", + opensWith: "apps:VideoPlayer", + icon: "fileVideo", + }, + wav: { + type: "audio", + label: "WAV audio", + opensWith: "apps:AudioPlayer", + icon: "fileAudio", + }, + m4a: { + type: "audio", + label: "MPEG audio", + opensWith: "apps:AudioPlayer", + icon: "fileAudio", + }, + mp3: { + type: "audio", + label: "MP3 audio", + opensWith: "apps:AudioPlayer", + icon: "fileAudio", }, shrt: { type: "text", diff --git a/pkgs/lib/ThemeLib.js b/pkgs/lib/ThemeLib.js index b6aebac..d1687af 100644 --- a/pkgs/lib/ThemeLib.js +++ b/pkgs/lib/ThemeLib.js @@ -5,6 +5,10 @@ let Core = {}; const CURSOR_DEFAULT = `data:image/svg+xml,%3Csvg width='20' height='26' viewBox='0 0 20 26' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1104_663)'%3E%3Cpath d='M14.5 15.5L2 3V19.5L5.5 17L7.30828 21.9728C7.41041 22.2536 7.73448 22.3828 8.00178 22.2491L11.0885 20.7057C11.3211 20.5895 11.4257 20.3143 11.3291 20.0728L9.5 15.5H14.5Z' fill='black'/%3E%3Cpath d='M14.5 15.5L2 3V19.5L5.5 17L7.30828 21.9728C7.41041 22.2536 7.73448 22.3828 8.00178 22.2491L11.0885 20.7057C11.3211 20.5895 11.4257 20.3143 11.3291 20.0728L9.5 15.5H14.5Z' stroke='white' stroke-width='1.5'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1104_663' x='0.55' y='0.489453' width='18.4605' height='25.2633' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1104_663'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1104_663' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E`; const CURSOR_POINTER = `data:image/svg+xml,%3Csvg width='23' height='26' viewBox='0 0 23 26' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1104_682)'%3E%3Cpath d='M2.36495 13.635L2.17678 13.8232C2.06359 13.9364 2 14.0899 2 14.25C2 14.4101 2.06359 14.5636 2.17678 14.6768L6.5 19L8.5 21L9.35355 21.8536C9.44732 21.9473 9.5745 22 9.70711 22H17C19.2793 21.9375 19.0003 21.9999 19.4992 20.0032C19.4997 20.0012 19.5 19.9989 19.5 19.9968V11.6706C19.5 10.0694 17.4416 9.41611 16.5182 10.7242C16.5126 10.7322 16.5 10.7282 16.5 10.7185V10C16.5 9.01608 15.5361 8.32131 14.6026 8.63246L13.5182 8.99394C13.5092 8.99692 13.5 8.99026 13.5 8.98083V8C13.5 7.36506 12.9033 6.89917 12.2873 7.05317L10.5182 7.49545C10.5089 7.49776 10.5 7.49078 10.5 7.48126V6.75V3.25C10.5 2.55964 9.94036 2 9.25 2H9C8.44772 2 8 2.44772 8 3V15.4254C8 15.4597 7.96394 15.482 7.93328 15.4666L3.80328 13.4016C3.32357 13.1618 2.7442 13.2558 2.36495 13.635Z' fill='black'/%3E%3Cpath d='M10.5 11.5V6.75M10.5 6.75V3.25C10.5 2.55964 9.94036 2 9.25 2H9C8.44772 2 8 2.44772 8 3V3V15.4254C8 15.4597 7.96394 15.482 7.93328 15.4666L3.80328 13.4016C3.32357 13.1618 2.7442 13.2558 2.36495 13.635L2.17678 13.8232C2.06359 13.9364 2 14.0899 2 14.25V14.25C2 14.4101 2.06359 14.5636 2.17678 14.6768L6.5 19L8.5 21L9.35355 21.8536C9.44732 21.9473 9.5745 22 9.70711 22H17C19.2793 21.9375 19.0003 21.9999 19.4992 20.0032C19.4997 20.0012 19.5 19.9989 19.5 19.9968V11.6706C19.5 10.0694 17.4416 9.41611 16.5182 10.7242V10.7242C16.5126 10.7322 16.5 10.7282 16.5 10.7185V10M10.5 6.75V7.48126C10.5 7.49078 10.5089 7.49776 10.5182 7.49545L12.2873 7.05317C12.9033 6.89917 13.5 7.36506 13.5 8V8M13.5 8V10.5V11.5M13.5 8V8.98083C13.5 8.99026 13.5092 8.99692 13.5182 8.99394L14.6026 8.63246C15.5361 8.32131 16.5 9.01608 16.5 10V10M16.5 10V11.5' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1104_682' x='0.55' y='0.55' width='22.4' height='24.9' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1104_682'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1104_682' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E`; const CURSOR_TEXT = `data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_1118_622)'%3E%3Cg filter='url(%23filter0_d_1118_622)'%3E%3Cpath d='M14 1H10.5H7V3.07692L9.1 4.07692V15.9231L7 16.9231V19H14V16.9231L11.9 15.9231V4.07692L14 3.07692V1Z' fill='black'/%3E%3Cpath d='M14 1H10.5H7V3.07692L9.1 4.07692V15.9231L7 16.9231V19H14V16.9231L11.9 15.9231V4.07692L14 3.07692V1Z' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1118_622' x='5.55' y='-0.45' width='11.9' height='22.9' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1118_622'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1118_622' result='shape'/%3E%3C/filter%3E%3CclipPath id='clip0_1118_622'%3E%3Crect width='20' height='20' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E`; +const CURSOR_NS = `data:image/svg+xml,%3Csvg width='20' height='26' viewBox='0 0 20 26' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1266_626)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6 9H4.71429H3L9 2L15 9H12V15H15L9 22L3 15H4.71429H6V9Z' fill='black'/%3E%3Cpath d='M6 9H6.75V8.25H6V9ZM3 9L2.43056 8.51191L1.36933 9.75H3V9ZM9 2L9.56944 1.51191L9 0.847557L8.43056 1.51191L9 2ZM15 9V9.75H16.6307L15.5694 8.51191L15 9ZM12 9V8.25H11.25V9H12ZM12 15H11.25V15.75H12V15ZM15 15L15.5694 15.4881L16.6307 14.25H15V15ZM9 22L8.43056 22.4881L9 23.1524L9.56944 22.4881L9 22ZM3 15V14.25H1.36933L2.43056 15.4881L3 15ZM6 15V15.75H6.75V15H6ZM4.71429 9.75H6V8.25H4.71429V9.75ZM3 9.75H4.71429V8.25H3V9.75ZM8.43056 1.51191L2.43056 8.51191L3.56944 9.48809L9.56944 2.48809L8.43056 1.51191ZM15.5694 8.51191L9.56944 1.51191L8.43056 2.48809L14.4306 9.48809L15.5694 8.51191ZM12 9.75H15V8.25H12V9.75ZM11.25 9V15H12.75V9H11.25ZM12 15.75H15V14.25H12V15.75ZM14.4306 14.5119L8.43056 21.5119L9.56944 22.4881L15.5694 15.4881L14.4306 14.5119ZM9.56944 21.5119L3.56944 14.5119L2.43056 15.4881L8.43056 22.4881L9.56944 21.5119ZM3 15.75H4.71429V14.25H3V15.75ZM4.71429 15.75H6V14.25H4.71429V15.75ZM6.75 15V9H5.25V15H6.75Z' fill='white'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1266_626' x='0.669385' y='0.147656' width='18.6612' height='25.7047' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1266_626'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1266_626' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E`; +const CURSOR_EW = `data:image/svg+xml,%3Csvg width='26' height='20' viewBox='0 0 26 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1266_631)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M9 12L9 13.2857L9 15L2 9L9 3L9 6L15 6L15 3L22 9L15 15L15 13.2857L15 12L9 12Z' fill='black'/%3E%3Cpath d='M9 12L9 11.25L8.25 11.25L8.25 12L9 12ZM9 15L8.51191 15.5694L9.75 16.6307L9.75 15L9 15ZM2 9L1.51191 8.43056L0.847557 9L1.51191 9.56944L2 9ZM9 3L9.75 3L9.75 1.36933L8.51191 2.43056L9 3ZM9 6L8.25 6L8.25 6.75L9 6.75L9 6ZM15 6L15 6.75L15.75 6.75L15.75 6L15 6ZM15 3L15.4881 2.43056L14.25 1.36933L14.25 3L15 3ZM22 9L22.4881 9.56944L23.1524 9L22.4881 8.43056L22 9ZM15 15L14.25 15L14.25 16.6307L15.4881 15.5694L15 15ZM15 12L15.75 12L15.75 11.25L15 11.25L15 12ZM9.75 13.2857L9.75 12L8.25 12L8.25 13.2857L9.75 13.2857ZM9.75 15L9.75 13.2857L8.25 13.2857L8.25 15L9.75 15ZM1.51191 9.56944L8.51191 15.5694L9.48809 14.4306L2.48809 8.43056L1.51191 9.56944ZM8.51191 2.43056L1.51191 8.43056L2.48809 9.56944L9.48809 3.56944L8.51191 2.43056ZM9.75 6L9.75 3L8.25 3L8.25 6L9.75 6ZM9 6.75L15 6.75L15 5.25L9 5.25L9 6.75ZM15.75 6L15.75 3L14.25 3L14.25 6L15.75 6ZM14.5119 3.56944L21.5119 9.56944L22.4881 8.43056L15.4881 2.43056L14.5119 3.56944ZM21.5119 8.43056L14.5119 14.4306L15.4881 15.5694L22.4881 9.56944L21.5119 8.43056ZM15.75 15L15.75 13.2857L14.25 13.2857L14.25 15L15.75 15ZM15.75 13.2857L15.75 12L14.25 12L14.25 13.2857L15.75 13.2857ZM15 11.25L9 11.25L9 12.75L15 12.75L15 11.25Z' fill='white'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1266_631' x='0.147656' y='0.669141' width='25.7047' height='18.6617' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1266_631'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1266_631' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E`; +const CURSOR_NWSE = `data:image/svg+xml,%3Csvg width='21' height='18' viewBox='0 0 21 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1266_655)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M5.27119 8.33425L4.43618 9.31192L3.32284 10.6155L1.89669 1.5069L11.1162 1.4906L9.16787 3.77182L13.7303 7.66851L15.6787 5.38729L17.1048 14.4959L7.88528 14.5122L8.99862 13.2086L9.83362 12.2309L5.27119 8.33425Z' fill='black'/%3E%3Cpath d='M5.27119 8.33425L5.75827 7.76395L5.18797 7.27686L4.70088 7.84717L5.27119 8.33425ZM3.32284 10.6155L2.58187 10.7315L2.83411 12.3425L3.89315 11.1026L3.32284 10.6155ZM1.89669 1.5069L1.89536 0.756901L1.02036 0.758448L1.15572 1.62292L1.89669 1.5069ZM11.1162 1.4906L11.6865 1.97769L12.7456 0.737719L11.1149 0.740602L11.1162 1.4906ZM9.16787 3.77182L8.59757 3.28473L8.11048 3.85504L8.68079 4.34212L9.16787 3.77182ZM13.7303 7.66851L13.2432 8.23881L13.8135 8.7259L14.3006 8.15559L13.7303 7.66851ZM15.6787 5.38729L16.4196 5.27127L16.1674 3.66023L15.1083 4.9002L15.6787 5.38729ZM17.1048 14.4959L17.1061 15.2459L17.9811 15.2443L17.8458 14.3798L17.1048 14.4959ZM7.88528 14.5122L7.31497 14.0251L6.25594 15.265L7.8866 15.2622L7.88528 14.5122ZM9.83362 12.2309L10.4039 12.718L10.891 12.1477L10.3207 11.6606L9.83362 12.2309ZM5.00649 9.799L5.84149 8.82134L4.70088 7.84717L3.86588 8.82483L5.00649 9.799ZM3.89315 11.1026L5.00649 9.799L3.86588 8.82483L2.75254 10.1284L3.89315 11.1026ZM1.15572 1.62292L2.58187 10.7315L4.06381 10.4995L2.63766 1.39088L1.15572 1.62292ZM11.1149 0.740602L1.89536 0.756901L1.89801 2.2569L11.1175 2.2406L11.1149 0.740602ZM9.73818 4.2589L11.6865 1.97769L10.5459 1.00351L8.59757 3.28473L9.73818 4.2589ZM8.68079 4.34212L13.2432 8.23881L14.2174 7.0982L9.65496 3.20151L8.68079 4.34212ZM14.3006 8.15559L16.249 5.87437L15.1083 4.9002L13.16 7.18142L14.3006 8.15559ZM14.9377 5.5033L16.3638 14.6119L17.8458 14.3798L16.4196 5.27127L14.9377 5.5033ZM17.1035 13.7459L7.88395 13.7622L7.8866 15.2622L17.1061 15.2459L17.1035 13.7459ZM8.45558 14.9992L9.56892 13.6957L8.42831 12.7215L7.31497 14.0251L8.45558 14.9992ZM9.56892 13.6957L10.4039 12.718L9.26332 11.7439L8.42831 12.7215L9.56892 13.6957ZM10.3207 11.6606L5.75827 7.76395L4.7841 8.90456L9.34653 12.8012L10.3207 11.6606Z' fill='white'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1266_655' x='0.320264' y='0.0373046' width='20.3609' height='17.9273' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1266_655'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1266_655' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E`; +const CURSOR_NESW = `data:image/svg+xml,%3Csvg width='22' height='18' viewBox='0 0 22 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1266_641)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.333 3.77169L9.49802 2.79402L8.38468 1.49047L17.6042 1.50677L16.1781 10.6153L14.2297 8.33412L9.66727 12.2308L11.6156 14.512L2.39609 14.4957L3.82224 5.38716L4.93558 6.69071L5.77059 7.66838L10.333 3.77169Z' fill='black'/%3E%3Cpath d='M9.49802 2.79402L10.0683 2.30694L9.49802 2.79402ZM10.333 3.77169L10.8201 4.34199L11.3904 3.85491L10.9033 3.2846L10.333 3.77169ZM8.38468 1.49047L8.386 0.740473L6.75534 0.73759L7.81437 1.97756L8.38468 1.49047ZM17.6042 1.50677L18.3452 1.62279L18.4805 0.75832L17.6055 0.756773L17.6042 1.50677ZM16.1781 10.6153L15.6078 11.1024L16.6668 12.3424L16.919 10.7314L16.1781 10.6153ZM14.2297 8.33412L14.8 7.84704L14.3129 7.27673L13.7426 7.76382L14.2297 8.33412ZM9.66727 12.2308L9.18019 11.6605L8.60988 12.1476L9.09697 12.7179L9.66727 12.2308ZM11.6156 14.512L11.6143 15.262L13.245 15.2649L12.1859 14.0249L11.6156 14.512ZM2.39609 14.4957L1.65512 14.3797L1.51977 15.2442L2.39476 15.2457L2.39609 14.4957ZM3.82224 5.38716L4.39255 4.90007L3.33351 3.66011L3.08127 5.27114L3.82224 5.38716ZM4.93558 6.69071L4.36528 7.1778L4.93558 6.69071ZM5.77059 7.66838L5.20028 8.15546L5.68737 8.72577L6.25767 8.23868L5.77059 7.66838ZM8.92771 3.28111L9.76272 4.25878L10.9033 3.2846L10.0683 2.30694L8.92771 3.28111ZM7.81437 1.97756L8.92771 3.28111L10.0683 2.30694L8.95498 1.00339L7.81437 1.97756ZM17.6055 0.756773L8.386 0.740473L8.38335 2.24047L17.6029 2.25677L17.6055 0.756773ZM16.919 10.7314L18.3452 1.62279L16.8632 1.39076L15.4371 10.4993L16.919 10.7314ZM13.6594 8.82121L15.6078 11.1024L16.7484 10.1283L14.8 7.84704L13.6594 8.82121ZM13.7426 7.76382L9.18019 11.6605L10.1544 12.8011L14.7168 8.90443L13.7426 7.76382ZM9.09697 12.7179L11.0453 14.9991L12.1859 14.0249L10.2376 11.7437L9.09697 12.7179ZM11.6169 13.762L2.39742 13.7457L2.39476 15.2457L11.6143 15.262L11.6169 13.762ZM3.13706 14.6117L4.56322 5.50318L3.08127 5.27114L1.65512 14.3797L3.13706 14.6117ZM3.25194 5.87425L4.36528 7.1778L5.50589 6.20363L4.39255 4.90007L3.25194 5.87425ZM4.36528 7.1778L5.20028 8.15546L6.34089 7.18129L5.50589 6.20363L4.36528 7.1778ZM6.25767 8.23868L10.8201 4.34199L9.84594 3.20139L5.2835 7.09807L6.25767 8.23868Z' fill='white'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1266_641' x='0.819775' y='0.0373046' width='20.3607' height='17.9273' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1266_641'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1266_641' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E`; export default { name: "Theme Lib", @@ -215,6 +219,22 @@ export default { "--cursor-text", stringify(CURSOR_TEXT) ); + document.documentElement.style.setProperty( + "--cursor-resize-ns", + stringify(CURSOR_NS) + ); + document.documentElement.style.setProperty( + "--cursor-resize-ew", + stringify(CURSOR_EW) + ); + document.documentElement.style.setProperty( + "--cursor-resize-nwse", + stringify(CURSOR_NWSE) + ); + document.documentElement.style.setProperty( + "--cursor-resize-nesw", + stringify(CURSOR_NESW) + ); }, setWallpaper: async (wallpaper) => { if (wallpaper) { diff --git a/pkgs/lib/VirtualFS.js b/pkgs/lib/VirtualFS.js index 9734b9b..39bf514 100644 --- a/pkgs/lib/VirtualFS.js +++ b/pkgs/lib/VirtualFS.js @@ -48,10 +48,15 @@ const Vfs = { // The file system is represented as a nested object, where each key is a folder or file name // and the value is either a string (for file contents) or another object (for a subfolder) fileSystem: {}, + log(info) { + console.debug(`[Vfs] ${info}`); + }, async save(reason = "save") { await localforage.setItem("fs", JSON.stringify(this.fileSystem)); this.fileSystem = JSON.parse(await localforage.getItem("fs")); + this.log(reason); + // Global VFS Events document.dispatchEvent(new CustomEvent("pluto.vfs-refresh"), { detail: { reason }, @@ -98,6 +103,7 @@ const Vfs = { } current = current[part]; } + this.log(`whatIs ${path}`); if (typeof current !== "string") { return "dir"; } else { @@ -105,23 +111,60 @@ const Vfs = { } }, // Function to get the contents of a file at a given path - async readFile(path, fsObject = this.fileSystem) { - const parts = path.split("/"); - let current = fsObject; - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - if (typeof current[part] === "undefined") { - return null; + async readFile(path, fsObject = this.fileSystem, bypass = false) { + return new Promise(async (resolve, reject) => { + const parts = path.split("/"); + let current = fsObject; + this.log(`read ${path}`); + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (typeof current[part] === "undefined") { + return resolve(null); + } + current = current[part]; } - current = current[part]; - } - if (typeof current !== "string") { - return null; - } - return current; + if (typeof current !== "string") { + return resolve(null); + } + if (bypass === false) { + // special vfs import handler + if (current.startsWith("vfsImport:")) { + const vfsImportPath = current.substring(10); + if (vfsImportPath === "fs") + return resolve("vfsImportError:not-allowed"); + const item = await localforage.getItem(vfsImportPath); + + if (item !== null && item !== undefined) { + if (item instanceof Blob) { + if (item.size > 1024 * 1024 * 10) { + return resolve(URL.createObjectURL(item)); + } else { + let result = await new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = function (e) { + return resolve(e.target.result); + }; + + reader.readAsDataURL(item); + }); + return resolve(result); + } + } else { + return resolve(item); + } + } else { + return resolve("vfsImportFailed:" + vfsImportPath); + } + } + } + return resolve(current); + }); }, // Function to write to a file at a given path async writeFile(path, contents, fsObject = this.fileSystem) { + if (typeof contents !== "string") + throw new Error("Tried to write a non-string to a file."); const parts = path.split("/"); const filename = parts.pop(); let current = fsObject; @@ -132,7 +175,31 @@ const Vfs = { } current = current[part]; } - current[filename] = contents; + + // if file has a special + if (current[filename] !== undefined) { + // special vfs import handler + if (current[filename].startsWith("vfsImport:")) { + const vfsImportPath = current[filename].substring(10); + if (vfsImportPath === "fs") return "vfsImportError:not-allowed"; + await localforage.setItem(vfsImportPath, contents); + } else { + current[filename] = contents; + } + } else { + // if file is bigger than 8kb then put it into the special bin + if (contents.length > 8192) { + const vfsImportPath = Math.random().toString(36).substring(2); + if (vfsImportPath === "fs") return "vfsImportError:not-allowed"; + + // save link to file + await localforage.setItem(vfsImportPath, contents); + current[filename] = `vfsImport:${vfsImportPath}`; + } else { + // save normally + current[filename] = contents; + } + } this.save("write " + path); }, // Function to create a new folder at a given path @@ -163,7 +230,21 @@ const Vfs = { } parent = parent[part]; } + + // maybe use readFile here so we don't + // accidentally delete the key "fs" + const tmp = String(await this.readFile(path, this.fileSystem, true)); + console.log(tmp); + // if it's an import then handle it specially + if (tmp.startsWith("vfsImport:")) { + const tmpName = tmp.substring(10); + await localforage.removeItem(tmpName); + + this.log(`Deleted reference file "${tmpName}"`); + } + delete parent[filename]; + this.save("delete " + path); }, // Function to list all files and folders at a given path @@ -177,6 +258,7 @@ const Vfs = { } current = current[part]; } + this.log(`list ${path}`); const result = await Promise.all( Object.keys(current).map(async (m) => { return { item: m, type: await this.whatIs(path + "/" + m) }; diff --git a/pkgs/system/BootLoader.js b/pkgs/system/BootLoader.js index d8c4454..b51d3b8 100644 --- a/pkgs/system/BootLoader.js +++ b/pkgs/system/BootLoader.js @@ -24,7 +24,7 @@ export default { const ls = await Root.Core.startPkg("ui:LoadingScreen"); const FileMapping = await Root.Core.startPkg("lib:FileMappings"); // start loading screen - const lsg = ls.loader(); + const lsg = ls.loader(Root.Lib); try { const serviceReference = []; diff --git a/pkgs/ui/Desktop.js b/pkgs/ui/Desktop.js index ddce473..c537037 100644 --- a/pkgs/ui/Desktop.js +++ b/pkgs/ui/Desktop.js @@ -636,7 +636,6 @@ export default { } break; case "wsEvent": - console.log('wsevent', type, data); if (data.type === "focusedWindow") { if (data.data === undefined) return; const p = data.data.options.pid; diff --git a/pkgs/ui/LoadingScreen.js b/pkgs/ui/LoadingScreen.js index 4d83941..1d1965b 100644 --- a/pkgs/ui/LoadingScreen.js +++ b/pkgs/ui/LoadingScreen.js @@ -10,7 +10,7 @@ export default { lib = l; }, data: { - loader: function () { + loader: function (il = null) { const x = new lib.html("div") .html( 'loading...' @@ -18,6 +18,51 @@ export default { .class("loading-screen") .appendTo("body"); + setTimeout(() => { + if (x.elm !== null) { + // weird workaround + x.elm.style.setProperty("cursor", "auto", "important"); + x.append( + new lib.html("div") + .styleJs({ + marginTop: "0.75rem", + display: "flex", + justifyContent: "center", + alignItems: "center", + flexDirection: "column", + gap: "0.5rem", + }) + .text("Is it taking too long to load?") + .append( + new lib.html("div") + .style({ + display: "flex", + justifyContent: "center", + alignItems: "center", + gap: "0.5rem", + }) + .appendMany( + new lib.html("button") + .text("Bypass loading screen") + .on("click", () => { + x.cleanup(); + }), + new lib.html("button") + .text("Launch Basic Mode") + .on("click", () => { + if (il !== null) { + window.err = new Error( + "Manually got past loading screen." + ); + il.launch("system:Basic"); + } + }) + ) + ) + ); + } + }, 5000); + return x; }, }, diff --git a/style.css b/style.css index 070e8cc..2849064 100644 --- a/style.css +++ b/style.css @@ -15,16 +15,14 @@ body { color-scheme: dark; } -*:not( - .resize-grips, - .resize-grip, - input, - textarea, - button, - button *, - select, - select * - ) { +*:not(.resize-grips, + .resize-grip, + input, + textarea, + button, + button *, + select, + select *) { cursor: var(--cursor-default) 2 2, auto !important; } @@ -81,9 +79,6 @@ a { --short-animation-duration: 0.15s; --animation-duration: 0.45s; --long-animation-duration: 1s; - /* --cursor-default: url("data:image/svg+xml,%3Csvg width='20' height='26' viewBox='0 0 20 26' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1104_663)'%3E%3Cpath d='M14.5 15.5L2 3V19.5L5.5 17L7.30828 21.9728C7.41041 22.2536 7.73448 22.3828 8.00178 22.2491L11.0885 20.7057C11.3211 20.5895 11.4257 20.3143 11.3291 20.0728L9.5 15.5H14.5Z' fill='black'/%3E%3Cpath d='M14.5 15.5L2 3V19.5L5.5 17L7.30828 21.9728C7.41041 22.2536 7.73448 22.3828 8.00178 22.2491L11.0885 20.7057C11.3211 20.5895 11.4257 20.3143 11.3291 20.0728L9.5 15.5H14.5Z' stroke='white' stroke-width='1.5'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1104_663' x='0.55' y='0.489453' width='18.4605' height='25.2633' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1104_663'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1104_663' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E"); - --cursor-pointer: url("data:image/svg+xml,%3Csvg width='23' height='26' viewBox='0 0 23 26' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1104_682)'%3E%3Cpath d='M2.36495 13.635L2.17678 13.8232C2.06359 13.9364 2 14.0899 2 14.25C2 14.4101 2.06359 14.5636 2.17678 14.6768L6.5 19L8.5 21L9.35355 21.8536C9.44732 21.9473 9.5745 22 9.70711 22H17C19.2793 21.9375 19.0003 21.9999 19.4992 20.0032C19.4997 20.0012 19.5 19.9989 19.5 19.9968V11.6706C19.5 10.0694 17.4416 9.41611 16.5182 10.7242C16.5126 10.7322 16.5 10.7282 16.5 10.7185V10C16.5 9.01608 15.5361 8.32131 14.6026 8.63246L13.5182 8.99394C13.5092 8.99692 13.5 8.99026 13.5 8.98083V8C13.5 7.36506 12.9033 6.89917 12.2873 7.05317L10.5182 7.49545C10.5089 7.49776 10.5 7.49078 10.5 7.48126V6.75V3.25C10.5 2.55964 9.94036 2 9.25 2H9C8.44772 2 8 2.44772 8 3V15.4254C8 15.4597 7.96394 15.482 7.93328 15.4666L3.80328 13.4016C3.32357 13.1618 2.7442 13.2558 2.36495 13.635Z' fill='black'/%3E%3Cpath d='M10.5 11.5V6.75M10.5 6.75V3.25C10.5 2.55964 9.94036 2 9.25 2H9C8.44772 2 8 2.44772 8 3V3V15.4254C8 15.4597 7.96394 15.482 7.93328 15.4666L3.80328 13.4016C3.32357 13.1618 2.7442 13.2558 2.36495 13.635L2.17678 13.8232C2.06359 13.9364 2 14.0899 2 14.25V14.25C2 14.4101 2.06359 14.5636 2.17678 14.6768L6.5 19L8.5 21L9.35355 21.8536C9.44732 21.9473 9.5745 22 9.70711 22H17C19.2793 21.9375 19.0003 21.9999 19.4992 20.0032C19.4997 20.0012 19.5 19.9989 19.5 19.9968V11.6706C19.5 10.0694 17.4416 9.41611 16.5182 10.7242V10.7242C16.5126 10.7322 16.5 10.7282 16.5 10.7185V10M10.5 6.75V7.48126C10.5 7.49078 10.5089 7.49776 10.5182 7.49545L12.2873 7.05317C12.9033 6.89917 13.5 7.36506 13.5 8V8M13.5 8V10.5V11.5M13.5 8V8.98083C13.5 8.99026 13.5092 8.99692 13.5182 8.99394L14.6026 8.63246C15.5361 8.32131 16.5 9.01608 16.5 10V10M16.5 10V11.5' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1104_682' x='0.55' y='0.55' width='22.4' height='24.9' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1104_682'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1104_682' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E"); - --cursor-text: url("data:image/svg+xml,%3Csvg width='13' height='24' viewBox='0 0 13 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg filter='url(%23filter0_d_1104_683)'%3E%3Cpath d='M9 2H5.5H2V4.07692L4.1 5.07692V16.9231L2 17.9231V20H9V17.9231L6.9 16.9231V5.07692L9 4.07692V2Z' fill='black'/%3E%3Cpath d='M9 2H5.5H2V4.07692L4.1 5.07692V16.9231L2 17.9231V20H9V17.9231L6.9 16.9231V5.07692L9 4.07692V2Z' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_d_1104_683' x='0.55' y='0.55' width='11.9' height='22.9' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='1' dy='1'/%3E%3CfeGaussianBlur stdDeviation='0.85'/%3E%3CfeComposite in2='hardAlpha' operator='out'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3CfeBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow_1104_683'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow_1104_683' result='shape'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E"); */ } :root[data-theme="red"] { @@ -109,6 +104,7 @@ a { --neutral-focus: hsl(0, 27%, 20%); --outline: hsl(0, 23%, 22%); } + :root[data-theme="green"] { color-scheme: dark; --primary: hsl(131, 81%, 21%); @@ -195,9 +191,9 @@ body { } .win-window.closing { - animation: slide-down var(--animation-duration) var(--easing-function) - forwards; + animation: slide-down var(--animation-duration) var(--easing-function) forwards; } + .win-window.dragging { transition: none !important; } @@ -235,18 +231,21 @@ body { border: none; box-shadow: none; } -.win-window.max > .win-titlebar > .buttons > .win-btn.win-minimize { + +.win-window.max>.win-titlebar>.buttons>.win-btn.win-minimize { visibility: collapse; } -.desktop ~ .win-window.max { + +.desktop~.win-window.max { height: calc(100% - 42px - 12px) !important; } -.win-window.max > .win-titlebar, -.win-window.max > .win-content { + +.win-window.max>.win-titlebar, +.win-window.max>.win-content { border-radius: 0; } -.win-window.max > .resize-grips { +.win-window.max>.resize-grips { display: none; } @@ -255,12 +254,15 @@ body { height: auto !important; border-radius: 5px; } -.win-window.min > .win-titlebar { + +.win-window.min>.win-titlebar { border-radius: 5px; } + .win-window.min .win-content { display: none; } + .win-window.min .resize-grips { display: none; } @@ -276,17 +278,18 @@ body { flex-shrink: 0; } -.win-window:not(.focus) > .win-titlebar .title, -.win-window-decorative:not(.focus) > .win-titlebar .title { +.win-window:not(.focus)>.win-titlebar .title, +.win-window-decorative:not(.focus)>.win-titlebar .title { opacity: 0.5; } -.win-window:not(.focus) > .win-titlebar button, -.win-window-decorative:not(.focus) > .win-titlebar button { + +.win-window:not(.focus)>.win-titlebar button, +.win-window-decorative:not(.focus)>.win-titlebar button { filter: brightness(0.5); } -.win-window.focus > .win-titlebar, -.win-window-decorative.focus > .win-titlebar { +.win-window.focus>.win-titlebar, +.win-window-decorative.focus>.win-titlebar { background-color: var(--header); } @@ -307,6 +310,7 @@ body { min-width: 0; overflow: hidden; } + .win-titlebar .title { white-space: nowrap; text-overflow: ellipsis; @@ -340,6 +344,7 @@ body { .win-titlebar button:active { background-color: var(--text-alt) !important; } + .win-titlebar button:target, .win-titlebar button:focus { background-color: var(--text); @@ -368,9 +373,11 @@ body { .win-content.with-sidebar { padding: 0; } + [data-sidebar-type="horizontal"] .with-sidebar { flex-direction: column; } + .win-content { padding: 10px; flex: 1; @@ -380,6 +387,7 @@ body { border-bottom-right-radius: 5px; position: relative; } + .container { padding: 10px; } @@ -426,6 +434,7 @@ body { top: -1.5rem; left: -1.5rem; } + .win-window .resize-grips div { width: 1.5rem !important; height: 1.5rem !important; @@ -446,45 +455,52 @@ body { top: -6px; left: 0; width: 100% !important; - cursor: ns-resize; + cursor: var(--cursor-resize-ns) 8 10, auto !important; } + .win-window .resize-grips div[data-resize-type="right"] { top: 0 !important; right: -6px !important; height: 100% !important; - cursor: ew-resize !important; + cursor: var(--cursor-resize-ew) 10 6, auto !important; } + .win-window .resize-grips div[data-resize-type="bottom"] { bottom: -6px; left: 0; width: 100% !important; - cursor: ns-resize; + cursor: var(--cursor-resize-ns) 8 10, auto !important; } + .win-window .resize-grips div[data-resize-type="left"] { top: 0; left: -6px; height: 100% !important; - cursor: ew-resize; + cursor: var(--cursor-resize-ew) 10 6, auto !important; } + .win-window .resize-grips div[data-resize-type="top-right"] { top: -6px; right: -6px; - cursor: nesw-resize; + cursor: var(--cursor-resize-nesw) 8 8, auto !important; } + .win-window .resize-grips div[data-resize-type="bottom-right"] { bottom: -6px; right: -6px; - cursor: nwse-resize; + cursor: var(--cursor-resize-nwse) 8 8, auto !important; } + .win-window .resize-grips div[data-resize-type="bottom-left"] { bottom: -6px; left: -6px; - cursor: nesw-resize; + cursor: var(--cursor-resize-nesw) 8 8, auto !important; } + .win-window .resize-grips div[data-resize-type="top-left"] { top: -6px; left: -6px; - cursor: nwse-resize; + cursor: var(--cursor-resize-nwse) 8 8, auto !important; } .modal { @@ -500,13 +516,15 @@ body { left: 0; animation: fadeIn var(--animation-duration) linear; } + .modal.closing { animation: fadeOut var(--animation-duration) linear; } + .modal.closing .modal-content { - animation: shortSlideDown var(--animation-duration) var(--easing-function) - forwards; + animation: shortSlideDown var(--animation-duration) var(--easing-function) forwards; } + .modal-content { display: flex; flex-direction: column; @@ -519,6 +537,7 @@ body { overflow: clip; animation: shortSlideUp var(--animation-duration) var(--easing-function); } + .modal .modal-content .modal-header { padding: 8px; display: flex; @@ -527,6 +546,7 @@ body { flex-shrink: 0; border-radius: 5px 5px 0 0; } + .modal .modal-content .modal-body { padding: 16px 18px; background-color: var(--root); @@ -538,17 +558,21 @@ body { border-radius: 0 0 5px 5px; overflow: auto; } -.modal-body > :first-child { + +.modal-body> :first-child { flex: 1; white-space: pre-wrap; } + .modal-body .flex-group { justify-content: flex-end; } + .flex-group { display: flex; gap: 8px; } + button { padding: 4px 14px; border-radius: 8px; @@ -564,40 +588,51 @@ button { justify-content: center; align-items: center; } + button.small { padding: 4px 8px; } + button.square { padding: 4px; } + button:target, button:focus { background-color: var(--neutral); } + button:hover { background-color: var(--neutral); } + button.primary { background-color: var(--primary); color: var(--text-light); } + button.danger { background-color: var(--negative); color: var(--text-light); } + button.success { background-color: var(--positive); color: var(--text-light); } + .success { color: var(--positive-light); } + .danger { color: var(--negative-light); } + .warning { color: var(--warning-light); } + .info { color: var(--primary); } @@ -631,6 +666,7 @@ iframe { color: #999; font-weight: 500; } + .superscript { align-self: flex-start; } @@ -644,7 +680,12 @@ iframe { justify-content: center; align-items: center; gap: 6px; - z-index: 999; + z-index: 99999999999999999; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; cursor: none !important; } @@ -655,10 +696,6 @@ iframe { font-style: italic; } -.loading-screen * { - cursor: none !important; -} - .desktop { display: flex; } @@ -668,18 +705,22 @@ iframe { display: flex; flex-direction: row; } + .col { display: flex; flex-direction: column; } + .row-wrap { display: flex; flex-direction: row; flex-wrap: wrap; } + .padded { padding: 16px 18px; } + .pad { padding: 8px; } @@ -703,6 +744,7 @@ iframe { .w-100 { width: 100%; } + .h-100 { height: 100%; } @@ -778,18 +820,22 @@ table { border-collapse: collapse; border-spacing: 0; } -table > tbody > tr > td div { + +table>tbody>tr>td div { width: 24px; display: flex; justify-content: center; align-items: center; } + tr { transition: none !important; } + tr:hover { background-color: var(--header); } + thead::after { content: ""; position: absolute; @@ -800,22 +846,27 @@ thead::after { background-color: var(--outline); transition: background-color 1.5s var(--easing-function); } + thead { position: sticky; top: 0; background: var(--root); } + thead tr { height: 30px; } + th { padding: 4px 8px; font-weight: normal; text-align: left; } + td { padding: 4px 8px; } + .table-selected { background-color: var(--neutral) !important; color: var(--text); @@ -824,12 +875,14 @@ td { .sidebar { background-color: var(--unfocused); display: flex; - overflow-y: scroll; + /* overflow-y: scroll; */ + overflow-y: auto; padding: 4px; gap: 2px; min-width: 32px; flex-shrink: 0; } + [data-sidebar-type="horizontal"] .sidebar, [data-sidebar-type="horizontal"] .text-sidebar { flex-direction: row; @@ -838,6 +891,7 @@ td { flex-shrink: 0; width: 100%; } + .sidebar button { padding: 4px; display: flex; @@ -846,9 +900,11 @@ td { width: 32px; flex-shrink: 0; } + .sidebar button:hover { background-color: var(--neutral); } + .sidebar button:focus, .sidebar button:active { background-color: transparent; @@ -866,6 +922,7 @@ td { opacity: 1; pointer-events: all; } + 100% { transform: translateY(0); opacity: 1; @@ -898,17 +955,20 @@ td { opacity: 1; pointer-events: none; } + 100% { transform: translateY(0); opacity: 1; } } + @keyframes shortSlideDown { 0% { transform: translateY(0); opacity: 1; pointer-events: none; } + 100% { transform: translateY(25%) scale(0.85); opacity: 0; @@ -920,14 +980,17 @@ td { from { opacity: 0; } + to { opacity: 1; } } + @keyframes fadeOut { from { opacity: 1; } + to { opacity: 0; } @@ -952,43 +1015,36 @@ td { border: 1px solid var(--outline); box-shadow: 0px 0px 28px 2px rgba(0, 0, 0, 0.41); - animation: simpleSlideUp var(--long-animation-duration) var(--easing-function) - forwards; + animation: simpleSlideUp var(--long-animation-duration) var(--easing-function) forwards; } .desktop .dock.hiding { - animation: simpleSlideDown var(--long-animation-duration) - var(--easing-function) forwards; + animation: simpleSlideDown var(--long-animation-duration) var(--easing-function) forwards; } [data-dock-style="compact"] .desktop .dock { max-width: 680px; left: 50%; - animation: compactSimpleSlideUp var(--long-animation-duration) - var(--easing-function) forwards; + animation: compactSimpleSlideUp var(--long-animation-duration) var(--easing-function) forwards; } [data-dock-style="compact"] .desktop .dock.hiding { - animation: compactSimpleSlideDown var(--long-animation-duration) - var(--easing-function) forwards; + animation: compactSimpleSlideDown var(--long-animation-duration) var(--easing-function) forwards; } [data-dock-style="minimal"] .desktop .dock { width: max-content; max-width: 680px; left: 50%; - animation: compactSimpleSlideUp var(--long-animation-duration) - var(--easing-function) forwards; + animation: compactSimpleSlideUp var(--long-animation-duration) var(--easing-function) forwards; } [data-dock-style="minimal"] .desktop .dock.hiding { - animation: compactSimpleSlideDown var(--long-animation-duration) - var(--easing-function) forwards; + animation: compactSimpleSlideDown var(--long-animation-duration) var(--easing-function) forwards; } .desktop .dock.collapse { - animation: slideLeft var(--long-animation-duration) var(--easing-function) - forwards; + animation: slideLeft var(--long-animation-duration) var(--easing-function) forwards; } .desktop .dock .toolbar-button { @@ -1001,14 +1057,17 @@ td { flex-shrink: 0; outline: 0; } + .desktop .dock .toolbar-button:first-child { border-right: 1px solid var(--outline); border-radius: 8px 0 0 8px; } + .desktop .dock .toolbar-button:last-child { border-left: 1px solid var(--outline); border-radius: 0 8px 8px 0; } + .desktop .dock .dock-item { background-color: var(--root); padding: 4px 12px; @@ -1019,9 +1078,11 @@ td { flex-shrink: 0; border-radius: 0; } + .desktop .dock .dock-item.selected { background-color: var(--neutral); } + .desktop .dock .items { display: flex; flex: 1; @@ -1036,19 +1097,23 @@ td { word-wrap: break-word; white-space: pre-wrap; } + .list-item:not(:last-child) { border-top: 1px solid var(--outline) 54; } + .monospace { font-family: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace; } + .log-error, .log-warn { padding-left: 28px; position: relative; } + .log-error::before, .log-warn::before { content: ""; @@ -1065,6 +1130,7 @@ td { border-bottom: 1px solid #743e4c !important; color: #ffb3d2; } + .log-error::before { background-color: #ff3b6b; -webkit-mask: url("./assets/icons/error.svg") no-repeat center center; @@ -1072,12 +1138,14 @@ td { mask: url("./assets/icons/error.svg") no-repeat center center; mask-size: 100% 100%; } + .log-warn { background-color: #42381f; border-top: 1px solid #56562e !important; border-bottom: 1px solid #56562e !important; color: #fce2a1; } + .log-warn::before { background-color: #d7b600; -webkit-mask: url("./assets/icons/warn.svg") no-repeat center center; @@ -1170,6 +1238,7 @@ ul { .ace-tm .ace_support.ace_function { color: #dcdcaa; } + [data-theme="light"] .ace-tm .ace_support.ace_function { color: #29bd78; } @@ -1186,6 +1255,7 @@ ul { .ace-tm .ace_operator { color: #48befd; } + .ace-tm .ace_keyword.ace_operator { color: rgb(104, 118, 135); } @@ -1255,9 +1325,11 @@ ul { background: var(--unfocused); border: 0; } + .ace-tm.ace_multiselect .ace_selection.ace_start { box-shadow: 0 0 3px 0px white; } + .ace-tm .ace_marker-layer .ace_step { background: rgb(252, 255, 0); } @@ -1282,6 +1354,7 @@ ul { .ace-tm .ace_indent-guide { position: relative; } + .ace-tm .ace_indent-guide::before { content: ""; position: absolute; @@ -1311,6 +1384,7 @@ ul { border-color: var(--outline); color: var(--text); } + .ace_button:hover, .ace_searchbtn:hover { background-color: var(--neutral-focus); @@ -1404,6 +1478,12 @@ ul { transition-timing-function: var(--easing-function); } +*:empty { + transition-property: background-color, color, border-color; + transition-duration: 0.5s; + transition-timing-function: var(--easing-function); +} + #snap-preview { border: 1px solid var(--outline); z-index: 1; @@ -1432,6 +1512,7 @@ svg.lucide:not(.ignore-css) { padding: calc(1.08vmax) 1.5vmax; flex-wrap: wrap; } + .desktop-icon { display: flex; flex-direction: column; @@ -1441,16 +1522,18 @@ svg.lucide:not(.ignore-css) { width: 90px; border-radius: 8px; padding: 8px 12px; - animation: shortSlideUp var(--long-animation-duration) var(--easing-function) - forwards; + animation: shortSlideUp var(--long-animation-duration) var(--easing-function) forwards; } + .desktop-icon:hover { background-color: var(--header); } + .desktop-icon .icon { width: 48px; height: 48px; } + .desktop-icon .text-wrapper { display: flex; justify-content: center; @@ -1460,6 +1543,7 @@ svg.lucide:not(.ignore-css) { width: 100%; min-width: 0; } + .desktop-icon .text { text-align: center; text-shadow: 0 0 20px #000; @@ -1499,15 +1583,19 @@ svg.lucide:not(.ignore-css) { justify-content: center; align-items: center; } + .jc { justify-content: center; } + .js { justify-content: flex-start; } + .ac { align-items: center; } + .as { align-items: flex-start; } @@ -1520,11 +1608,9 @@ svg.lucide:not(.ignore-css) { flex-direction: column; height: 174px; padding: 10% 8%; - background-image: linear-gradient( - 0deg, + background-image: linear-gradient(0deg, rgba(var(--root-rgb), 0.8), - rgba(var(--root-rgb), 0.8) - ), + rgba(var(--root-rgb), 0.8)), var(--bg); background-size: cover; background-position: center; @@ -1561,6 +1647,7 @@ svg.lucide:not(.ignore-css) { font-weight: 600; text-transform: uppercase; } + .label-bar .bar { display: flex; flex: 1; @@ -1596,9 +1683,11 @@ svg.lucide:not(.ignore-css) { .gap { gap: 8px; } + .gap-mid { gap: 6px; } + .gap-small { gap: 4px; } @@ -1634,14 +1723,17 @@ hr { font-weight: bold; margin: 0; } + .h1 { font-size: 24px; font-weight: 600; } + .h2 { font-size: 18px; font-weight: 500; } + .h3 { font-size: 16px; font-weight: 600; @@ -1651,6 +1743,7 @@ hr { color: var(--label); font-size: 12px; } + .label-light { color: var(--label-light); font-size: 12px; @@ -1659,9 +1752,11 @@ hr { .if { display: inline-flex; } + .mc { width: max-content; } + .mhc { height: max-content; } @@ -1669,6 +1764,7 @@ hr { .fadeIn { animation: fadeIn var(--animation-duration) var(--easing-function) forwards; } + .fadeOut { animation: fadeOut var(--animation-duration) var(--easing-function) forwards; } @@ -1694,14 +1790,15 @@ hr { height: 50vh; } -.desktop .menu > .me { +.desktop .menu>.me { display: flex; align-items: center; gap: 10px; align-self: stretch; margin-top: 12px; } -.desktop .menu > .me .pfp { + +.desktop .menu>.me .pfp { width: 44px; height: 44px; border-radius: 50%; @@ -1710,14 +1807,16 @@ hr { background-position: center; --url: url("./assets/user-avatar.svg"); } -.desktop .menu > .me > .text { + +.desktop .menu>.me>.text { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; flex: 1 0 0; } -.desktop .menu > .me > .text > .subheading { + +.desktop .menu>.me>.text>.subheading { color: var(--label-light); font-size: 12px; font-style: normal; @@ -1737,6 +1836,7 @@ hr { .spacer .space:first-child { width: 12px; } + .spacer .space:last-child { flex: 1 0 0; } @@ -1747,7 +1847,7 @@ hr { background: var(--label); } -.desktop .menu > .apps { +.desktop .menu>.apps { display: flex; flex-direction: column; align-items: flex-start; @@ -1755,7 +1855,8 @@ hr { align-self: stretch; overflow: auto; } -.desktop .menu > .apps > .app { + +.desktop .menu>.apps>.app { display: flex; padding: 8px; align-items: center; @@ -1764,31 +1865,34 @@ hr { border-radius: 8px; cursor: pointer; } -.desktop .menu > .apps > .app:hover { + +.desktop .menu>.apps>.app:hover { background-color: var(--header); } -.desktop .menu > .apps > .app > .app-icon, -.desktop .menu > .apps > .app > .app-icon svg { +.desktop .menu>.apps>.app>.app-icon, +.desktop .menu>.apps>.app>.app-icon svg { width: 32px; height: 32px; stroke-width: 1.5px; } -.desktop .menu > .apps > .app > .app-text { + +.desktop .menu>.apps>.app>.app-text { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; } -.desktop .menu > .apps > .app > .app-text > .app-heading { +.desktop .menu>.apps>.app>.app-text>.app-heading { color: var(--text); font-size: 14px; font-style: normal; font-weight: 500; line-height: normal; } -.desktop .menu > .apps > .app > .app-text > .app-description { + +.desktop .menu>.apps>.app>.app-text>.app-description { color: var(--text-alt); font-size: 12px; font-style: normal; @@ -1800,30 +1904,37 @@ hr { from { transform: translateX(-120%); } + to { transform: translateX(0); } } + @keyframes menuClose { from { transform: translateX(0); } + to { transform: translateX(-150%); } } + @keyframes simpleSlideUp { from { transform: translateY(150%); } + to { transform: translateY(0); } } + @keyframes simpleSlideDown { from { transform: translateY(0); } + to { transform: translateY(150%); } @@ -1833,50 +1944,61 @@ hr { from { transform: translateY(120%); } + to { transform: translateY(0); } } + @keyframes compactMenuClose { from { transform: translateY(0); } + to { transform: translateY(150%); } } + @keyframes minimalMenuOpen { from { transform: translate(-50%, 120%); left: 50%; } + to { transform: translate(-50%, 0); left: 50%; } } + @keyframes minimalMenuClose { from { transform: translate(-50%, 0); left: 50%; } + to { transform: translate(-50%, 150%); left: 50%; } } + @keyframes compactSimpleSlideUp { from { transform: translate(-50%, 150%); } + to { transform: translate(-50%, 0); } } + @keyframes compactSimpleSlideDown { from { transform: translate(-50%, 0); } + to { transform: translate(-50%, 150%); } @@ -1885,17 +2007,17 @@ hr { .desktop .menu.opening { animation: menuOpen var(--animation-duration) var(--easing-function) forwards; } + .desktop .menu.closing { animation: menuClose var(--animation-duration) var(--easing-function) forwards; } [data-dock-style="compact"] .desktop .menu.opening { - animation: compactMenuOpen var(--animation-duration) var(--easing-function) - forwards; + animation: compactMenuOpen var(--animation-duration) var(--easing-function) forwards; } + [data-dock-style="compact"] .desktop .menu.closing { - animation: compactMenuClose var(--animation-duration) var(--easing-function) - forwards; + animation: compactMenuClose var(--animation-duration) var(--easing-function) forwards; } [data-dock-style="compact"] .desktop .menu { @@ -1903,12 +2025,11 @@ hr { } [data-dock-style="minimal"] .desktop .menu.opening { - animation: minimalMenuOpen var(--animation-duration) var(--easing-function) - forwards; + animation: minimalMenuOpen var(--animation-duration) var(--easing-function) forwards; } + [data-dock-style="minimal"] .desktop .menu.closing { - animation: minimalMenuClose var(--animation-duration) var(--easing-function) - forwards; + animation: minimalMenuClose var(--animation-duration) var(--easing-function) forwards; } [data-dock-style="minimal"] .desktop .menu { @@ -1928,9 +2049,11 @@ hr { max-width: fit-content; position: relative; } + .card-box.max { max-width: 240px; } + .blur { position: absolute; top: 0; @@ -1943,6 +2066,7 @@ hr { backdrop-filter: blur(10px) brightness(0.65); -webkit-backdrop-filter: blur(10px) brightness(0.65); } + .card-box .icon { width: 44px; height: 44px; @@ -1952,11 +2076,13 @@ hr { background-position: center; --url: linear-gradient(147deg, #005be3 0%, #3d00e3 42.71%); } + .card-box .text { display: flex; flex-direction: column; gap: 2px; } + .card-box .label { color: var(--label-light); font-size: 12px; @@ -2008,6 +2134,7 @@ hr { position: relative; max-width: fit-content; } + .alert::before { content: ""; position: absolute; @@ -2021,6 +2148,7 @@ hr { mask-repeat: no-repeat; mask-position: 12px center; } + .alert.warning { background-color: var(--warning-dark); color: var(--text); @@ -2034,9 +2162,11 @@ hr { border-radius: 8px; border: 1px solid var(--outline); } + .app-store .search-bar input { flex: 1; } + .app-store .app { display: flex; padding: 8px; @@ -2045,27 +2175,32 @@ hr { gap: 8px; flex-wrap: wrap; } + .app-store .app.in-menu { flex-direction: column; align-items: stretch; } + .app-store .app-meta { display: flex; align-items: center; gap: 8px; flex: 1; } + .app-store .app-text { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; } + .app-store .app-icon { width: 64px; height: 64px; border-radius: 50%; } + .app-store .app-buttons { display: flex; align-items: center; @@ -2082,10 +2217,12 @@ hr { background-repeat: no-repeat; /* border-radius: 8px; */ } + .app-store .app-banner.no-banner { background: var(--unfocused); position: relative; } + .app-store .app-banner.no-banner::after { content: ""; position: absolute; @@ -2111,6 +2248,7 @@ hr { transition: opacity 0.25s var(--easing-function); z-index: 99999; } + .window-box.hidden { opacity: 0; } @@ -2130,6 +2268,7 @@ hr { background-blend-mode: multiply; image-rendering: pixelated; */ } + #snap-indicator.animated { transition: opacity 0.15s linear, width 0.25s var(--easing-function), height 0.25s var(--easing-function), top 0.25s var(--easing-function), @@ -2140,10 +2279,12 @@ hr { font-size: 5vmax; font-weight: 600; } + .display-subheading { font-size: 1.5vmax; font-weight: 550; } + .display-padding { padding: 15vmax; } @@ -2166,6 +2307,7 @@ hr { .no-ui .input { outline: none; } + .no-ui .output { white-space: pre-wrap; } @@ -2211,4 +2353,4 @@ hr { .ctx-menu .item:hover, .ctx-menu .item.over { background-color: var(--header); -} +} \ No newline at end of file