Merge pull request #63 from Flow-Works/61-organize-interfaces-types-classes-etc
[🎨] Major codebase overhaul
This commit is contained in:
commit
52875af182
23 changed files with 257 additions and 151 deletions
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "flow-os",
|
"name": "flowos",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "flow-os",
|
"name": "flowos",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "flow-os",
|
"name": "flowos",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "The future of FlowOS.",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "ts-standard",
|
"test": "ts-standard",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import icon from '../assets/icons/null.png'
|
import icon from '../../assets/icons/null.png'
|
||||||
import { App } from '../types.ts'
|
import { App } from '../../types'
|
||||||
|
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
|
||||||
export default class BrowserApp implements App {
|
export default class BrowserApp implements App {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import icon from '../assets/icons/editor.png'
|
import icon from '../../assets/icons/editor.png'
|
||||||
import { App } from '../types.ts'
|
import { App } from '../../types'
|
||||||
|
|
||||||
import { fullEditor } from 'prism-code-editor/setups'
|
import { fullEditor } from 'prism-code-editor/setups'
|
||||||
// this will also import markup, clike, javascript, typescript and jsx
|
// this will also import markup, clike, javascript, typescript and jsx
|
||||||
|
|
@ -8,7 +8,7 @@ import 'prism-code-editor/grammars/css-extras'
|
||||||
import 'prism-code-editor/grammars/markdown'
|
import 'prism-code-editor/grammars/markdown'
|
||||||
import 'prism-code-editor/grammars/python'
|
import 'prism-code-editor/grammars/python'
|
||||||
|
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
|
||||||
interface EditorConfig {
|
interface EditorConfig {
|
||||||
path: string
|
path: string
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import icon from '../assets/icons/files.png'
|
import icon from '../../assets/icons/files.png'
|
||||||
import { App } from '../types.ts'
|
import { App } from '../../types'
|
||||||
|
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
|
||||||
import { Stats } from 'fs'
|
import { Stats } from 'fs'
|
||||||
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import icon from '../assets/icons/info.png'
|
import icon from '../../assets/icons/info.png'
|
||||||
import badge from '../assets/badge.png'
|
import badge from '../../assets/badge.png'
|
||||||
import { App, PackageJSON } from '../types.ts'
|
import { App, PackageJSON } from '../../types'
|
||||||
|
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
|
||||||
export default class InfoApp implements App {
|
export default class InfoApp implements App {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -14,7 +14,7 @@ export default class InfoApp implements App {
|
||||||
}
|
}
|
||||||
|
|
||||||
async open (): Promise<FlowWindow> {
|
async open (): Promise<FlowWindow> {
|
||||||
const packageJSON: PackageJSON = await import('../../package.json')
|
const packageJSON: PackageJSON = await import('../../../package.json')
|
||||||
const win = window.wm.createWindow({
|
const win = window.wm.createWindow({
|
||||||
title: this.meta.name,
|
title: this.meta.name,
|
||||||
icon: this.meta.icon,
|
icon: this.meta.icon,
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import icon from '../assets/icons/manager.png'
|
import icon from '../../assets/icons/manager.png'
|
||||||
import { App, LoadedApp, LoadedPlugin } from '../types.ts'
|
import { App, LoadedApp, LoadedPlugin } from '../../types'
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
import nullIcon from '../assets/icons/null.png'
|
import nullIcon from '../../assets/icons/null.png'
|
||||||
|
|
||||||
export default class ManagerApp implements App {
|
export default class ManagerApp implements App {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import icon from '../assets/icons/music.png'
|
import icon from '../../assets/icons/music.png'
|
||||||
import { App } from '../types.ts'
|
import { App } from '../../types'
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
|
||||||
export default class MusicApp implements App {
|
export default class MusicApp implements App {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import icon from '../assets/icons/settings.png'
|
import icon from '../../assets/icons/settings.png'
|
||||||
import { App } from '../types.ts'
|
import { App } from '../../types'
|
||||||
import { FlowWindow } from '../wm.ts'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
|
||||||
export default class SettingsApp implements App {
|
export default class SettingsApp implements App {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { AppOpenedEvent, AppClosedEvent } from '../types'
|
import { AppOpenedEvent, AppClosedEvent } from '../../types'
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
name: 'Apps',
|
name: 'Apps',
|
||||||
14
src/index.ts
14
src/index.ts
|
|
@ -1,9 +1,9 @@
|
||||||
import './style.less'
|
import './assets/style.less'
|
||||||
|
|
||||||
import Preloader from './preloader'
|
import Preloader from './instances/Preloader'
|
||||||
import StatusBar from './statusbar'
|
import StatusBar from './instances/StatusBar'
|
||||||
import WM from './wm'
|
import WindowManager from './instances/WindowManager'
|
||||||
import Flow from './flow'
|
import Flow from './instances/Flow'
|
||||||
|
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ declare global {
|
||||||
flow: Flow
|
flow: Flow
|
||||||
fs: typeof fs
|
fs: typeof fs
|
||||||
statusBar: StatusBar
|
statusBar: StatusBar
|
||||||
wm: WM
|
wm: WindowManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ if (params.get('debug') !== null && params.get('debug') !== undefined) {
|
||||||
window.preloader = new Preloader()
|
window.preloader = new Preloader()
|
||||||
window.flow = new Flow()
|
window.flow = new Flow()
|
||||||
window.statusBar = new StatusBar()
|
window.statusBar = new StatusBar()
|
||||||
window.wm = new WM();
|
window.wm = new WindowManager();
|
||||||
|
|
||||||
(async function () {
|
(async function () {
|
||||||
window.preloader.setPending('filesystem')
|
window.preloader.setPending('filesystem')
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { LoadedApp, LoadedPlugin } from './types.ts'
|
import { LoadedApp, LoadedPlugin } from '../types'
|
||||||
|
|
||||||
class Flow {
|
class Flow {
|
||||||
apps: LoadedApp[] = []
|
apps: LoadedApp[] = []
|
||||||
|
|
@ -22,13 +22,16 @@ class Flow {
|
||||||
'battery'
|
'battery'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates applications.
|
||||||
|
*/
|
||||||
private async initApps (): Promise<void> {
|
private async initApps (): Promise<void> {
|
||||||
window.preloader.setPending('apps')
|
window.preloader.setPending('apps')
|
||||||
window.preloader.setStatus('importing default apps...')
|
window.preloader.setStatus('importing default apps...')
|
||||||
|
|
||||||
for (const appPath of this.appList) {
|
for (const appPath of this.appList) {
|
||||||
window.preloader.setStatus(`importing default apps\n${appPath}`)
|
window.preloader.setStatus(`importing default apps\n${appPath}`)
|
||||||
const { default: ImportedApp } = await import(`./apps/${appPath}.ts`).catch((e: Error) => {
|
const { default: ImportedApp } = await import(`../builtin/apps/${appPath}.ts`).catch((e: Error) => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`)
|
window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`)
|
||||||
})
|
})
|
||||||
|
|
@ -61,13 +64,16 @@ class Flow {
|
||||||
await window.preloader.setDone('apps')
|
await window.preloader.setDone('apps')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates plugins.
|
||||||
|
*/
|
||||||
private async initPlugins (): Promise<void> {
|
private async initPlugins (): Promise<void> {
|
||||||
window.preloader.setPending('plugins')
|
window.preloader.setPending('plugins')
|
||||||
window.preloader.setStatus('importing default plugins...')
|
window.preloader.setStatus('importing default plugins...')
|
||||||
|
|
||||||
for (const pluginPath of this.pluginList) {
|
for (const pluginPath of this.pluginList) {
|
||||||
window.preloader.setStatus(`importing default plugins\n${pluginPath}`)
|
window.preloader.setStatus(`importing default plugins\n${pluginPath}`)
|
||||||
const plugin = await import(`./plugins/${pluginPath}.ts`).catch((e: Error) => {
|
const plugin = await import(`../builtin/plugins/${pluginPath}.ts`).catch((e: Error) => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
window.preloader.setStatus(`unable to import ${pluginPath}\n${e.name}: ${e.message}`)
|
window.preloader.setStatus(`unable to import ${pluginPath}\n${e.name}: ${e.message}`)
|
||||||
})
|
})
|
||||||
|
|
@ -79,11 +85,19 @@ class Flow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates the Flow session.
|
||||||
|
*/
|
||||||
async init (): Promise<void> {
|
async init (): Promise<void> {
|
||||||
await this.initApps()
|
await this.initApps()
|
||||||
await this.initPlugins()
|
await this.initPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an app.
|
||||||
|
*
|
||||||
|
* @param app The app to be registered.
|
||||||
|
*/
|
||||||
addApp (app: LoadedApp): void {
|
addApp (app: LoadedApp): void {
|
||||||
if (this.apps.some(x => x.meta.pkg === app.meta.pkg)) {
|
if (this.apps.some(x => x.meta.pkg === app.meta.pkg)) {
|
||||||
console.error(`Unable to register app; ${app.meta.pkg} is already registered.`)
|
console.error(`Unable to register app; ${app.meta.pkg} is already registered.`)
|
||||||
|
|
@ -93,6 +107,11 @@ class Flow {
|
||||||
this.apps.push(app)
|
this.apps.push(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a plugin.
|
||||||
|
*
|
||||||
|
* @param plugin The plugin to be registered.
|
||||||
|
*/
|
||||||
addPlugin (plugin: LoadedPlugin): void {
|
addPlugin (plugin: LoadedPlugin): void {
|
||||||
if (window.flow.plugins.some(x => x.meta.pkg === plugin.meta.pkg)) {
|
if (window.flow.plugins.some(x => x.meta.pkg === plugin.meta.pkg)) {
|
||||||
console.error(`Unable to register tool; ${plugin.meta.pkg} is already registered.`)
|
console.error(`Unable to register tool; ${plugin.meta.pkg} is already registered.`)
|
||||||
|
|
@ -101,6 +120,12 @@ class Flow {
|
||||||
this.plugins.push(plugin)
|
this.plugins.push(plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a registered application.
|
||||||
|
*
|
||||||
|
* @param pkg The PKG ID of the application.
|
||||||
|
* @param data Payload info for app to recieve.
|
||||||
|
*/
|
||||||
async openApp (pkg: string, data?: any): Promise<void> {
|
async openApp (pkg: string, data?: any): Promise<void> {
|
||||||
const app = this.apps.find(x => x.meta.pkg === pkg)
|
const app = this.apps.find(x => x.meta.pkg === pkg)
|
||||||
const win = app?.open(data)
|
const win = app?.open(data)
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import flowIcon from './assets/flow.png'
|
import flowIcon from '../assets/flow.png'
|
||||||
|
|
||||||
class Preloader {
|
class Preloader {
|
||||||
element: HTMLElement
|
element: HTMLElement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the preloader.
|
||||||
|
*/
|
||||||
constructor () {
|
constructor () {
|
||||||
this.element = document.createElement('preloader')
|
this.element = document.createElement('preloader')
|
||||||
|
|
||||||
|
|
@ -15,14 +18,29 @@ class Preloader {
|
||||||
document.body.appendChild(this.element)
|
document.body.appendChild(this.element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the status text of the preloader.
|
||||||
|
*
|
||||||
|
* @param value The preloader status text.
|
||||||
|
*/
|
||||||
setStatus (value: string): void {
|
setStatus (value: string): void {
|
||||||
(this.element.querySelector('.status') as HTMLElement).innerText = value
|
(this.element.querySelector('.status') as HTMLElement).innerText = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets loading state of an instance to pending.
|
||||||
|
*
|
||||||
|
* @param value The name of an instance.
|
||||||
|
*/
|
||||||
setPending (value: string): void {
|
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('-')}"><i class='icon bx bx-minus' ></i>${value}</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets loading state of an instance to done.
|
||||||
|
*
|
||||||
|
* @param value The name of an instance.
|
||||||
|
*/
|
||||||
async setDone (value: string): Promise<void> {
|
async setDone (value: string): Promise<void> {
|
||||||
const icon = this.element.querySelector('.done')?.querySelector(`.${value.split(' ').join('-')}`)?.querySelector('.icon')
|
const icon = this.element.querySelector('.done')?.querySelector(`.${value.split(' ').join('-')}`)?.querySelector('.icon')
|
||||||
icon?.classList.remove('bx-minus')
|
icon?.classList.remove('bx-minus')
|
||||||
|
|
@ -30,6 +48,9 @@ class Preloader {
|
||||||
await new Promise(resolve => setTimeout(resolve, 300))
|
await new Promise(resolve => setTimeout(resolve, 300))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the preloader.
|
||||||
|
*/
|
||||||
finish (): void {
|
finish (): void {
|
||||||
this.element.style.opacity = '0'
|
this.element.style.opacity = '0'
|
||||||
this.element.style.pointerEvents = 'none'
|
this.element.style.pointerEvents = 'none'
|
||||||
|
|
@ -1,15 +1,23 @@
|
||||||
|
|
||||||
import { Plugin } from './types'
|
import { Plugin } from '../types'
|
||||||
|
|
||||||
class StatusBar {
|
class StatusBar {
|
||||||
element: HTMLElement
|
element: HTMLElement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the status bar.
|
||||||
|
*/
|
||||||
constructor () {
|
constructor () {
|
||||||
this.element = document.createElement('toolbar')
|
this.element = document.createElement('toolbar')
|
||||||
|
|
||||||
document.body.appendChild(this.element)
|
document.body.appendChild(this.element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a plugin to the status bar.
|
||||||
|
*
|
||||||
|
* @param item The plugin to be added to the status bar.
|
||||||
|
*/
|
||||||
async add (item: Plugin): Promise<void> {
|
async add (item: Plugin): Promise<void> {
|
||||||
const element = document.createElement('div')
|
const element = document.createElement('div')
|
||||||
element.setAttribute('data-toolbar-id', item.meta.pkg)
|
element.setAttribute('data-toolbar-id', item.meta.pkg)
|
||||||
|
|
@ -19,6 +27,9 @@ class StatusBar {
|
||||||
await item.run(element)
|
await item.run(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates the status bar.
|
||||||
|
*/
|
||||||
async init (): Promise<void> {
|
async init (): Promise<void> {
|
||||||
window.preloader.setStatus('adding plugins to statusbar...')
|
window.preloader.setStatus('adding plugins to statusbar...')
|
||||||
|
|
||||||
117
src/instances/WindowManager.ts
Normal file
117
src/instances/WindowManager.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
import FlowWindow from '../structures/FlowWindow'
|
||||||
|
import { FlowWindowConfig } from '../types'
|
||||||
|
|
||||||
|
class WindowManager {
|
||||||
|
private isLauncherOpen = false
|
||||||
|
windowArea: HTMLElement
|
||||||
|
launcher: HTMLElement
|
||||||
|
windows: FlowWindow[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the window container.
|
||||||
|
*/
|
||||||
|
constructor () {
|
||||||
|
this.windowArea = document.createElement('window-area')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the highest window's z-index.
|
||||||
|
*
|
||||||
|
* @returns The heighest window's z-index.
|
||||||
|
*/
|
||||||
|
getHighestZIndex (): number {
|
||||||
|
const indexes = this.windows.map((win: FlowWindow) => {
|
||||||
|
return parseInt(win.element.style.zIndex)
|
||||||
|
})
|
||||||
|
|
||||||
|
const max = Math.max(...indexes)
|
||||||
|
|
||||||
|
return max === -Infinity ? 0 : max
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a window.
|
||||||
|
*
|
||||||
|
* @param config The config for the window to follow.
|
||||||
|
* @returns The created window.
|
||||||
|
*/
|
||||||
|
createWindow (config: FlowWindowConfig): FlowWindow {
|
||||||
|
const win = new FlowWindow(this, config)
|
||||||
|
this.windows.push(win)
|
||||||
|
this.windowArea.appendChild(win.element)
|
||||||
|
return win
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the app launcher.
|
||||||
|
*/
|
||||||
|
toggleLauncher (): boolean {
|
||||||
|
if (this.isLauncherOpen) {
|
||||||
|
this.launcher.style.opacity = '0'
|
||||||
|
this.launcher.style.backdropFilter = 'blur(0px)'
|
||||||
|
this.launcher.style.pointerEvents = 'none'
|
||||||
|
} else {
|
||||||
|
this.launcher.style.opacity = '1'
|
||||||
|
this.launcher.style.backdropFilter = 'blur(20px)'
|
||||||
|
this.launcher.style.pointerEvents = 'all'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLauncherOpen = !this.isLauncherOpen
|
||||||
|
return this.isLauncherOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates the window manager.
|
||||||
|
*/
|
||||||
|
async init (): Promise<void> {
|
||||||
|
window.preloader.setPending('window manager')
|
||||||
|
window.preloader.setStatus('creating app launcher...')
|
||||||
|
this.launcher = document.createElement('launcher')
|
||||||
|
|
||||||
|
this.launcher.innerHTML = `
|
||||||
|
<input placeholder="Search"/>
|
||||||
|
<apps></apps>
|
||||||
|
`;
|
||||||
|
|
||||||
|
(this.launcher.querySelector('input') as HTMLInputElement).onkeyup = () => {
|
||||||
|
(this.launcher.querySelector('apps') as HTMLElement).innerHTML = ''
|
||||||
|
if ((this.launcher.querySelector('input') as HTMLInputElement).value !== '') {
|
||||||
|
window.flow.apps.filter(x => x.meta.name.toLowerCase().includes((this.launcher.querySelector('input') as HTMLInputElement).value.toLowerCase())).forEach((app) => {
|
||||||
|
const appElement = document.createElement('app')
|
||||||
|
appElement.onclick = async () => {
|
||||||
|
await window.flow.openApp(app.meta.pkg)
|
||||||
|
this.toggleLauncher()
|
||||||
|
}
|
||||||
|
appElement.innerHTML = `<img src="${app.meta.icon}"><div>${app.meta.name}</div>`
|
||||||
|
this.launcher.querySelector('apps')?.appendChild(appElement)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
window.flow.apps.forEach((app) => {
|
||||||
|
window.preloader.setStatus(`adding apps to app launcher\n${app.meta.name}`)
|
||||||
|
const appElement = document.createElement('app')
|
||||||
|
appElement.onclick = async () => {
|
||||||
|
await window.flow.openApp(app.meta.pkg)
|
||||||
|
window.wm.toggleLauncher()
|
||||||
|
}
|
||||||
|
appElement.innerHTML = `<img src="${app.meta.icon}"><div>${app.meta.name}</div>`
|
||||||
|
window.wm.launcher.querySelector('apps')?.appendChild(appElement)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.launcher.onclick = (e) => {
|
||||||
|
if (e.target !== e.currentTarget) return
|
||||||
|
this.toggleLauncher()
|
||||||
|
}
|
||||||
|
|
||||||
|
(this.launcher.querySelector('apps') as HTMLElement).onclick = (e) => {
|
||||||
|
if (e.target !== e.currentTarget) return
|
||||||
|
this.toggleLauncher()
|
||||||
|
}
|
||||||
|
|
||||||
|
await window.preloader.setDone('window manager')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WindowManager
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import { FlowWindowConfig } from './types.ts'
|
import WindowManager from '../instances/WindowManager'
|
||||||
|
import { FlowWindowConfig } from '../types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes an element draggable.
|
||||||
|
*
|
||||||
|
* @param element The element to become draggable.
|
||||||
|
* @param container The draggable element container.
|
||||||
|
*/
|
||||||
function dragElement (element: HTMLElement, container: HTMLElement): void {
|
function dragElement (element: HTMLElement, container: HTMLElement): void {
|
||||||
let posX = 0; let posY = 0
|
let posX = 0; let posY = 0
|
||||||
|
|
||||||
|
|
@ -53,7 +60,7 @@ function dragElement (element: HTMLElement, container: HTMLElement): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FlowWindow {
|
class FlowWindow {
|
||||||
element: HTMLElement
|
element: HTMLElement
|
||||||
|
|
||||||
private readonly header: HTMLElement
|
private readonly header: HTMLElement
|
||||||
|
|
@ -69,19 +76,25 @@ export class FlowWindow {
|
||||||
isMinimized = false
|
isMinimized = false
|
||||||
isMaximized = false
|
isMaximized = false
|
||||||
|
|
||||||
wm: WM
|
wm: WindowManager
|
||||||
|
|
||||||
id = uuid()
|
id = uuid()
|
||||||
|
|
||||||
config: FlowWindowConfig
|
config: FlowWindowConfig
|
||||||
|
|
||||||
constructor (wm: WM, config: FlowWindowConfig) {
|
/**
|
||||||
|
* Creates a window session.
|
||||||
|
*
|
||||||
|
* @param wm The current window manager session.
|
||||||
|
* @param config The window's pre-set config.
|
||||||
|
*/
|
||||||
|
constructor (wm: WindowManager, config: FlowWindowConfig) {
|
||||||
this.wm = wm
|
this.wm = wm
|
||||||
this.config = config
|
this.config = config
|
||||||
|
|
||||||
this.element = document.createElement('window')
|
this.element = document.createElement('window')
|
||||||
|
|
||||||
this.element.style.zIndex = (wm.getHighestZIndex() + 1).toString()
|
this.element.style.zIndex = (window.wm.getHighestZIndex() + 1).toString()
|
||||||
this.element.style.position = 'absolute'
|
this.element.style.position = 'absolute'
|
||||||
this.focus()
|
this.focus()
|
||||||
|
|
||||||
|
|
@ -141,6 +154,11 @@ export class FlowWindow {
|
||||||
dragElement(this.element, (document.querySelector('window-area') as HTMLElement))
|
dragElement(this.element, (document.querySelector('window-area') as HTMLElement))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the window's minimization.
|
||||||
|
*
|
||||||
|
* @returns The window's current minimization state.
|
||||||
|
*/
|
||||||
toggleMin (): boolean {
|
toggleMin (): boolean {
|
||||||
if (this.isMinimized) {
|
if (this.isMinimized) {
|
||||||
this.element.style.pointerEvents = 'all'
|
this.element.style.pointerEvents = 'all'
|
||||||
|
|
@ -158,6 +176,12 @@ export class FlowWindow {
|
||||||
private prevLeft: string
|
private prevLeft: string
|
||||||
private prevWidth: string
|
private prevWidth: string
|
||||||
private prevHeight: string
|
private prevHeight: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the window's maximization.
|
||||||
|
*
|
||||||
|
* @returns The window's current maximization state.
|
||||||
|
*/
|
||||||
toggleMax (): boolean {
|
toggleMax (): boolean {
|
||||||
if (this.isMaximized) {
|
if (this.isMaximized) {
|
||||||
this.element.style.width = this.prevWidth
|
this.element.style.width = this.prevWidth
|
||||||
|
|
@ -180,113 +204,32 @@ export class FlowWindow {
|
||||||
return this.isMaximized
|
return this.isMaximized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the window.
|
||||||
|
*/
|
||||||
focus (): void {
|
focus (): void {
|
||||||
if (this.element.style.zIndex !== this.wm.getHighestZIndex().toString()) {
|
if (this.element.style.zIndex !== this.wm.getHighestZIndex().toString()) {
|
||||||
this.element.style.zIndex = (this.wm.getHighestZIndex() + 1).toString()
|
this.element.style.zIndex = (this.wm.getHighestZIndex() + 1).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the window.
|
||||||
|
*/
|
||||||
close (): void {
|
close (): void {
|
||||||
this.element.remove()
|
this.element.remove()
|
||||||
const event = new CustomEvent('app_closed', { detail: { win: this } })
|
const event = new CustomEvent('app_closed', { detail: { win: this } })
|
||||||
window.dispatchEvent(event)
|
window.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the title of the window.
|
||||||
|
*
|
||||||
|
* @param title - The desired window title.
|
||||||
|
*/
|
||||||
setTitle (title: string): void {
|
setTitle (title: string): void {
|
||||||
(this.header.querySelector('.title') as HTMLElement).innerText = title
|
(this.header.querySelector('.title') as HTMLElement).innerText = title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WM {
|
export default FlowWindow
|
||||||
private isLauncherOpen = false
|
|
||||||
windowArea: HTMLElement
|
|
||||||
launcher: HTMLElement
|
|
||||||
windows: FlowWindow[] = []
|
|
||||||
|
|
||||||
constructor () {
|
|
||||||
this.windowArea = document.createElement('window-area')
|
|
||||||
}
|
|
||||||
|
|
||||||
getHighestZIndex (): number {
|
|
||||||
const indexes = this.windows.map((win: FlowWindow) => {
|
|
||||||
return parseInt(win.element.style.zIndex)
|
|
||||||
})
|
|
||||||
|
|
||||||
const max = Math.max(...indexes)
|
|
||||||
|
|
||||||
return max === -Infinity ? 0 : max
|
|
||||||
}
|
|
||||||
|
|
||||||
createWindow (config: FlowWindowConfig): FlowWindow {
|
|
||||||
const win = new FlowWindow(this, config)
|
|
||||||
this.windows.push(win)
|
|
||||||
this.windowArea.appendChild(win.element)
|
|
||||||
return win
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleLauncher (): boolean {
|
|
||||||
if (this.isLauncherOpen) {
|
|
||||||
this.launcher.style.opacity = '0'
|
|
||||||
this.launcher.style.backdropFilter = 'blur(0px)'
|
|
||||||
this.launcher.style.pointerEvents = 'none'
|
|
||||||
} else {
|
|
||||||
this.launcher.style.opacity = '1'
|
|
||||||
this.launcher.style.backdropFilter = 'blur(20px)'
|
|
||||||
this.launcher.style.pointerEvents = 'all'
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isLauncherOpen = !this.isLauncherOpen
|
|
||||||
return this.isLauncherOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
async init (): Promise<void> {
|
|
||||||
window.preloader.setPending('window manager')
|
|
||||||
window.preloader.setStatus('creating app launcher...')
|
|
||||||
this.launcher = document.createElement('launcher')
|
|
||||||
|
|
||||||
this.launcher.innerHTML = `
|
|
||||||
<input placeholder="Search"/>
|
|
||||||
<apps></apps>
|
|
||||||
`;
|
|
||||||
|
|
||||||
(this.launcher.querySelector('input') as HTMLInputElement).onkeyup = () => {
|
|
||||||
(this.launcher.querySelector('apps') as HTMLElement).innerHTML = ''
|
|
||||||
if ((this.launcher.querySelector('input') as HTMLInputElement).value !== '') {
|
|
||||||
window.flow.apps.filter(x => x.meta.name.toLowerCase().includes((this.launcher.querySelector('input') as HTMLInputElement).value.toLowerCase())).forEach((app) => {
|
|
||||||
const appElement = document.createElement('app')
|
|
||||||
appElement.onclick = async () => {
|
|
||||||
await window.flow.openApp(app.meta.pkg)
|
|
||||||
this.toggleLauncher()
|
|
||||||
}
|
|
||||||
appElement.innerHTML = `<img src="${app.meta.icon}"><div>${app.meta.name}</div>`
|
|
||||||
this.launcher.querySelector('apps')?.appendChild(appElement)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
window.flow.apps.forEach((app) => {
|
|
||||||
window.preloader.setStatus(`adding apps to app launcher\n${app.meta.name}`)
|
|
||||||
const appElement = document.createElement('app')
|
|
||||||
appElement.onclick = async () => {
|
|
||||||
await window.flow.openApp(app.meta.pkg)
|
|
||||||
window.wm.toggleLauncher()
|
|
||||||
}
|
|
||||||
appElement.innerHTML = `<img src="${app.meta.icon}"><div>${app.meta.name}</div>`
|
|
||||||
window.wm.launcher.querySelector('apps')?.appendChild(appElement)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.launcher.onclick = (e) => {
|
|
||||||
if (e.target !== e.currentTarget) return
|
|
||||||
this.toggleLauncher()
|
|
||||||
}
|
|
||||||
|
|
||||||
(this.launcher.querySelector('apps') as HTMLElement).onclick = (e) => {
|
|
||||||
if (e.target !== e.currentTarget) return
|
|
||||||
this.toggleLauncher()
|
|
||||||
}
|
|
||||||
|
|
||||||
await window.preloader.setDone('window manager')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WM
|
|
||||||
13
src/types.ts
13
src/types.ts
|
|
@ -1,10 +1,8 @@
|
||||||
import { FlowWindow } from './wm'
|
import FlowWindow from './structures/FlowWindow'
|
||||||
|
|
||||||
export type AppOpenFunction = (data: any) => Promise<FlowWindow>
|
export type AppOpenFunction = (data: any) => Promise<FlowWindow>
|
||||||
export type PluginRunFunction = (element: HTMLDivElement) => void | Promise<void>
|
export type PluginRunFunction = (element: HTMLDivElement) => void | Promise<void>
|
||||||
|
|
||||||
/* EVENTS */
|
|
||||||
|
|
||||||
export interface AppClosedEvent extends CustomEvent {
|
export interface AppClosedEvent extends CustomEvent {
|
||||||
detail: {
|
detail: {
|
||||||
win: FlowWindow
|
win: FlowWindow
|
||||||
|
|
@ -17,9 +15,6 @@ export interface AppOpenedEvent extends CustomEvent {
|
||||||
win: FlowWindow
|
win: FlowWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* METADATA */
|
|
||||||
|
|
||||||
export interface BaseMeta {
|
export interface BaseMeta {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
|
|
@ -35,8 +30,6 @@ export interface PluginMeta extends BaseMeta {
|
||||||
icon?: string
|
icon?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OBJECTS */
|
|
||||||
|
|
||||||
export interface Apps {
|
export interface Apps {
|
||||||
[key: string]: App
|
[key: string]: App
|
||||||
}
|
}
|
||||||
|
|
@ -45,8 +38,6 @@ export interface Plugins {
|
||||||
[key: string]: Plugin
|
[key: string]: Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MAIN INTERFACES */
|
|
||||||
|
|
||||||
export interface App {
|
export interface App {
|
||||||
meta: AppMeta
|
meta: AppMeta
|
||||||
open: AppOpenFunction
|
open: AppOpenFunction
|
||||||
|
|
@ -57,8 +48,6 @@ export interface Plugin {
|
||||||
run: PluginRunFunction
|
run: PluginRunFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MISC */
|
|
||||||
|
|
||||||
export interface FlowWindowConfig {
|
export interface FlowWindowConfig {
|
||||||
title: string
|
title: string
|
||||||
icon: string
|
icon: string
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue