Merge pull request #76 from Flow-Works/74-rework-status-bar

[💄] UI Overhaul
This commit is contained in:
ThinLiquid 2023-12-04 09:41:10 +00:00 committed by GitHub
commit 16abfe4278
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 271 additions and 165 deletions

6
package-lock.json generated
View file

@ -12,6 +12,7 @@
"@ptkdev/logger": "^1.8.0",
"eruda": "^3.0.1",
"filer": "^1.4.1",
"material-symbols": "^0.14.1",
"prism-code-editor": "^2.2.0",
"uuid": "^9.0.1"
},
@ -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",

View file

@ -31,6 +31,7 @@
"@ptkdev/logger": "^1.8.0",
"eruda": "^3.0.1",
"filer": "^1.4.1",
"material-symbols": "^0.14.1",
"prism-code-editor": "^2.2.0",
"uuid": "^9.0.1"
},

View file

@ -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 {
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;
&.outlined {
padding: 10px;
background: var(--base);
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;
}

View file

@ -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'))

View file

@ -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>

View file

@ -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 () => {

View file

@ -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()
}
}

View file

@ -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()

View file

@ -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')
}
}

View file

@ -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>'
}

View file

@ -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'
}

View file

@ -1,3 +1,4 @@
import 'material-symbols'
import './assets/style.less'
import Preloader from './instances/Preloader'

View file

@ -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()

View file

@ -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))
}

View file

@ -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)
}

View file

@ -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;