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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------
/assets/startup.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeondev/pluto/8cdc4bb0422b8f1be6bcc5a366683b7dbdfdebb2/assets/startup.wav
--------------------------------------------------------------------------------
/assets/user-avatar.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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: '