commit
60d1c68bc9
12 changed files with 129 additions and 61 deletions
|
|
@ -30,8 +30,7 @@ export default class EditorApp implements App {
|
||||||
title: this.name,
|
title: this.name,
|
||||||
icon,
|
icon,
|
||||||
width: 500,
|
width: 500,
|
||||||
height: 400,
|
height: 400
|
||||||
canResize: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import icon from '../assets/icons/files.png'
|
import icon from '../assets/icons/files.png'
|
||||||
import { App } from '../types.ts'
|
import { App } from '../types.ts'
|
||||||
|
|
||||||
import flow from '../flow.ts'
|
|
||||||
import { FlowWindow } from '../wm.ts'
|
import { FlowWindow } from '../wm.ts'
|
||||||
|
|
||||||
import { Stats } from 'fs'
|
import { Stats } from 'fs'
|
||||||
|
|
@ -17,8 +16,7 @@ export default class FilesApp implements App {
|
||||||
title: this.name,
|
title: this.name,
|
||||||
icon,
|
icon,
|
||||||
width: 500,
|
width: 500,
|
||||||
height: 400,
|
height: 400
|
||||||
canResize: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
win.content.style.display = 'flex'
|
win.content.style.display = 'flex'
|
||||||
|
|
@ -121,7 +119,7 @@ export default class FilesApp implements App {
|
||||||
if (fileStat.isDirectory()) {
|
if (fileStat.isDirectory()) {
|
||||||
await setDir(dir + separator + file)
|
await setDir(dir + separator + file)
|
||||||
} else {
|
} else {
|
||||||
flow.openApp('flow.editor', { path: dir + separator + file })
|
await window.flow.openApp('flow.editor', { path: dir + separator + file })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ export default class SettingsApp implements App {
|
||||||
win.content.style.flexDirection = 'column'
|
win.content.style.flexDirection = 'column'
|
||||||
win.content.style.justifyContent = 'center'
|
win.content.style.justifyContent = 'center'
|
||||||
win.content.style.alignItems = 'center'
|
win.content.style.alignItems = 'center'
|
||||||
|
win.content.style.background = 'var(--base)'
|
||||||
win.content.innerHTML = `
|
win.content.innerHTML = `
|
||||||
<div>
|
<div>
|
||||||
<h1 style="margin:0;">FlowOS</h1>
|
<h1 style="margin:0;">FlowOS</h1>
|
||||||
|
|
|
||||||
40
src/apps/manager.ts
Normal file
40
src/apps/manager.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import icon from '../assets/icons/manager.png'
|
||||||
|
import { App } from '../types.ts'
|
||||||
|
import { FlowWindow } from '../wm.ts'
|
||||||
|
|
||||||
|
export default class ManagerApp implements App {
|
||||||
|
name = 'Manager'
|
||||||
|
pkg = 'flow.manager'
|
||||||
|
icon = icon
|
||||||
|
version = '1.0.0'
|
||||||
|
|
||||||
|
async open (): Promise<FlowWindow> {
|
||||||
|
const win = window.wm.createWindow({
|
||||||
|
title: this.name,
|
||||||
|
icon,
|
||||||
|
width: 350,
|
||||||
|
height: 500
|
||||||
|
})
|
||||||
|
|
||||||
|
win.content.style.display = 'flex'
|
||||||
|
win.content.style.flexDirection = 'column'
|
||||||
|
win.content.style.gap = '10px'
|
||||||
|
win.content.style.padding = '10px'
|
||||||
|
win.content.style.background = 'var(--base)'
|
||||||
|
win.content.innerHTML = `
|
||||||
|
${window.flow.apps.map(app => {
|
||||||
|
return `
|
||||||
|
<div style="display:flex;gap: 10px;padding: 10px;background: var(--surface-0);border-radius: 10px;">
|
||||||
|
<img src="${app.icon}" style="border-radius: 40%;aspect-ratio: 1 / 1;height: 50px;"/>
|
||||||
|
<div>
|
||||||
|
<h3 style="margin:0;">${app.name} ${(app.builtin ?? false) ? '<code style="font-size: 0.75em;">(builtin)</code>' : ''}</h3>
|
||||||
|
<p style="margin:0;">${app.pkg} (v${app.version})</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}).join('')}
|
||||||
|
`
|
||||||
|
|
||||||
|
return win
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,10 +13,10 @@ export default class MusicApp implements App {
|
||||||
title: this.name,
|
title: this.name,
|
||||||
icon,
|
icon,
|
||||||
width: 700,
|
width: 700,
|
||||||
height: 300,
|
height: 300
|
||||||
canResize: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
win.content.style.background = 'var(--base)'
|
||||||
win.content.innerHTML = 'hi'
|
win.content.innerHTML = 'hi'
|
||||||
|
|
||||||
return win
|
return win
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ export default class SettingsApp implements App {
|
||||||
title: this.name,
|
title: this.name,
|
||||||
icon,
|
icon,
|
||||||
width: 700,
|
width: 700,
|
||||||
height: 300,
|
height: 300
|
||||||
canResize: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
win.content.style.background = 'var(--base)'
|
||||||
win.content.style.padding = '10px'
|
win.content.style.padding = '10px'
|
||||||
win.content.innerHTML = `
|
win.content.innerHTML = `
|
||||||
<h1>Settings</h1>
|
<h1>Settings</h1>
|
||||||
|
|
|
||||||
BIN
src/assets/icons/manager.png
Normal file
BIN
src/assets/icons/manager.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
79
src/flow.ts
79
src/flow.ts
|
|
@ -1,24 +1,67 @@
|
||||||
import { Flow } from './types.ts'
|
import { App, LoadedApp } from './types.ts'
|
||||||
|
|
||||||
import SettingsApp from './apps/settings.ts'
|
class Flow {
|
||||||
import FilesApp from './apps/files.ts'
|
apps: LoadedApp[] = []
|
||||||
import MusicApp from './apps/music.ts'
|
appList = [
|
||||||
import EditorApp from './apps/editor.ts'
|
'settings',
|
||||||
import InfoApp from './apps/info.ts'
|
'music',
|
||||||
|
'files',
|
||||||
|
'editor',
|
||||||
|
'info',
|
||||||
|
'manager'
|
||||||
|
]
|
||||||
|
|
||||||
const flow: Flow = {
|
async init (): Promise<void> {
|
||||||
apps: {
|
window.preloader.setPending('apps')
|
||||||
'flow.settings': new SettingsApp(),
|
window.preloader.setStatus('importing default apps...')
|
||||||
'flow.music': new MusicApp(),
|
|
||||||
'flow.files': new FilesApp(),
|
for (const appPath of this.appList) {
|
||||||
'flow.editor': new EditorApp(),
|
const { default: ImportedApp } = await import(`./apps/${appPath}.ts`)
|
||||||
'flow.info': new InfoApp()
|
const app = new ImportedApp()
|
||||||
},
|
app.builtin = true
|
||||||
async openApp (pkg: string, data: any) {
|
|
||||||
const win = this.apps[pkg].open(data)
|
window.preloader.setStatus(`importing default apps\n${appPath}`)
|
||||||
const event = new CustomEvent('app_opened', { detail: { app: this.apps[pkg], win: await win } })
|
this.add(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.wm.launcher.style.opacity = '0'
|
||||||
|
window.wm.launcher.style.filter = 'blur(0px)'
|
||||||
|
window.wm.launcher.style.pointerEvents = 'none'
|
||||||
|
|
||||||
|
window.preloader.setStatus('adding apps to app launcher...')
|
||||||
|
|
||||||
|
this.apps.forEach((app) => {
|
||||||
|
window.preloader.setStatus(`adding apps to app launcher\n${app.name}`)
|
||||||
|
const appElement = document.createElement('app')
|
||||||
|
appElement.onclick = async () => {
|
||||||
|
await window.flow.openApp(app.pkg)
|
||||||
|
window.wm.toggleLauncher()
|
||||||
|
}
|
||||||
|
appElement.innerHTML = `<img src="${app.icon}"><div>${app.name}</div>`
|
||||||
|
window.wm.launcher.querySelector('apps')?.appendChild(appElement)
|
||||||
|
})
|
||||||
|
|
||||||
|
document.body.appendChild(window.wm.windowArea)
|
||||||
|
document.body.appendChild(window.wm.launcher)
|
||||||
|
|
||||||
|
await window.preloader.setDone('apps')
|
||||||
|
}
|
||||||
|
|
||||||
|
add (app: App): void {
|
||||||
|
if (this.apps.some(x => x.pkg === app.pkg)) {
|
||||||
|
console.error(`Unable to register app; ${app.pkg} is already registered.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.apps.push(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
async openApp (pkg: string, data?: any): Promise<void> {
|
||||||
|
const app = this.apps.find(x => x.pkg === pkg)
|
||||||
|
const win = app?.open(data)
|
||||||
|
const event = new CustomEvent('app_opened', { detail: { app, win: await win } })
|
||||||
window.dispatchEvent(event)
|
window.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default flow
|
export default Flow
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,17 @@ import './style.less'
|
||||||
import Preloader from './preloader'
|
import Preloader from './preloader'
|
||||||
import StatusBar from './statusbar'
|
import StatusBar from './statusbar'
|
||||||
import WM from './wm'
|
import WM from './wm'
|
||||||
|
import Flow from './flow'
|
||||||
|
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
preloader: Preloader
|
preloader: Preloader
|
||||||
|
flow: Flow
|
||||||
|
fs: typeof fs
|
||||||
statusBar: StatusBar
|
statusBar: StatusBar
|
||||||
wm: WM
|
wm: WM
|
||||||
fs: typeof fs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,6 +30,7 @@ if (params.get('debug') !== null && params.get('debug') !== undefined) {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.preloader = new Preloader()
|
window.preloader = new Preloader()
|
||||||
|
window.flow = new Flow()
|
||||||
window.statusBar = new StatusBar()
|
window.statusBar = new StatusBar()
|
||||||
window.wm = new WM();
|
window.wm = new WM();
|
||||||
|
|
||||||
|
|
@ -39,6 +42,8 @@ window.wm = new WM();
|
||||||
await window.statusBar.init()
|
await window.statusBar.init()
|
||||||
await window.wm.init()
|
await window.wm.init()
|
||||||
|
|
||||||
|
await window.flow.init()
|
||||||
|
|
||||||
window.preloader.setStatus('')
|
window.preloader.setStatus('')
|
||||||
window.preloader.finish()
|
window.preloader.finish()
|
||||||
})().catch(e => console.error)
|
})().catch(e => console.error)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export const meta = {
|
export const meta = {
|
||||||
name: 'App View',
|
name: 'App Launcher',
|
||||||
description: 'Opens the app view.',
|
description: 'Opens the app launcher.',
|
||||||
id: 'appview'
|
id: 'applauncher'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const run = (element: HTMLDivElement): void => {
|
export const run = (element: HTMLDivElement): void => {
|
||||||
|
|
|
||||||
|
|
@ -44,15 +44,16 @@ export interface FlowWindowConfig {
|
||||||
width?: number
|
width?: number
|
||||||
height?: number
|
height?: number
|
||||||
|
|
||||||
canResize: boolean
|
canResize?: boolean
|
||||||
|
|
||||||
minWidth?: number
|
minWidth?: number
|
||||||
minHeight?: number
|
minHeight?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Flow {
|
export interface Apps {
|
||||||
apps: {
|
|
||||||
[key: string]: App
|
[key: string]: App
|
||||||
}
|
}
|
||||||
openApp: Function
|
|
||||||
|
export interface LoadedApp extends App {
|
||||||
|
builtin: boolean
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
src/wm.ts
27
src/wm.ts
|
|
@ -1,4 +1,3 @@
|
||||||
import flow from './flow.ts'
|
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import { FlowWindowConfig } from './types.ts'
|
import { FlowWindowConfig } from './types.ts'
|
||||||
|
|
||||||
|
|
@ -89,12 +88,14 @@ export class FlowWindow {
|
||||||
this.focus()
|
this.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.canResize === undefined) config.canResize = true
|
||||||
|
|
||||||
this.element.style.width = `${config.width ?? 300}px`
|
this.element.style.width = `${config.width ?? 300}px`
|
||||||
this.element.style.height = `${config.height ?? 200}px`
|
this.element.style.height = `${config.height ?? 200}px`
|
||||||
|
|
||||||
this.header = document.createElement('window-header')
|
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='bx bx-minus'></i><i id="close" class='bx bx-x'></i>`
|
||||||
if (config.canResize) {
|
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='bx bx-minus'></i><i id="max" class='bx bx-checkbox'></i><i id="close" class='bx bx-x'></i>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +105,7 @@ export class FlowWindow {
|
||||||
|
|
||||||
(this.header.querySelector('#min') as HTMLElement).onclick = () => this.toggleMin()
|
(this.header.querySelector('#min') as HTMLElement).onclick = () => this.toggleMin()
|
||||||
|
|
||||||
if (config.canResize) {
|
if (!config.canResize) {
|
||||||
(this.header.querySelector('#max') as HTMLElement).onclick = () => this.toggleMax()
|
(this.header.querySelector('#max') as HTMLElement).onclick = () => this.toggleMax()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,26 +235,6 @@ class WM {
|
||||||
this.toggleLauncher()
|
this.toggleLauncher()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.launcher.style.opacity = '0'
|
|
||||||
this.launcher.style.filter = 'blur(0px)'
|
|
||||||
this.launcher.style.pointerEvents = 'none'
|
|
||||||
|
|
||||||
window.preloader.setStatus('adding apps to app launcher...')
|
|
||||||
|
|
||||||
for (const pkg in flow.apps) {
|
|
||||||
window.preloader.setStatus(`adding apps to app launcher\n${flow.apps[pkg].name}`)
|
|
||||||
const app = document.createElement('app')
|
|
||||||
app.onclick = () => {
|
|
||||||
flow.openApp(pkg)
|
|
||||||
this.toggleLauncher()
|
|
||||||
}
|
|
||||||
app.innerHTML = `<img src="${flow.apps[pkg].icon}"><div>${flow.apps[pkg].name}</div>`
|
|
||||||
this.launcher.querySelector('apps')?.appendChild(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.appendChild(this.windowArea)
|
|
||||||
document.body.appendChild(this.launcher)
|
|
||||||
|
|
||||||
await window.preloader.setDone('window manager')
|
await window.preloader.setDone('window manager')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue