Merge branch 'master' into snyk-upgrade-34508c939a56079503c35612be03dfe9
This commit is contained in:
commit
8270175310
17 changed files with 272 additions and 166 deletions
|
|
@ -75,4 +75,4 @@ This is an example of this being used for "**Assets**":
|
|||
|
||||
## Code of Conduct
|
||||
|
||||
Our Code of Conduct is located at `CODE_OF_CONDUCT.md`
|
||||
Our Code of Conduct is located at `CODE_OF_CONDUCT.md`.
|
||||
|
|
|
|||
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -13,6 +13,7 @@
|
|||
"eruda": "^3.0.1",
|
||||
"filer": "^1.4.1",
|
||||
"prism-code-editor": "^2.2.1",
|
||||
"material-symbols": "^0.14.1",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -4101,6 +4102,11 @@
|
|||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/material-symbols": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/material-symbols/-/material-symbols-0.14.1.tgz",
|
||||
"integrity": "sha512-qFUhEt90BbKMkpUIN8OdrjFpFBSeQ2XyB6H2Z08wecOWJnbkvAgslejKivgEewtPse14ElkOwZeoHakPZYUEhQ=="
|
||||
},
|
||||
"node_modules/md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
"eruda": "^3.0.1",
|
||||
"filer": "^1.4.1",
|
||||
"prism-code-editor": "^2.2.1",
|
||||
"material-symbols": "^0.14.1",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"ts-standard": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
@import url(https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css);
|
||||
@import url(https://api.fontshare.com/v2/css?f[]=satoshi@1,2&display=swap);
|
||||
|
||||
:root {
|
||||
|
|
@ -9,6 +8,16 @@
|
|||
--base: #1e1e2e;
|
||||
--mantle: #181825;
|
||||
--crust: #11111b;
|
||||
|
||||
--app-radius: 25%;
|
||||
}
|
||||
|
||||
.material-symbols-rounded {
|
||||
font-variation-settings:
|
||||
'FILL' 0,
|
||||
'wght' 400,
|
||||
'GRAD' 200,
|
||||
'opsz' 24
|
||||
}
|
||||
|
||||
body,
|
||||
|
|
@ -18,10 +27,9 @@ html {
|
|||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
margin: 10px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
@ -31,6 +39,7 @@ html {
|
|||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
|
|
@ -44,34 +53,44 @@ html {
|
|||
}
|
||||
|
||||
toolbar {
|
||||
width: calc(100% - 40px);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 0 0 0 0;
|
||||
justify-content: center;
|
||||
|
||||
div[data-toolbar-id="appview"] {
|
||||
background: linear-gradient(45deg, var(--crust), var(--surface-0));
|
||||
}
|
||||
background: var(--mantle);
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
|
||||
& > div {
|
||||
&.outlined {
|
||||
padding: 10px;
|
||||
background: var(--base);
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.1), 0 0px 10px rgba(0, 0, 0, 0.1);
|
||||
height: 40px !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&[data-toolbar-id="controls"] {
|
||||
* {
|
||||
font-size: 24px;
|
||||
}
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
&[data-toolbar-id="plugins"] {
|
||||
width: 24px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
align-self: center;
|
||||
|
||||
app {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
aspect-ratio: 1 / 1;
|
||||
height: 2em;
|
||||
border-radius: 40%;
|
||||
height: 35px;
|
||||
border-radius: var(--app-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,11 +98,10 @@ toolbar {
|
|||
|
||||
window-area {
|
||||
position: relative;
|
||||
width: calc(100% - 40px);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 10px;
|
||||
|
||||
window {
|
||||
background: var(--base);
|
||||
|
|
@ -97,7 +115,7 @@ window-area {
|
|||
transition: 0.2s opacity, 0.2s width, 0.2s height;
|
||||
|
||||
window-header {
|
||||
height: 20px;
|
||||
height: 35px;
|
||||
padding: 7.5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -106,7 +124,7 @@ window-area {
|
|||
img {
|
||||
aspect-ratio: 1 / 1;
|
||||
height: 1.2em;
|
||||
border-radius: 40%;
|
||||
border-radius: var(--app-radius);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +181,7 @@ launcher {
|
|||
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: 40%;
|
||||
border-radius: var(--app-radius);
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
|
|
@ -219,3 +237,7 @@ preloader {
|
|||
gap: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
flex {
|
||||
flex: 1;
|
||||
}
|
||||
|
|
@ -29,12 +29,12 @@ export default class BrowserApp implements App {
|
|||
<button class="add">+</button>
|
||||
</div>
|
||||
<div class="tools" style="display:flex;gap:10px;align-items:center;">
|
||||
<i class='back bx bx-left-arrow-alt'></i>
|
||||
<i class='forward bx bx-right-arrow-alt'></i>
|
||||
<i class='refresh bx bx-refresh'></i>
|
||||
<i class='back material-symbols-rounded'>arrow_back</i>
|
||||
<i class='forward material-symbols-rounded'>arrow_forward</i>
|
||||
<i class='refresh material-symbols-rounded'>refresh</i>
|
||||
<input class="inp" style="border-radius: 15px;flex: 1;background: var(--base);border:none;padding: 0px 16px;height: 30px;">
|
||||
<i class='toggle bx bx-toggle-right'></i>
|
||||
<i class='fullscreen bx bx-fullscreen'></i>
|
||||
<i class='toggle material-symbols-rounded'>toggle_on</i>
|
||||
<i class='fullscreen material-symbols-rounded'>fullscreen</i>
|
||||
</div>
|
||||
<div id="content-container"></div>
|
||||
<style>
|
||||
|
|
@ -87,14 +87,12 @@ export default class BrowserApp implements App {
|
|||
this.proxy = !this.proxy
|
||||
if (!this.proxy) {
|
||||
if (this === tabManager.activeTab) {
|
||||
win.content.querySelector('.toggle')?.classList.remove('bx-toggle-right')
|
||||
win.content.querySelector('.toggle')?.classList.add('bx-toggle-left')
|
||||
(win.content.querySelector('.toggle') as HTMLElement).innerHTML = 'toggle_off'
|
||||
}
|
||||
this.iframe.src = win.content.querySelector('input')?.value as string
|
||||
} else {
|
||||
if (this === tabManager.activeTab) {
|
||||
win.content.querySelector('.toggle')?.classList.remove('bx-toggle-left')
|
||||
win.content.querySelector('.toggle')?.classList.add('bx-toggle-right')
|
||||
(win.content.querySelector('.toggle') as HTMLElement).innerHTML = 'toggle_on'
|
||||
}
|
||||
this.iframe.src = `/service/${xor.encode(win.content.querySelector('input')?.value as string)}`
|
||||
}
|
||||
|
|
@ -145,13 +143,12 @@ export default class BrowserApp implements App {
|
|||
})
|
||||
|
||||
if (!tab.proxy) {
|
||||
(tab.header.querySelector('.title') as HTMLElement).textContent = 'Tab'
|
||||
try { (win.content.querySelector('.inp') as HTMLInputElement).value = (tab.iframe.contentWindow as Window).location.href } catch (e) { (win.content.querySelector('.inp') as HTMLInputElement).value = 'about:blank' }
|
||||
win.content.querySelector('.toggle')?.classList.remove('bx-toggle-right')
|
||||
win.content.querySelector('.toggle')?.classList.add('bx-toggle-left')
|
||||
(win.content.querySelector('.toggle') as HTMLElement).innerHTML = 'toggle_off'
|
||||
} else {
|
||||
try { (win.content.querySelector('.inp') as HTMLInputElement).value = xor.decode((tab.iframe.contentWindow as Window).location.href.split('/service/')[1]) } catch (e) { (win.content.querySelector('.inp') as HTMLInputElement).value = 'about:blank' }
|
||||
win.content.querySelector('.toggle')?.classList.remove('bx-toggle-left')
|
||||
win.content.querySelector('.toggle')?.classList.add('bx-toggle-right')
|
||||
(win.content.querySelector('.toggle') as HTMLElement).innerHTML = 'toggle_on'
|
||||
}
|
||||
|
||||
tab.active = true
|
||||
|
|
@ -237,18 +234,20 @@ export default class BrowserApp implements App {
|
|||
tabManager.activeTab.toggle()
|
||||
}
|
||||
|
||||
let full = false;
|
||||
(win.content.querySelector('.fullscreen') as HTMLElement).onclick = async () => {
|
||||
if (full) {
|
||||
win.content.querySelector('.fullscreen')?.classList.remove('bx-fullscreen')
|
||||
win.content.querySelector('.fullscreen')?.classList.add('bx-exit-fullscreen')
|
||||
await document.exitFullscreen()
|
||||
win.content.onfullscreenchange = () => {
|
||||
if (document.fullscreenElement !== null) {
|
||||
(win.content.querySelector('.fullscreen') as HTMLElement).innerHTML = 'fullscreen_exit'
|
||||
} else {
|
||||
win.content.querySelector('.fullscreen')?.classList.remove('bx-exit-fullscreen')
|
||||
win.content.querySelector('.fullscreen')?.classList.add('bx-fullscreen')
|
||||
await win.content.requestFullscreen()
|
||||
(win.content.querySelector('.fullscreen') as HTMLElement).innerHTML = 'fullscreen'
|
||||
}
|
||||
}
|
||||
|
||||
(win.content.querySelector('.fullscreen') as HTMLElement).onclick = async () => {
|
||||
if (document.fullscreenElement !== null) {
|
||||
await document.exitFullscreen().catch(e => console.error)
|
||||
} else {
|
||||
await win.content.requestFullscreen().catch(e => console.error)
|
||||
}
|
||||
full = !full
|
||||
}
|
||||
|
||||
tabManager.addTab(new Tab('https://google.com'))
|
||||
|
|
|
|||
|
|
@ -63,13 +63,13 @@ export default class EditorApp implements App {
|
|||
|
||||
<div class="dropdown" id="file">
|
||||
<a id="save">
|
||||
<i class='bx bxs-save' style="font-size: 1.1rem;"></i>
|
||||
<i class='material-symbols-rounded' style="font-size: 1.1rem;">save</i>
|
||||
Save
|
||||
</a>
|
||||
</div>
|
||||
<div class="dropdown" id="edit">
|
||||
<a id="find">
|
||||
<i class='bx bxs-save' style="font-size: 1.1rem;"></i>
|
||||
<i class='material-symbols-rounded' style="font-size: 1.1rem;">search</i>
|
||||
Find
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,18 +27,18 @@ export default class FilesApp implements App {
|
|||
|
||||
async function setDir (dir: string): Promise<void> {
|
||||
await window.fs.readdir(dir, (e: NodeJS.ErrnoException, files: string[]) => {
|
||||
const back = dir === '/' ? '<i class=\'bx bx-arrow-to-left\'></i>' : '<i class=\'back bx bx-left-arrow-alt\'></i>'
|
||||
const back = dir === '/' ? '<span class="material-symbols-rounded">first_page</span>' : '<span class="back material-symbols-rounded">chevron_left</span>'
|
||||
|
||||
win.content.innerHTML = `
|
||||
<div style="padding: 5px;display: flex;align-items: center;gap: 5px;">
|
||||
${back}${dir}
|
||||
<div style="flex:1;"></div>
|
||||
<i class='folder bx bxs-folder-plus' style="font-size: 17.5px;"></i><i class='file bx bxs-file-plus' style="font-size: 17.5px;"></i>
|
||||
<i class='folder material-symbols-rounded' style="font-size: 17.5px;">create_new_folder</i><i class='file material-symbols-rounded' style="font-size: 17.5px;">note_add</i>
|
||||
</div>
|
||||
<div class="files" style="background: var(--base);flex: 1;border-radius: 10px;display: flex;flex-direction: column;"></div>
|
||||
`
|
||||
|
||||
if (back !== '<i class=\'bx bx-arrow-to-left\'></i>') {
|
||||
if (back !== '<span class="material-symbols-rounded">first_page</span>') {
|
||||
(win.content.querySelector('.back') as HTMLElement).onclick = async () => {
|
||||
if (dir.split('/')[1] === dir.replace('/', '')) {
|
||||
await setDir(`/${dir.split('/')[0]}`)
|
||||
|
|
@ -71,51 +71,45 @@ export default class FilesApp implements App {
|
|||
case 'js':
|
||||
case 'mjs':
|
||||
case 'cjs': {
|
||||
return '<i class=\'bx bxs-file-js\' ></i>'
|
||||
return '<span class="material-symbols-rounded">javascript</span>'
|
||||
}
|
||||
|
||||
case 'html':
|
||||
case 'htm': {
|
||||
return '<i class=\'bx bxs-file-html\' ></i>'
|
||||
return '<span class="material-symbols-rounded">html</span>'
|
||||
}
|
||||
|
||||
case 'css': {
|
||||
return '<i class=\'bx bxs-file-css\' ></i>'
|
||||
return '<span class="material-symbols-rounded">css</span>'
|
||||
}
|
||||
|
||||
case 'json': {
|
||||
return '<i class=\'bx bxs-file-json\' ></i>'
|
||||
return '<span class="material-symbols-rounded">code</span>'
|
||||
}
|
||||
|
||||
case 'md': {
|
||||
return '<i class=\'bx bxs-file-md\' ></i>'
|
||||
return '<span class="material-symbols-rounded">markdown</span>'
|
||||
}
|
||||
|
||||
case 'txt':
|
||||
case 'text': {
|
||||
return '<i class=\'bx bxs-file-txt\' ></i>'
|
||||
return '<span class="material-symbols-rounded">description</span>'
|
||||
}
|
||||
|
||||
case 'png':
|
||||
case 'apng': {
|
||||
return '<i class=\'bx bxs-file-png\' ></i>'
|
||||
}
|
||||
|
||||
case 'apng':
|
||||
case 'jpg':
|
||||
case 'jpeg': {
|
||||
return '<i class=\'bx bxs-file-jpg\' ></i>'
|
||||
}
|
||||
|
||||
case 'jpeg':
|
||||
case 'gif': {
|
||||
return '<i class=\'bx bxs-file-gif\' ></i>'
|
||||
return '<span class="material-symbols-rounded">image</span>'
|
||||
}
|
||||
|
||||
default: {
|
||||
return '<i class=\'bx bxs-file-blank\' ></i>'
|
||||
return '<span class="material-symbols-rounded">draft</span>'
|
||||
}
|
||||
}
|
||||
}
|
||||
const icon = fileStat.isDirectory() ? '<i class=\'bx bx-folder\'></i>' : genIcon()
|
||||
const icon = fileStat.isDirectory() ? '<span class="material-symbols-rounded">folder</span>' : genIcon()
|
||||
|
||||
element.innerHTML += `${icon} ${file}`
|
||||
element.onclick = async () => {
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
export const meta = {
|
||||
name: 'App Launcher',
|
||||
description: 'Opens the app launcher.',
|
||||
pkg: 'flow.applauncher',
|
||||
version: '1.0.0'
|
||||
}
|
||||
|
||||
export const run = (element: HTMLDivElement): void => {
|
||||
element.style.display = 'flex'
|
||||
element.style.alignItems = 'center'
|
||||
element.style.justifyContent = 'center'
|
||||
element.style.aspectRatio = '1 / 1'
|
||||
element.innerHTML = '<i class=\'bx bx-category\'></i>'
|
||||
|
||||
element.onclick = () => {
|
||||
window.wm.toggleLauncher()
|
||||
}
|
||||
}
|
||||
|
|
@ -9,18 +9,15 @@ export const meta = {
|
|||
|
||||
export const run = (element: HTMLDivElement): void => {
|
||||
element.style.display = 'flex'
|
||||
element.style.alignItems = 'center'
|
||||
element.style.gap = '5px'
|
||||
element.style.alignItems = 'bottom'
|
||||
element.style.flex = '1'
|
||||
|
||||
window.addEventListener('app_opened', (e: AppOpenedEvent): void => {
|
||||
const appIcon = document.createElement('app')
|
||||
const app = e.detail.app
|
||||
const win = e.detail.win
|
||||
appIcon.style.background = 'var(--surface-0)'
|
||||
appIcon.style.padding = '5px 7.5px'
|
||||
appIcon.style.borderRadius = '5px'
|
||||
appIcon.innerHTML = `<img data-id="${win.id}" src="${app.meta.icon}"/> ${app.meta.name}`
|
||||
appIcon.innerHTML = `<img data-id="${win.id}" src="${app.meta.icon}"/>`;
|
||||
(appIcon.querySelector('img') as HTMLElement).style.borderBottom = '2px solid var(--text)'
|
||||
appIcon.onclick = async () => {
|
||||
const win = await e.detail.win
|
||||
win.focus()
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
export const meta = {
|
||||
name: 'Battery',
|
||||
description: 'Tells you your device\'s battery.',
|
||||
pkg: 'flow.battery',
|
||||
version: '1.0.0'
|
||||
}
|
||||
|
||||
export const run = (element: HTMLDivElement): void => {
|
||||
element.style.display = 'flex'
|
||||
element.style.alignItems = 'center'
|
||||
element.style.paddingLeft = '15px'
|
||||
element.style.paddingRight = '15px'
|
||||
|
||||
if ('getBattery' in navigator) {
|
||||
// types don't exist for battery api
|
||||
// @ts-expect-error
|
||||
navigator.getBattery().then((battery) => {
|
||||
element.innerHTML = `🔋 ${(battery.level * 100).toFixed(0)}%`
|
||||
battery.addEventListener('', () => {
|
||||
element.innerHTML = `🔋 ${(battery.level * 100).toFixed(0)}%`
|
||||
})
|
||||
})
|
||||
} else {
|
||||
console.log('Battery API is not supported on this device')
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
export const meta = {
|
||||
name: 'Desktop Switcher',
|
||||
description: 'Allows you to switch between desktops.',
|
||||
pkg: 'flow.switcher',
|
||||
version: '1.0.0'
|
||||
}
|
||||
|
||||
export const run = (element: HTMLDivElement): void => {
|
||||
element.style.display = 'flex'
|
||||
element.style.gap = '10px'
|
||||
element.style.alignItems = 'center'
|
||||
element.style.paddingLeft = '15px'
|
||||
element.style.paddingRight = '15px'
|
||||
element.innerHTML = '<i class=\'bx bxs-dice-1\'></i><i class=\'bx bx-dice-2\'></i><i class=\'bx bx-dice-3\'></i>'
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
export const meta = {
|
||||
name: 'Weather',
|
||||
description: 'Tells you the weather.',
|
||||
pkg: 'flow.weather',
|
||||
version: '1.0.0'
|
||||
}
|
||||
|
||||
export const run = (element: HTMLDivElement): void => {
|
||||
element.style.display = 'flex'
|
||||
element.style.alignItems = 'center'
|
||||
element.style.paddingLeft = '15px'
|
||||
element.style.paddingRight = '15px'
|
||||
element.innerHTML = '☁️ 26*C'
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import 'material-symbols'
|
||||
import './assets/style.less'
|
||||
|
||||
import Preloader from './instances/Preloader'
|
||||
|
|
|
|||
|
|
@ -13,14 +13,7 @@ class Flow {
|
|||
]
|
||||
|
||||
plugins: LoadedPlugin[] = []
|
||||
pluginList: string[] = [
|
||||
'appLauncher',
|
||||
'apps',
|
||||
'weather',
|
||||
'clock',
|
||||
'switcher',
|
||||
'battery'
|
||||
]
|
||||
pluginList: string[] = []
|
||||
|
||||
/**
|
||||
* Initiates applications.
|
||||
|
|
@ -31,8 +24,9 @@ class Flow {
|
|||
|
||||
for (const appPath of this.appList) {
|
||||
window.preloader.setStatus(`importing default apps\n${appPath}`)
|
||||
const { default: ImportedApp } = await import(`../builtin/apps/${appPath}.ts`).catch((e: Error) => {
|
||||
const { default: ImportedApp } = await import(`../builtin/apps/${appPath}.ts`).catch(async (e: Error) => {
|
||||
console.error(e)
|
||||
await window.preloader.setError('apps')
|
||||
window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`)
|
||||
})
|
||||
const app = new ImportedApp()
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class Preloader {
|
|||
* @param value The name of an instance.
|
||||
*/
|
||||
setPending (value: string): void {
|
||||
(this.element.querySelector('.done') as HTMLElement).innerHTML += `<div class="${value.split(' ').join('-')}"><i class='icon bx bx-minus' ></i>${value}</div>`
|
||||
(this.element.querySelector('.done') as HTMLElement).innerHTML += `<div class="${value.split(' ').join('-')}"><span class='material-symbols-rounded'>check_indeterminate_small</span>${value}</div>`
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,9 +42,19 @@ class Preloader {
|
|||
* @param value The name of an instance.
|
||||
*/
|
||||
async setDone (value: string): Promise<void> {
|
||||
const icon = this.element.querySelector('.done')?.querySelector(`.${value.split(' ').join('-')}`)?.querySelector('.icon')
|
||||
icon?.classList.remove('bx-minus')
|
||||
icon?.classList.add('bx-check')
|
||||
const icon = this.element.querySelector('.done')?.querySelector(`.${value.split(' ').join('-')}`)?.querySelector('.material-symbols-rounded') as HTMLElement
|
||||
icon.innerHTML = 'check_small'
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets loading state of an instance to error.
|
||||
*
|
||||
* @param value The name of an instance.
|
||||
*/
|
||||
async setError (value: string): Promise<void> {
|
||||
const icon = this.element.querySelector('.done')?.querySelector(`.${value.split(' ').join('-')}`)?.querySelector('.material-symbols-rounded') as HTMLElement
|
||||
icon.innerHTML = 'close_small'
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
import { Plugin } from '../types'
|
||||
import { AppClosedEvent, AppOpenedEvent, Plugin } from '../types'
|
||||
import { getTime } from '../utils'
|
||||
|
||||
class StatusBar {
|
||||
element: HTMLElement
|
||||
|
|
@ -10,6 +11,151 @@ class StatusBar {
|
|||
constructor () {
|
||||
this.element = document.createElement('toolbar')
|
||||
|
||||
this.element.innerHTML = `
|
||||
<div class="outlined" data-toolbar-id="start"><span class="material-symbols-rounded">space_dashboard</span></div>
|
||||
${/* <div class="outlined" data-toolbar-id="widgets"><span class="material-symbols-rounded">widgets</span></div> */ ''}
|
||||
<div data-toolbar-id="apps"></div>
|
||||
<flex></flex>
|
||||
<div class="outlined" data-toolbar-id="plugins"><span class="material-symbols-rounded">expand_less</span></div>
|
||||
<div class="outlined" data-toolbar-id="controls">
|
||||
<span class="material-symbols-rounded battery">battery_2_bar</span>
|
||||
<span class="material-symbols-rounded signal">signal_cellular_4_bar</span>
|
||||
</div>
|
||||
<div class="outlined" data-toolbar-id="calendar"></div>
|
||||
${/* <div class="outlined" data-toolbar-id="notifications">
|
||||
<span class="material-symbols-rounded">notifications</span>
|
||||
</div> */ ''}
|
||||
`
|
||||
|
||||
setInterval((): any => {
|
||||
getTime().then((time) => {
|
||||
(this.element.querySelector('div[data-toolbar-id="calendar"]') as HTMLElement).innerText = time
|
||||
}).catch(e => console.error)
|
||||
}, 1000)
|
||||
|
||||
this.element.querySelector('div[data-toolbar-id="start"]')?.addEventListener('click', () => {
|
||||
window.wm.toggleLauncher()
|
||||
})
|
||||
|
||||
function updateBatteryIcon (battery: any): void {
|
||||
let iconHTML = ''
|
||||
|
||||
if (battery.charging === true) {
|
||||
if (battery.level === 1) {
|
||||
iconHTML = 'battery_charging_full'
|
||||
} else if (battery.level >= 0.9) {
|
||||
iconHTML = 'battery_charging_90'
|
||||
} else if (battery.level >= 0.8) {
|
||||
iconHTML = 'battery_charging_80'
|
||||
} else if (battery.level >= 0.7) {
|
||||
iconHTML = 'battery_charging_70'
|
||||
} else if (battery.level >= 0.6) {
|
||||
iconHTML = 'battery_charging_60'
|
||||
} else if (battery.level >= 0.5) {
|
||||
iconHTML = 'battery_charging_50'
|
||||
} else if (battery.level >= 0.4) {
|
||||
iconHTML = 'battery_charging_40'
|
||||
} else if (battery.level >= 0.3) {
|
||||
iconHTML = 'battery_charging_30'
|
||||
} else if (battery.level >= 0.2) {
|
||||
iconHTML = 'battery_charging_20'
|
||||
}
|
||||
} else {
|
||||
if (battery.level === 1) {
|
||||
iconHTML = 'battery_full'
|
||||
} else if (battery.level >= 0.6) {
|
||||
iconHTML = 'battery_6_bar'
|
||||
} else if (battery.level >= 0.5) {
|
||||
iconHTML = 'battery_5_bar'
|
||||
} else if (battery.level >= 0.4) {
|
||||
iconHTML = 'battery_4_bar'
|
||||
} else if (battery.level >= 0.3) {
|
||||
iconHTML = 'battery_3_bar'
|
||||
} else if (battery.level >= 0.2) {
|
||||
iconHTML = 'battery_2_bar'
|
||||
} else if (battery.level >= 0.1) {
|
||||
iconHTML = 'battery_1_bar'
|
||||
} else if (battery.level >= 0) {
|
||||
iconHTML = 'battery_0_bar'
|
||||
}
|
||||
}
|
||||
|
||||
const batteryDiv = document.querySelector('div[data-toolbar-id="controls"] > .battery')
|
||||
if (batteryDiv != null) {
|
||||
batteryDiv.innerHTML = iconHTML
|
||||
}
|
||||
}
|
||||
|
||||
if ('getBattery' in navigator) {
|
||||
(navigator as any).getBattery().then(function (battery: any) {
|
||||
updateBatteryIcon(battery)
|
||||
|
||||
battery.addEventListener('levelchange', function () {
|
||||
updateBatteryIcon(battery)
|
||||
})
|
||||
|
||||
battery.addEventListener('chargingchange', function () {
|
||||
updateBatteryIcon(battery)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
const batteryDiv = document.querySelector('div[data-toolbar-id="controls"] > .battery')
|
||||
if (batteryDiv != null) {
|
||||
batteryDiv.innerHTML = 'battery_unknown'
|
||||
}
|
||||
}
|
||||
|
||||
function updateIcon (ms: number): void {
|
||||
let icon = ''
|
||||
|
||||
if (ms >= 200 && ms < 400) {
|
||||
icon = 'signal_cellular_1_bar'
|
||||
} else if (ms >= 400 && ms < 600) {
|
||||
icon = 'signal_cellular_2_bar'
|
||||
} else if (ms >= 600 && ms < 800) {
|
||||
icon = 'signal_cellular_3_bar'
|
||||
} else if (ms >= 800) {
|
||||
icon = 'signal_cellular_4_bar'
|
||||
} else {
|
||||
icon = 'signal_cellular_0_bar'
|
||||
}
|
||||
|
||||
(document.querySelector('div[data-toolbar-id="controls"] > .signal') as HTMLElement).innerHTML = icon
|
||||
}
|
||||
|
||||
async function ping (startTime: number): Promise<void> {
|
||||
fetch((await window.config()).SERVER_URL + '/bare/')
|
||||
.then(() => {
|
||||
const endTime = performance.now()
|
||||
const pingTime = endTime - startTime
|
||||
updateIcon(pingTime)
|
||||
})
|
||||
.catch(() => {
|
||||
(document.querySelector('div[data-toolbar-id="controls"] > .signal') as HTMLElement).innerHTML = 'signal_cellular_connected_no_internet_4_bar'
|
||||
})
|
||||
}
|
||||
|
||||
setInterval((): any => ping(performance.now()), 10000)
|
||||
|
||||
window.addEventListener('app_opened', (e: AppOpenedEvent): void => {
|
||||
const appIcon = document.createElement('app')
|
||||
const app = e.detail.app
|
||||
const win = e.detail.win
|
||||
appIcon.innerHTML = `<img data-id="${win.id}" src="${app.meta.icon}"/>`
|
||||
appIcon.onclick = async () => {
|
||||
const win = await e.detail.win
|
||||
win.focus()
|
||||
win.toggleMin()
|
||||
}
|
||||
this.element.querySelector('div[data-toolbar-id="apps"]')?.appendChild(appIcon)
|
||||
})
|
||||
|
||||
window.addEventListener('app_closed', (e: AppClosedEvent): void => {
|
||||
const win = e.detail.win
|
||||
this.element.querySelector('div[data-toolbar-id="apps"]')?.querySelector(`img[data-id="${win.id}"]`)?.parentElement?.remove()
|
||||
})
|
||||
|
||||
document.body.style.flexDirection = 'column-reverse'
|
||||
document.body.appendChild(this.element)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,9 +108,9 @@ class FlowWindow {
|
|||
this.element.style.height = `${config.height ?? 200}px`
|
||||
|
||||
this.header = document.createElement('window-header')
|
||||
this.header.innerHTML = `<img src="${config.icon}"></img> <div class="title">${config.title}</div><div style="flex:1;"></div><i id="min" class='bx bx-minus'></i><i id="close" class='bx bx-x'></i>`
|
||||
this.header.innerHTML = `<img src="${config.icon}"></img> <div class="title">${config.title}</div><div style="flex:1;"></div><i id="min" class='material-symbols-rounded' style="margin-bottom: 5px;">minimize</i><i id="close" class='material-symbols-rounded'>close</i>`
|
||||
if (config.canResize) {
|
||||
this.header.innerHTML = `<img src="${config.icon}"></img> <div class="title">${config.title}</div><div style="flex:1;"></div><i id="min" class='bx bx-minus'></i><i id="max" class='bx bx-checkbox'></i><i id="close" class='bx bx-x'></i>`
|
||||
this.header.innerHTML = `<img src="${config.icon}"></img> <div class="title">${config.title}</div><div style="flex:1;"></div><i id="min" class='material-symbols-rounded' style="margin-bottom: 5px;">minimize</i><i id="max" class='material-symbols-rounded' style="font-size: 20px;">square</i><i id="close" class='material-symbols-rounded'>close</i>`
|
||||
}
|
||||
|
||||
(this.header.querySelector('#close') as HTMLElement).onclick = () => {
|
||||
|
|
@ -126,8 +126,17 @@ class FlowWindow {
|
|||
this.realContent = document.createElement('window-content')
|
||||
const shadow = this.realContent.attachShadow({ mode: 'open' })
|
||||
shadow.innerHTML = `
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||
<style>
|
||||
.material-symbols-rounded {
|
||||
font-variation-settings:
|
||||
'FILL' 0,
|
||||
'wght' 400,
|
||||
'GRAD' 200,
|
||||
'opsz' 48
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
@import url(https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css);.bx {font-size: 25px;}
|
||||
* {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue