├── .gitignore ├── .prettierignore ├── .vscode └── settings.json ├── CNAME ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── TODO.md ├── assets ├── alert.wav ├── banner.svg ├── browserhp.html ├── example-app.app.js ├── example-theme.json ├── fuse.mjs ├── gamecontroller.js ├── html.js ├── icons.js ├── icons │ ├── error.svg │ └── warn.svg ├── images │ ├── banner.svg │ ├── getitonpluto.png │ ├── screenshot.png │ ├── screenshot2.png │ ├── ss_dark.png │ ├── ss_light.png │ ├── thewebosalliancebanner.png │ ├── zeonbanner.png │ └── zeoncommunitybanner.png ├── localforage.min.js ├── notify.wav ├── pluto-logo.svg ├── semver.min.js ├── startup.wav ├── strings.js ├── user-avatar.svg └── wallpapers │ ├── green.jpg │ ├── grey.png │ ├── grey.svg │ ├── light.png │ ├── light2.png │ ├── purple.svg │ ├── red.png │ ├── space.png │ └── space2.png ├── core.js ├── docs ├── README.html ├── README.md ├── css-classes.html ├── css-classes.md ├── images │ ├── card.png │ ├── ctxMenu.png │ ├── menuBar.png │ ├── notificationWithButtons.png │ ├── notify.png │ ├── selectList_row.png │ ├── selectList_table.png │ ├── sidebar.png │ ├── textSidebar.png │ └── tooltip.png ├── libs-and-components.html ├── libs-and-components.md ├── localization.html ├── localization.md ├── pluto-markup-language.html ├── pluto-markup-language.md ├── virtualFS.html └── virtualFS.md ├── index.html ├── pkgs ├── apps │ ├── Admin.js │ ├── AppStore.js │ ├── AudioPlayer.js │ ├── Browser.js │ ├── BuildAKtat.js │ ├── Calculator.js │ ├── Cloudburst.js │ ├── Debug.js │ ├── DevEnv.js │ ├── EventViewer.js │ ├── Example.js │ ├── FTGSF.js │ ├── FileManager.js │ ├── Fps.js │ ├── ImageViewer.js │ ├── Notepad.js │ ├── PML.js │ ├── PanicViewer.js │ ├── PassiveReboot.js │ ├── Settings.js │ ├── TaskLauncher.js │ ├── TaskManager.js │ ├── Terminal.js │ ├── VideoPlayer.js │ ├── Weather.js │ └── Welcome.js ├── components │ ├── Card.js │ ├── DropDown.js │ ├── ImageButton.js │ ├── MenuBar.js │ ├── SelectList.js │ ├── Sidebar.js │ ├── TextSidebar.js │ └── Tooltip.js ├── lib │ ├── Assistant.js │ ├── CodeScanner.js │ ├── CtxMenu.js │ ├── FileDialog.js │ ├── FileMappings.js │ ├── Notify.js │ ├── NowPlaying.js │ ├── PanicParser.js │ ├── Registry.js │ ├── TestLib.js │ ├── ThemeLib.js │ ├── UserManagement.js │ ├── VirtualFS.js │ └── WindowSystem.js ├── services │ ├── Account.js │ └── OpenWeatherMap.js ├── system │ ├── Basic.js │ ├── BootLoader.js │ └── Console.js └── ui │ ├── ActualLoginScreen.js │ ├── Desktop.js │ ├── LoadingScreen.js │ ├── LockScreen.js │ ├── LoginScreen.js │ └── Modal.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Auto-generated by the Prettier Extension for Replit 2 | # node_modules is ignored by default 3 | .config 4 | .build 5 | .cache 6 | .upm 7 | .tmp 8 | .git -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5500, 3 | "liveServer.settings.ignoreFiles": [ 4 | ".vscode/**", 5 | "**/*.scss", 6 | "**/*.sass", 7 | "**/*.ts", 8 | "**/*.md", 9 | "**/*.svg" 10 | ], 11 | "cSpell.words": [ 12 | "Baro", 13 | "colspan", 14 | "FTGSF", 15 | "KFWS", 16 | "Ktat", 17 | "localforage", 18 | "lucide", 19 | "NOAA", 20 | "pkgs", 21 | "Procs", 22 | "shrt", 23 | "titlebar", 24 | "yxxx", 25 | "Zeon" 26 | ], 27 | "cSpell.ignorePaths": [ 28 | "package-lock.json", 29 | "node_modules", 30 | "vscode-extension", 31 | ".git/objects", 32 | ".vscode", 33 | ".vscode-insiders", 34 | "style.css" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | pluto-app.zeon.dev 2 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | - kat21 (https://github.com/datkat21) 4 | - itslap (https://github.com/itslap) 5 | - exeon (https://github.com/actuallyexeon) 6 | - F_Zone (https://discord.com/users/689103439980003359) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Pluto v1.6.2

2 | 3 | ![Pluto banner](assets/images/banner.svg) 4 | 5 |
6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 |

15 | 16 | Try the live demo ↗ 17 | 18 |
19 | 20 |

Pluto is a minimal but functional "web OS" that aims to provide a desktop environment-like experience inside of a website.

21 | 22 | 23 | 24 | 25 | 26 | 27 | ## Table of Contents 28 | 29 | - [Table of Contents](#table-of-contents) 30 | - [Features](#features) 31 | - [Documentation](#documentation) 32 | - [Contributing](#contributing) 33 | 34 | ## Features 35 | 36 | Pluto has several key features some of which include: 37 | 38 | - Clean and minimal UI design, focused on personalization 39 | - Simple file system 40 | - Easy to create your own apps 41 | 42 | ## Documentation 43 | 44 | You can find documentation in the [docs](docs/) folder. 45 | 46 | ## Contributing 47 | 48 | When contributing to Pluto, please consider the following: 49 | 50 | - It is recommended to use [VS Code](https://code.visualstudio.com) along with the Live Server and Prettier extensions for the best experience. 51 | - Following these guidelines increases the chance of your pull request being approved. 52 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | [docs/virtualFS.md](docs/virtualFS.md) 4 | [docs/libs-and-components.md](docs/libs-and-components.md) 5 | -------------------------------------------------------------------------------- /assets/alert.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/alert.wav -------------------------------------------------------------------------------- /assets/banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/browserhp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Browser Homepage 5 | 6 | 10 | 16 | Pluto 17 | 18 | 19 | 23 | 29 | 30 | 31 | 32 | 103 | 104 | 105 |
106 |

Pluto Browser

107 |
108 |
109 | 115 | 116 | 117 | 118 |
119 |
120 |
121 | 122 | 123 | -------------------------------------------------------------------------------- /assets/example-theme.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "name": "Purple", 4 | "description": "A custom theme.", 5 | "values": { 6 | "primary": "hsl(249, 80%, 40%)", 7 | "negative": "hsl(0, 80%, 40%)", 8 | "negative-light": "hsl(0, 80%, 73%)", 9 | "negative-dark": "hsl(0, 79%, 25%)", 10 | "positive": "hsl(133, 80%, 40%)", 11 | "positive-light": "hsl(134, 81%, 72%)", 12 | "positive-dark": "hsl(133, 79%, 25%)", 13 | "root": "hsl(249, 25%, 8%)", 14 | "root-rgb": "15, 18, 25", 15 | "header": "hsl(249, 28%, 12%)", 16 | "unfocused": "hsl(249, 31%, 5%)", 17 | "text": "hsl(249, 0%, 100%)", 18 | "text-rgb": "255, 255, 255", 19 | "text-alt": "hsl(249, 33%, 80%)", 20 | "text-light": "hsl(249, 0%, 100%)", 21 | "label": "hsl(249, 16%, 38%)", 22 | "label-light": "hsl(249, 15%, 50%)", 23 | "neutral": "hsl(249, 26%, 18%)", 24 | "neutral-focus": "hsl(249, 27%, 20%)", 25 | "outline": "hsl(249, 23%, 22%)" 26 | }, 27 | "cssThemeDataset": null, 28 | "wallpaper": "https://github.com/itslap.png" 29 | } 30 | -------------------------------------------------------------------------------- /assets/gamecontroller.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";const t=(t,e="log")=>{"error"===e?console&&"function"==typeof console.error&&console.error(t):console&&"function"==typeof console.info&&console.info(t)},e=e=>t(e,"error"),n=()=>navigator.getGamepads&&"function"==typeof navigator.getGamepads||navigator.getGamepads&&"function"==typeof navigator.webkitGetGamepads||!1,o="Invalid property.",i="Invalid value. It must be a number between 0.00 and 1.00.",s="Button does not exist.",a="Unknown event name.",c=function(t){let n={id:t.index,buttons:t.buttons.length,axes:Math.floor(t.axes.length/2),axeValues:[],axeThreshold:[1],hapticActuator:null,vibrationMode:-1,vibration:!1,mapping:t.mapping,buttonActions:{},axesActions:{},pressed:{},set:function(t,n){if(["axeThreshold"].indexOf(t)>=0){if("axeThreshold"===t&&(!parseFloat(n)||n<0||n>1))return void e(i);this[t]=n}else e(o)},vibrate:function(t=.75,e=500){if(this.hapticActuator)switch(this.vibrationMode){case 0:return this.hapticActuator.pulse(t,e);case 1:return this.hapticActuator.playEffect("dual-rumble",{duration:e,strongMagnitude:t,weakMagnitude:t})}},triggerDirectionalAction:function(t,e,n,o,i){n&&o%2===i?(this.pressed[`${t}${e}`]||(this.pressed[`${t}${e}`]=!0,this.axesActions[e][t].before()),this.axesActions[e][t].action()):this.pressed[`${t}${e}`]&&o%2===i&&(delete this.pressed[`${t}${e}`],this.axesActions[e][t].after())},checkStatus:function(){let t={};const e=navigator.getGamepads?navigator.getGamepads():navigator.webkitGetGamepads?navigator.webkitGetGamepads():[];if(e.length){if(t=e[this.id],t.buttons)for(let e=0;e=this.axeThreshold[0],n,0),this.triggerDirectionalAction("left",i,o<=-this.axeThreshold[0],n,0),this.triggerDirectionalAction("down",i,o>=this.axeThreshold[0],n,1),this.triggerDirectionalAction("up",i,o<=-this.axeThreshold[0],n,1)}}}},associateEvent:function(t,n,o){if(t.match(/^button\d+$/)){const i=parseInt(t.match(/^button(\d+)$/)[1]);i>=0&&i=17?this.buttonActions[16][o]=n:e(s);else if(t.match(/^(up|down|left|right)(\d+)$/)){const i=t.match(/^(up|down|left|right)(\d+)$/),a=i[1],c=parseInt(i[2]);c>=0&&c{},after:()=>{},before:()=>{}};for(let t=0;t{},after:()=>{},before:()=>{}},left:{action:()=>{},after:()=>{},before:()=>{}},right:{action:()=>{},after:()=>{},before:()=>{}},up:{action:()=>{},after:()=>{},before:()=>{}}},n.axeValues[t]=[0,0];return t.hapticActuators?"function"==typeof t.hapticActuators.pulse?(n.hapticActuator=t.hapticActuators,n.vibrationMode=0,n.vibration=!0):t.hapticActuators[0]&&"function"==typeof t.hapticActuators[0].pulse&&(n.hapticActuator=t.hapticActuators[0],n.vibrationMode=0,n.vibration=!0):t.vibrationActuator&&"function"==typeof t.vibrationActuator.playEffect&&(n.hapticActuator=t.vibrationActuator,n.vibrationMode=1,n.vibration=!0),n},r={gamepads:{},axeThreshold:[1],isReady:n(),onConnect:function(){},onDisconnect:function(){},onBeforeCycle:function(){},onAfterCycle:function(){},getGamepads:function(){return this.gamepads},getGamepad:function(t){return this.gamepads[t]?this.gamepads[t]:null},set:function(t,n){if(["axeThreshold"].indexOf(t)>=0){if("axeThreshold"===t&&(!parseFloat(n)||n<0||n>1))return void e(i);if(this[t]=n,"axeThreshold"===t){const t=this.getGamepads(),e=Object.keys(t);for(let n=0;n0&&t(r.checkStatus)},init:function(){window.addEventListener("gamepadconnected",(e=>{const n=e.gamepad||e.detail.gamepad;if(t("Gamepad detected."),window.gamepads||(window.gamepads={}),n){if(!window.gamepads[n.index]){window.gamepads[n.index]=n;const t=c(n);t.set("axeThreshold",this.axeThreshold),this.gamepads[t.id]=t,this.onConnect(this.gamepads[t.id])}1===Object.keys(this.gamepads).length&&this.checkStatus()}})),window.addEventListener("gamepaddisconnected",(e=>{const n=e.gamepad||e.detail.gamepad;t("Gamepad disconnected."),n&&(delete window.gamepads[n.index],delete this.gamepads[n.index],this.onDisconnect(n.index))}))},on:function(t,n){switch(t){case"connect":this.onConnect=n;break;case"disconnect":this.onDisconnect=n;break;case"beforeCycle":case"beforecycle":this.onBeforeCycle=n;break;case"afterCycle":case"aftercycle":this.onAfterCycle=n;break;default:e(a)}return this},off:function(t){switch(t){case"connect":this.onConnect=function(){};break;case"disconnect":this.onDisconnect=function(){};break;case"beforeCycle":case"beforecycle":this.onBeforeCycle=function(){};break;case"afterCycle":case"aftercycle":this.onAfterCycle=function(){};break;default:e(a)}return this}};r.init();const h=r;n()?window.gameControl=h:e("Your web browser does not support the Gamepad API.")})(); -------------------------------------------------------------------------------- /assets/html.js: -------------------------------------------------------------------------------- 1 | export default class Html { 2 | /** The HTML element referenced in this instance. Change using `.swapRef()`, or remove using `.cleanup()`. */ 3 | elm; 4 | /** 5 | * Create a new instance of the Html class. 6 | * @param elm The HTML element to be created or classified from. 7 | */ 8 | constructor(elm) { 9 | if (elm instanceof HTMLElement) { 10 | this.elm = elm; 11 | } else { 12 | this.elm = document.createElement(elm || "div"); 13 | } 14 | } 15 | /** 16 | * Sets the text of the current element. 17 | * @param val The text to set to. 18 | * @returns Html 19 | */ 20 | text(val) { 21 | this.elm.innerText = val; 22 | return this; 23 | } 24 | /** 25 | * Sets the text of the current element. 26 | * @param val The text to set to. 27 | * @returns Html 28 | */ 29 | html(val) { 30 | this.elm.innerHTML = val; 31 | return this; 32 | } 33 | /** 34 | * Safely remove the element. Can be used in combination with a `.swapRef()` to achieve a "delete & swap" result. 35 | * @returns Html 36 | */ 37 | cleanup() { 38 | this.elm.remove(); 39 | return this; 40 | } 41 | /** 42 | * querySelector something. 43 | * @param selector The query selector. 44 | * @returns The HTML element (not as Html) 45 | */ 46 | query(selector) { 47 | return this.elm.querySelector(selector); 48 | } 49 | /** 50 | * querySelector something and get Html access to it. 51 | * @param selector The query selector. 52 | * @returns The HTML element (as Html) 53 | */ 54 | queryHtml(selector) { 55 | return new Html(this.elm.querySelector(selector)); 56 | } 57 | /** 58 | * Toggle on/off a class. 59 | * @param val The class to toggle. 60 | * @returns Html 61 | */ 62 | class(...val) { 63 | for (let i = 0; i < val.length; i++) { 64 | this.elm.classList.toggle(val[i]); 65 | } 66 | return this; 67 | } 68 | /** 69 | * Toggles ON a class. 70 | * @param val The class to enable. 71 | * @returns Html 72 | */ 73 | classOn(...val) { 74 | for (let i = 0; i < val.length; i++) { 75 | this.elm.classList.add(val[i]); 76 | } 77 | return this; 78 | } 79 | /** 80 | * Toggles OFF a class. 81 | * @param val The class to disable. 82 | * @returns Html 83 | */ 84 | classOff(...val) { 85 | for (let i = 0; i < val.length; i++) { 86 | this.elm.classList.remove(val[i]); 87 | } 88 | return this; 89 | } 90 | /** 91 | * Apply CSS styles (dashed method.) Keys use CSS syntax, e.g. `background-color`. 92 | * @param obj The styles to apply (as an object of `key: value;`.) 93 | * @returns Html 94 | */ 95 | style(obj) { 96 | for (const key of Object.keys(obj)) { 97 | this.elm.style.setProperty(key, obj[key]); 98 | } 99 | return this; 100 | } 101 | /** 102 | * Apply CSS styles (JS method.) Keys use JS syntax, e.g. `backgroundColor`. 103 | * @param obj The styles to apply (as an object of `key: value;`) 104 | * @returns Html 105 | */ 106 | styleJs(obj) { 107 | for (const key of Object.keys(obj)) { 108 | //@ts-ignore No other workaround I could find. 109 | this.elm.style[key] = obj[key]; 110 | } 111 | return this; 112 | } 113 | /** 114 | * Apply an event listener. 115 | * @param ev The event listener type to add. 116 | * @param cb The event listener callback to add. 117 | * @returns Html 118 | */ 119 | on(ev, cb) { 120 | this.elm.addEventListener(ev, cb); 121 | return this; 122 | } 123 | /** 124 | * Remove an event listener. 125 | * @param ev The event listener type to remove. 126 | * @param cb The event listener callback to remove. 127 | * @returns Html 128 | */ 129 | un(ev, cb) { 130 | this.elm.removeEventListener(ev, cb); 131 | return this; 132 | } 133 | /** 134 | * Append this element to another element. Uses `appendChild()` on the parent. 135 | * @param parent Element to append to. HTMLElement, Html, and string (as querySelector) are supported. 136 | * @returns Html 137 | */ 138 | appendTo(parent) { 139 | if (parent instanceof HTMLElement) { 140 | parent.appendChild(this.elm); 141 | } else if (parent instanceof Html) { 142 | parent.elm.appendChild(this.elm); 143 | } else if (typeof parent === "string") { 144 | document.querySelector(parent)?.appendChild(this.elm); 145 | } 146 | return this; 147 | } 148 | /** 149 | * Append an element. Typically used as a `.append(new Html(...))` call. 150 | * @param elem The element to append. 151 | * @returns Html 152 | */ 153 | append(elem) { 154 | if (elem instanceof HTMLElement) { 155 | this.elm.appendChild(elem); 156 | } else if (elem instanceof Html) { 157 | this.elm.appendChild(elem.elm); 158 | } else if (typeof elem === "string") { 159 | const newElem = document.createElement(elem); 160 | this.elm.appendChild(newElem); 161 | return new Html(newElem.tagName); 162 | } 163 | return this; 164 | } 165 | /** 166 | * Append multiple elements. Typically used as a `.appendMany(new Html(...), new Html(...)` call. 167 | * @param elements The elements to append. 168 | * @returns Html 169 | */ 170 | appendMany(...elements) { 171 | for (const elem of elements) { 172 | this.append(elem); 173 | } 174 | return this; 175 | } 176 | /** 177 | * Clear the innerHTML of the element. 178 | * @returns Html 179 | */ 180 | clear() { 181 | this.elm.innerHTML = ""; 182 | return this; 183 | } 184 | /** 185 | * Set attributes (object method.) 186 | * @param obj The attributes to set (as an object of `key: value;`) 187 | * @returns Html 188 | */ 189 | attr(obj) { 190 | for (let key in obj) { 191 | if (obj[key] !== null && obj[key] !== undefined) { 192 | this.elm.setAttribute(key, obj[key]); 193 | } else { 194 | this.elm.removeAttribute(key); 195 | } 196 | } 197 | return this; 198 | } 199 | /** 200 | * Set the text value of the element. Only works if element is `input` or `textarea`. 201 | * @param str The value to set. 202 | * @returns Html 203 | */ 204 | val(str) { 205 | var x = this.elm; 206 | x.value = str; 207 | return this; 208 | } 209 | /** 210 | * Retrieve text content from the element. (as innerText, not trimmed) 211 | * @returns string 212 | */ 213 | getText() { 214 | return this.elm.innerText; 215 | } 216 | /** 217 | * Retrieve HTML content from the element. 218 | * @returns string 219 | */ 220 | getHtml() { 221 | return this.elm.innerHTML; 222 | } 223 | /** 224 | * Retrieve the value of the element. Only applicable if it is an `input` or `textarea`. 225 | * @returns string 226 | */ 227 | getValue() { 228 | return this.elm.value; 229 | } 230 | /** 231 | * Swap the local `elm` with a new HTMLElement. 232 | * @param elm The element to swap with. 233 | * @returns Html 234 | */ 235 | swapRef(elm) { 236 | this.elm = elm; 237 | return this; 238 | } 239 | /** 240 | * An alternative method to create an Html instance. 241 | * @param elm Element to create from. 242 | * @returns Html 243 | */ 244 | static from(elm) { 245 | return new Html(elm); 246 | } 247 | /** 248 | * An easier querySelector method. 249 | * @param query The string to query 250 | * @returns a new Html 251 | */ 252 | static qs(query) { 253 | if (document.querySelector(query)) { 254 | return Html.from(document.querySelector(query)); 255 | } else { 256 | return null; 257 | } 258 | } 259 | /** 260 | * An easier querySelectorAll method. 261 | * @param query The string to query 262 | * @returns a new Html 263 | */ 264 | static qsa(query) { 265 | if (document.querySelector(query)) { 266 | return Array.from(document.querySelectorAll(query)).map((e) => 267 | Html.from(e) 268 | ); 269 | } else { 270 | return null; 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /assets/icons/error.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/warn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/images/getitonpluto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/getitonpluto.png -------------------------------------------------------------------------------- /assets/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/screenshot.png -------------------------------------------------------------------------------- /assets/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/screenshot2.png -------------------------------------------------------------------------------- /assets/images/ss_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/ss_dark.png -------------------------------------------------------------------------------- /assets/images/ss_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/ss_light.png -------------------------------------------------------------------------------- /assets/images/thewebosalliancebanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/thewebosalliancebanner.png -------------------------------------------------------------------------------- /assets/images/zeonbanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/zeonbanner.png -------------------------------------------------------------------------------- /assets/images/zeoncommunitybanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/images/zeoncommunitybanner.png -------------------------------------------------------------------------------- /assets/notify.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/notify.wav -------------------------------------------------------------------------------- /assets/pluto-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /assets/startup.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/startup.wav -------------------------------------------------------------------------------- /assets/user-avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/wallpapers/green.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/green.jpg -------------------------------------------------------------------------------- /assets/wallpapers/grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/grey.png -------------------------------------------------------------------------------- /assets/wallpapers/grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/wallpapers/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/light.png -------------------------------------------------------------------------------- /assets/wallpapers/light2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/light2.png -------------------------------------------------------------------------------- /assets/wallpapers/purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/wallpapers/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/red.png -------------------------------------------------------------------------------- /assets/wallpapers/space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/space.png -------------------------------------------------------------------------------- /assets/wallpapers/space2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/wallpapers/space2.png -------------------------------------------------------------------------------- /docs/css-classes.md: -------------------------------------------------------------------------------- 1 | # CSS Classes 2 | 3 | [Back to README.md](README.md) 4 | 5 | - [CSS Classes](#css-classes) 6 | - [Layout](#layout) 7 | - [Spacing](#spacing) 8 | - [Layout](#layout-1) 9 | - [General](#general) 10 | - [Flexbox](#flexbox) 11 | - [Flexbox Spacing](#flexbox-spacing) 12 | - [Display](#display) 13 | - [Animation](#animation) 14 | - [Color](#color) 15 | 16 | There are many CSS classes that are available in Pluto. Until all of them are documented, here are a lot of the utility ones that are commonly used: 17 | 18 | ## Layout 19 | 20 | - `with-sidebar` - Applied to a window's wrapper if you want to use a sidebar. Removes padding so that the sidebar is flush with the window edges. 21 | - `iframe` - Applied to a window wrapper. Bypasses any sidebar position settings. Should be used if you are using in combination with a MenuBar instead of a Sidebar. 22 | 23 | ## Spacing 24 | 25 | - `ml-auto` - Applied to any div. Pushes content to the right. 26 | - `mr-auto` - Applied to any div. Pushes content to the left. 27 | - `mt-auto` - Applied to any div. Pushes content to the bottom. 28 | - `mb-auto` - Applied to any div. Pushes content to the top. 29 | - `m-0` - Disables margin. 30 | - `mt-0` - Disables margin for the top only. 31 | - `ml-0` - Disables margin for the left only. 32 | - `mr-0` - Disables margin for the right only. 33 | - `mt-1` - Adds 4px of margin to the top. 34 | - `mt-2` - Adds 8px of margin to the top. 35 | - `mb-0` - Disables margin for the bottom only. 36 | - `mb-2` - Adds 8px of margin to the bottom. 37 | - `mb-1` - Adds 4px of margin to the bottom. 38 | - `o-h` - Hides overflow for the content. 39 | - `ovh` - Allows overflow for the content. 40 | - `separator` - Space nearby elements as far apart as they can be. 41 | - `padded` - Applies large horizontal and vertical padding. 42 | - `container` - Applies 10px padding. 43 | - `padding` - Apply a padding of 8px. 44 | - `padding-mid` - Apply a padding of 6px. 45 | - `padding-small` - Apply a padding of 4px. 46 | 47 | ## Layout 48 | 49 | ### General 50 | 51 | - `w-100` - Sets the width to 100%. 52 | - `h-100` - Sets the height to 100%. 53 | - `mc` - Set the width to max content. 54 | - `mhc` - Set the height to max content. 55 | 56 | ### Flexbox 57 | 58 | - `row` - Applied to any div. Turns it into a flexbox row. 59 | - `row-wrap` - Applied to any div. Turns it into a flexbox row that wraps. 60 | - `col` - Applied to any div. Turns it into a flexbox column. 61 | - `fg` - Expand the element to fill the remaining area. May work better with `w-100` or `h-100`. 62 | - `fc` - Justify content to the center and align items to the center. 63 | - `jc` - Justify content to the center. 64 | - `js` - Justify content to the start. 65 | - `ac` - Align items to the center. 66 | - `as` - Align items to the start. 67 | - `if` - Display the element as inline-flex. 68 | 69 | ### Flexbox Spacing 70 | 71 | - `gap` - Apply a gap of 8px. 72 | - `gap-mid` - Apply a gap of 6px. 73 | - `gap-small` - Apply a gap of 4px. 74 | 75 | ## Display 76 | 77 | - `list-item` - Makes it fill the screen and have pre-formatted spacing. 78 | - `nf` - Display the element as a block. 79 | - `card` - Applies some basic styling to make it look like a card. 80 | - `card-box` - Make the element look like a container that holds cards. 81 | - `alert` - Displays a warning alert 82 | - `display-heading` - Very large heading. Useful for fullscreen windows. 83 | - `display-subheading` - Large heading, used in fullscreen windows 84 | - `display-padding` - Adds padding relative to viewport. 85 | - `no-ui` - Apply monospace font and some terminal-like styles. 86 | - `h1` - Make it look like a top-level heading. 87 | - `h2` - Make it look like a second-level heading. 88 | - `h3` - Make it look like a third-level heading. 89 | - `label` - Make it appear like a label. 90 | - `label-light` - Make it appear like a brighter-colored label. 91 | - `small-label` - Another small label. 92 | - `superscript` - Uses align-self: start. 93 | - `spacer` - Used to create a label with a line that stretches to the end: 94 | 95 | ```html 96 |
97 | Text 98 | 99 |
100 | ``` 101 | 102 | - `badge` - Create a inline rounded badge with text. 103 | 104 | ## Animation 105 | 106 | - `fadeIn` - Make the element fade in. 107 | - `fadeOut` - Make the element fade out. 108 | - `slideIn` - Make the element slide in. 109 | - `slideOut` - Make the element slide out. 110 | 111 | ## Color 112 | 113 | - `blur` - Applies backdrop filter to the element, blurring things behind it. 114 | - `success` - Turns the colo to the positive color- usually green. 115 | - `danger` - Turns the color to the negative color, usually red. 116 | - `warning` - Turns the color to the warning-light color, usually yellow. 117 | - `info` - Turns the color to the primary color, usually blue. 118 | - `transparent` - Hides the content's background and border colors. 119 | - `muted` - Apply label-light color and make the text look muted. 120 | -------------------------------------------------------------------------------- /docs/images/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/card.png -------------------------------------------------------------------------------- /docs/images/ctxMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/ctxMenu.png -------------------------------------------------------------------------------- /docs/images/menuBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/menuBar.png -------------------------------------------------------------------------------- /docs/images/notificationWithButtons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/notificationWithButtons.png -------------------------------------------------------------------------------- /docs/images/notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/notify.png -------------------------------------------------------------------------------- /docs/images/selectList_row.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/selectList_row.png -------------------------------------------------------------------------------- /docs/images/selectList_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/selectList_table.png -------------------------------------------------------------------------------- /docs/images/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/sidebar.png -------------------------------------------------------------------------------- /docs/images/textSidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/textSidebar.png -------------------------------------------------------------------------------- /docs/images/tooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/docs/images/tooltip.png -------------------------------------------------------------------------------- /docs/localization.md: -------------------------------------------------------------------------------- 1 | # Localization 2 | 3 | [Back to README.md](README.md) 4 | 5 | You can add localized strings to your app by adding a `strings` property to your export: 6 | 7 | ```js 8 | export default { 9 | // ... 10 | strings: { 11 | // Your strings... 12 | // Example: 13 | en_US: { 14 | myCustomString: "This is my custom string", 15 | }, 16 | // etc for other languages... 17 | es_ES: { ... }, 18 | de_DE: { ... }, 19 | }, 20 | // ... 21 | } 22 | ``` 23 | 24 | Strings that can't be found at all will display as their ID. Here's an example: 25 | 26 | ```js 27 | // logs "very_invalid_string" to the console 28 | console.log(Root.Lib.getString("very_invalid_string")); 29 | // logs "OK" to the console if the language is en_US 30 | console.log(Root.Lib.getString("ok")); 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/pluto-markup-language.md: -------------------------------------------------------------------------------- 1 | # Pluto Markup Language Documentation 2 | 3 | [Back to README.md](README.md) 4 | 5 | - [Pluto Markup Language Documentation](#pluto-markup-language-documentation) 6 | - [PML example app code](#pml-example-app-code) 7 | - [How to use PML](#how-to-use-pml) 8 | - [Info about PML](#info-about-pml) 9 | 10 | ## PML example app code 11 | 12 | Here is a template for a PML example app. 13 | 14 | You can copy this code. 15 | 16 | ```html 17 | 18 | 19 | Pluto Markup Language 20 | 21 | 22 |

Hello

23 |

PML example app.

24 |
25 | 28 | 31 |
32 | ``` 33 | 34 | ## How to use PML 35 | 36 | PML is a markup language that is used to create apps in Pluto. It is similar to HTML but with a few differences. 37 | To make a PML file, create a file with the extension `.pml` and use the structure above [PML example app code](#pml-example-app-code). 38 | 39 | ## Info about PML 40 | 41 | PML can only be currently launched through the Start Menu, Desktop and the File Manager. You can currently not launch PML files through the Terminal or DevEnv. 42 | -------------------------------------------------------------------------------- /docs/virtualFS.md: -------------------------------------------------------------------------------- 1 | # Virtual File System Documentation 2 | 3 | [Back to README.md](README.md) 4 | 5 | - [Virtual File System Documentation](#virtual-file-system-documentation) 6 | - [NEW: Registry API](#new-registry-api) 7 | - [Default file structure](#default-file-structure) 8 | - [Functions](#functions) 9 | - [Secret File Tips](#secret-file-tips) 10 | - [File Location Tips](#file-location-tips) 11 | - [Configuration files](#configuration-files) 12 | - [Storing external libraries](#storing-external-libraries) 13 | 14 | ## NEW: Registry API 15 | 16 | The brand new Registry API provides a simple way to create a Key/Value store for your app. 17 | 18 | See [Libraries and Components → Registry](libs-and-components.md#registry). 19 | 20 | ## Default file structure 21 | 22 | ```js 23 | { 24 | Registry: {}, 25 | Root: { 26 | Pluto: { 27 | panics: { ... }, 28 | config: { 29 | "appearanceConfig.json": JSON.stringify({ 30 | wallpaper: "./assets/wallpapers/space.png", 31 | useThemeWallpaper: true, 32 | theme: "dark.theme", 33 | sidebarType: "vertical", 34 | }), 35 | "settingsConfig.json": JSON.stringify({ 36 | warnSecurityIssues: true, 37 | }), 38 | themes: { ... }, 39 | }, 40 | apps: { 41 | "README.MD": 42 | "This folder contains all the apps that you have downloaded. If you have any questions about them please contact us.", 43 | }, 44 | startup: "", 45 | }, 46 | Desktop: {}, 47 | Documents: {}, 48 | Downloads: {}, 49 | Pictures: {}, 50 | Videos: {}, 51 | Music: {}, 52 | }, 53 | } 54 | ``` 55 | 56 | ## Functions 57 | 58 | Vfs functions are found as below. Some are documented, others are not 59 | 60 | - `async exportFS()` 61 | - `async importFS(fsObject = templateFsLayout)` 62 | - `async getParentFolder(path)` 63 | Helper function to get the parent folder of a given path 64 | - `async whatIs(path, fsObject = this.fileSystem)` 65 | function to tell you if stored data is a file or a folder 66 | - `async readFile(path, fsObject = this.fileSystem, bypass = false)` 67 | Function to get the contents of a file at a given path 68 | - `async writeFile(path, contents, fsObject = this.fileSystem)` 69 | Function to write to a file at a given path 70 | - `async createFolder(path, fsObject = this.fileSystem)` 71 | Function to create a new folder at a given path 72 | - `async delete(path, fsObject = this.fileSystem)` 73 | Function to delete a file or folder at a given path 74 | - `async list(path, fsObject = this.fileSystem)` 75 | Function to list all files and folders at a given path 76 | - `async rename(path, newName, fsObject = this.fileSystem)` 77 | Function to rename a file 78 | **newName MUST be the new exact name of the file, NOT absolute.** 79 | - `async exists(path, fsObject = this.fileSystem)` 80 | - `async merge(fsObject = this.fileSystem)` 81 | 82 | Using the VFS in your app: 83 | 84 | ```js 85 | const Vfs = await Root.Lib.loadLibrary("VirtualFS"); 86 | await Vfs.importFS(); 87 | 88 | // You can now use Vfs with any of the functions above 89 | ``` 90 | 91 | ## Secret File Tips 92 | 93 | The file `Root/Pluto/startup` allows you to configure startup apps. Use newlines to change which apps are launched at boot. 94 | 95 | Example: 96 | 97 | ``` 98 | Root/Desktop/Settings.shrt 99 | Root/Pluto/Apps/Example.js 100 | ``` 101 | 102 | launches Settings and Example at startup. 103 | 104 | ## File Location Tips 105 | 106 | ### Configuration files 107 | 108 | For config files, use the Registry path instead of Root. The files will be hidden from the end-user by default. 109 | Here's an example: 110 | 111 | ```js 112 | const vfs = await Root.Lib.loadLibrary("VirtualFS"); 113 | 114 | await vfs.importFS(); 115 | 116 | // Registry is a hidden folder that can be used for storing app data but keeping it away from the user 117 | vfs.writeFile("Registry/MyConfig.json", JSON.stringify(config)); 118 | ``` 119 | 120 | ### Storing external libraries 121 | 122 | Use the `Root/Pluto/cache/lib` folder for storing external libraries. 123 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 15 | Pluto 16 | 17 | 18 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /pkgs/apps/Admin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: "Admin", 3 | description: "An example of an elevated process.", 4 | privileges: [ 5 | { 6 | privilege: "full", 7 | description: "for the admin app to do admin things", 8 | }, 9 | ], 10 | ver: "v1.6.2", // Supports minimum Core version of v1.6.2 11 | type: "process", 12 | exec: async function (Root) { 13 | let wrapper; 14 | let MyWindow; 15 | 16 | Root.Lib.setOnEnd((_) => MyWindow.close()); 17 | 18 | const Win = (await Root.Lib.loadLibrary("WindowSystem")).win; 19 | 20 | MyWindow = new Win({ 21 | title: "Admin App", 22 | content: "Hello", 23 | pid: Root.PID, 24 | onclose: () => { 25 | onEnd(); 26 | }, 27 | }); 28 | wrapper = MyWindow.window.querySelector(".win-content"); 29 | 30 | /* Heading */ new Root.Lib.html("h1").text("Hi i am app").appendTo(wrapper); 31 | /* Paragraph */ 32 | new Root.Lib.html("p") 33 | .html(`I ${Root.Core !== null ? "have" : "do not have"} admin privileges`) 34 | .appendTo(wrapper); 35 | /* Button */ 36 | new Root.Lib.html("button") 37 | .text("Check privileges") 38 | .appendTo(wrapper) 39 | .on("click", (e) => { 40 | Root.Modal.alert( 41 | "Hi", 42 | `I am ${Root.Core !== null ? "Privileged" : "Non-Privileged"}`, 43 | wrapper 44 | ); 45 | }); 46 | /* Close Button */ 47 | new Root.Lib.html("button") 48 | .text("End Process") 49 | .appendTo(wrapper) 50 | .on("click", (e) => { 51 | Root.Lib.onEnd(); 52 | }); 53 | 54 | return Root.Lib.setupReturns((m) => { 55 | console.log("Example received message: " + m); 56 | }); 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /pkgs/apps/AudioPlayer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: "Audio Player", 3 | description: "Listen to your music in this app.", 4 | ver: "v1.6.2", // Supports minimum Core version of v1.6.2 5 | type: "process", 6 | exec: async function (Root) { 7 | let wrapper; // Lib.html | undefined 8 | let MyWindow; 9 | 10 | const Html = (await import("https://unpkg.com/@datkat21/html")).default; 11 | 12 | console.log("Hello from example package", Root.Lib); 13 | 14 | const NowPlaying = await Root.Lib.loadLibrary("NowPlaying"); 15 | Root.Lib.setOnEnd(function () { 16 | setTimeout(() => { 17 | NowPlaying.disposePlayer(); 18 | }, 1000); 19 | MyWindow.close(); 20 | }); 21 | 22 | const Win = (await Root.Lib.loadLibrary("WindowSystem")).win; 23 | 24 | MyWindow = new Win({ 25 | title: "Audio Player", 26 | pid: Root.PID, 27 | width: 854, 28 | height: 480, 29 | onclose: () => { 30 | Root.Lib.onEnd(); 31 | }, 32 | }); 33 | 34 | // initializing wrappers and vfs 35 | wrapper = MyWindow.window.querySelector(".win-content"); 36 | 37 | const vfs = await Root.Lib.loadLibrary("VirtualFS"); 38 | const FileDialog = await Root.Lib.loadLibrary("FileDialog"); 39 | 40 | await vfs.importFS(); 41 | 42 | wrapper.classList.add("with-sidebar", "row", "o-h", "h-100"); 43 | 44 | const Sidebar = await Root.Lib.loadComponent("Sidebar"); 45 | 46 | // this function won't return a module 47 | async function loadScript(url) { 48 | // script probably already exists 49 | if (Html.qs('script[src="' + url + '"]')) { 50 | return false; 51 | } 52 | 53 | // make new script 54 | new Html("script").html(await (await fetch(url)).text()).appendTo("body"); 55 | return true; 56 | } 57 | 58 | await loadScript( 59 | "https://cdnjs.cloudflare.com/ajax/libs/jsmediatags/3.9.5/jsmediatags.min.js" 60 | ); 61 | 62 | let jMediaTags = window.jsmediatags; 63 | console.log(jMediaTags); 64 | 65 | // this function opens the file and changes the title to the file name, 66 | // we load the file into a buffer 67 | let fileName = ""; 68 | async function openFile(path) { 69 | let file; 70 | if (path) file = path; 71 | else file = await FileDialog.pickFile("Root"); 72 | if (file === false) return; 73 | let result = updateAudio(await vfs.readFile(file)); 74 | if (result === false) return; 75 | fileName = file.split("/").pop(); 76 | MyWindow.window.querySelector(".win-titlebar .title").innerText = 77 | "Audio Player - " + fileName; 78 | MyWindow.focus(); 79 | } 80 | 81 | // creates sidebar 82 | Sidebar.new(wrapper, [ 83 | { 84 | onclick: async (_) => { 85 | openFile(); 86 | }, 87 | html: Root.Lib.icons.fileAudio, 88 | title: "Select Audio...", 89 | }, 90 | { 91 | style: { 92 | "margin-top": "auto", 93 | }, 94 | onclick: (_) => { 95 | alert("Not implemented"); 96 | }, 97 | html: Root.Lib.icons.help, 98 | title: "Help", 99 | }, 100 | ]); 101 | 102 | // creates the wrapper that the image is in 103 | let vidWrapper = new Root.Lib.html("div") 104 | .class("ovh", "fg", "fc") 105 | .styleJs({ 106 | display: "flex", 107 | flexDirection: "column", 108 | justifyContent: "center", 109 | width: "100%", 110 | height: "100%", 111 | }) 112 | .appendTo(wrapper); 113 | 114 | // creates the actual img element 115 | let metaDataDiv = new Root.Lib.html("div").appendTo(vidWrapper).styleJs({ 116 | display: "flex", 117 | gap: "25px", 118 | alignItems: "center", 119 | justifyContent: "center", 120 | }); 121 | let img = new Root.Lib.html("img").appendTo(metaDataDiv).styleJs({ 122 | objectFit: "cover", 123 | width: "200px", 124 | height: "200px", 125 | borderRadius: "5px", 126 | }); 127 | let texts = new Root.Lib.html("div").appendTo(metaDataDiv).styleJs({ 128 | display: "flex", 129 | flexDirection: "column", 130 | justifyContent: "center", 131 | }); 132 | new Root.Lib.html("br").appendTo(vidWrapper); 133 | new Root.Lib.html("br").appendTo(vidWrapper); 134 | let audio = new Root.Lib.html("audio") 135 | .appendTo(vidWrapper) 136 | .style({ 137 | width: "80%", 138 | "object-fit": "contain", 139 | border: "none", 140 | }) 141 | .attr({ draggable: "false", controls: "on" }); 142 | 143 | let playerCover = null; 144 | let playerSong = ""; 145 | let playerArtist = "Unknown artist"; 146 | 147 | // updates the video on the next load 148 | async function updateAudio(content) { 149 | playerSong = ""; 150 | playerArtist = "Unknown artist"; 151 | 1; 152 | if (!content.startsWith("data:audio/") && !content.startsWith("blob:")) { 153 | Root.Modal.alert("Error", "This does not look like an audio file").then( 154 | (_) => { 155 | MyWindow.focus(); 156 | } 157 | ); 158 | return false; 159 | } 160 | setTimeout(async () => { 161 | console.log(fileName); 162 | playerSong = fileName; 163 | texts.clear(); 164 | new Root.Lib.html("p").appendTo(texts).text("Now playing"); 165 | let songName = new Root.Lib.html("h1").appendTo(texts).text(playerSong); 166 | let songArtist = new Root.Lib.html("p") 167 | .appendTo(texts) 168 | .text(playerArtist); 169 | const audioBlob = await (await fetch(content)).blob(); 170 | jsmediatags.read(audioBlob, { 171 | onSuccess: function (tag) { 172 | console.log(tag); 173 | if ("title" in tag.tags) { 174 | playerSong = tag.tags.title; 175 | } 176 | if ("artist" in tag.tags) { 177 | playerArtist = tag.tags.artist; 178 | } 179 | if ("album" in tag.tags) { 180 | playerArtist = playerArtist + " • " + tag.tags.album; 181 | } 182 | if ("year" in tag.tags) { 183 | playerArtist = playerArtist + " • " + tag.tags.year; 184 | } 185 | if ("picture" in tag.tags) { 186 | let buf = new Uint8Array(tag.tags.picture.data); 187 | let blob = new Blob([buf]); 188 | console.log(blob); 189 | img.elm.src = URL.createObjectURL(blob); 190 | playerCover = URL.createObjectURL(blob); 191 | img.styleJs({ display: "flex" }); 192 | } else { 193 | img.styleJs({ display: "none" }); 194 | } 195 | songName.text(playerSong); 196 | songArtist.text(playerArtist); 197 | function isPlaying() { 198 | NowPlaying.setStatus({ 199 | pid: Root.PID, 200 | coverArt: playerCover, 201 | mediaName: playerSong, 202 | mediaAuthor: playerArtist.split(" • ")[0], 203 | appName: "Audio Player", 204 | controls: [ 205 | { 206 | icon: '', 207 | callbackEvent: "pausePlay", 208 | }, 209 | ], 210 | }); 211 | } 212 | function isPaused() { 213 | NowPlaying.setStatus({ 214 | pid: Root.PID, 215 | coverArt: playerCover, 216 | mediaName: playerSong, 217 | mediaAuthor: playerArtist.split(" • ")[0], 218 | appName: "Audio Player", 219 | controls: [ 220 | { 221 | icon: '', 222 | callbackEvent: "pausePlay", 223 | }, 224 | ], 225 | }); 226 | } 227 | audio.on("playing", () => { 228 | isPlaying(); 229 | }); 230 | audio.on("pause", () => { 231 | isPaused(); 232 | }); 233 | if (!audio.elm.paused) { 234 | isPlaying(); 235 | } 236 | }, 237 | onError: function (error) { 238 | console.log(error); 239 | img.cleanup(); 240 | }, 241 | }); 242 | }, 700); 243 | audio.elm.src = content; 244 | audio.elm.play(); 245 | } 246 | 247 | return Root.Lib.setupReturns((m) => { 248 | if (typeof m === "object" && m.type && m.type === "loadFile" && m.path) { 249 | openFile(m.path); 250 | } 251 | if ( 252 | typeof m === "object" && 253 | m.type && 254 | m.type === "mediaPlayerAction" && 255 | m.command && 256 | m.command === "pausePlay" 257 | ) { 258 | if (audio.elm.paused) { 259 | audio.elm.play(); 260 | } else { 261 | audio.elm.pause(); 262 | } 263 | } 264 | }); 265 | }, 266 | }; 267 | -------------------------------------------------------------------------------- /pkgs/apps/BuildAKtat.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: "Build A Ktat", 3 | description: "Build A Ktat app, very nice for some cool characters.", 4 | ver: "v1.6.2", // Supports minimum Core version of v1.6.2 5 | type: "process", 6 | exec: async function (Root) { 7 | let wrapper; // Lib.html | undefined 8 | let MyWindow; 9 | 10 | console.log("Hello from example package", Root.Lib); 11 | 12 | Root.Lib.setOnEnd(_ => MyWindow.close()); 13 | 14 | const Win = (await Root.Lib.loadLibrary("WindowSystem")).win; 15 | 16 | MyWindow = new Win({ 17 | title: "Build A Ktat", 18 | content: '