diff --git a/src/apps/editor.ts b/src/apps/editor.ts index 54c2ec1..334198a 100644 --- a/src/apps/editor.ts +++ b/src/apps/editor.ts @@ -19,15 +19,18 @@ interface EditorConfig { } export default class EditorApp implements App { - name = 'Editor' - pkg = 'flow.editor' - icon = icon - version = '1.0.0' + meta = { + name: 'Editor', + description: 'A simple editor app.', + pkg: 'flow.editor', + version: '1.0.0', + icon + } async open (data?: EditorConfig): Promise { const win = window.wm.createWindow({ - title: this.name, - icon, + title: this.meta.name, + icon: this.meta.icon, width: 500, height: 400 }) @@ -157,7 +160,6 @@ export default class EditorApp implements App { break } - case 'html': { language = 'html' break diff --git a/src/apps/files.ts b/src/apps/files.ts index 6ddaa50..9adac1d 100644 --- a/src/apps/files.ts +++ b/src/apps/files.ts @@ -6,15 +6,18 @@ import { FlowWindow } from '../wm.ts' import { Stats } from 'fs' export default class FilesApp implements App { - name = 'Files' - pkg = 'flow.files' - icon = icon - version = '1.0.0' + meta = { + name: 'Files', + description: 'A simple files app.', + pkg: 'flow.files', + version: '1.0.0', + icon + } async open (): Promise { const win = window.wm.createWindow({ - title: this.name, - icon, + title: this.meta.name, + icon: this.meta.icon, width: 500, height: 400 }) diff --git a/src/apps/info.ts b/src/apps/info.ts index 00b7178..0b7c58a 100644 --- a/src/apps/info.ts +++ b/src/apps/info.ts @@ -3,17 +3,19 @@ import { App, PackageJSON } from '../types.ts' import { FlowWindow } from '../wm.ts' export default class SettingsApp implements App { - name = 'Info' - pkg = 'flow.info' - icon = icon - version = '1.0.0' - canResize = true + meta = { + name: 'Info', + description: 'FlowOS Information.', + pkg: 'flow.info', + version: '1.0.0', + icon + } async open (): Promise { const packageJSON: PackageJSON = await import('../../package.json') const win = window.wm.createWindow({ - title: this.name, - icon, + title: this.meta.name, + icon: this.meta.icon, width: 300, height: 400, canResize: false diff --git a/src/apps/manager.ts b/src/apps/manager.ts index cfc064f..52922f4 100644 --- a/src/apps/manager.ts +++ b/src/apps/manager.ts @@ -1,17 +1,21 @@ import icon from '../assets/icons/manager.png' -import { App } from '../types.ts' +import { App, LoadedApp, LoadedPlugin } from '../types.ts' import { FlowWindow } from '../wm.ts' +import nullIcon from '../assets/icons/null.png' export default class ManagerApp implements App { - name = 'Manager' - pkg = 'flow.manager' - icon = icon - version = '1.0.0' + meta = { + name: 'Flow Manager', + description: 'A FlowOS utility app.', + pkg: 'flow.manager', + version: '1.0.0', + icon + } async open (): Promise { const win = window.wm.createWindow({ - title: this.name, - icon, + title: this.meta.name, + icon: this.meta.icon, width: 350, height: 500 }) @@ -22,13 +26,24 @@ export default class ManagerApp implements App { win.content.style.padding = '10px' win.content.style.background = 'var(--base)' win.content.innerHTML = ` - ${window.flow.apps.map(app => { + ${window.flow.apps.map((app: LoadedApp) => { return `
- +
-

${app.name} ${(app.builtin ?? false) ? '(builtin)' : ''}

-

${app.pkg} (v${app.version})

+

${app.meta.name} ${(app.builtin ?? false) ? '(builtin)' : ''}

+

${app.meta.pkg} (v${app.meta.version}) - App

+
+
+ ` + }).join('')} + ${window.flow.plugins.map((plugin: LoadedPlugin) => { + return ` +
+ +
+

${plugin.meta.name} ${(plugin.builtin ?? false) ? '(builtin)' : ''}

+

${plugin.meta.pkg} (v${plugin.meta.version}) - Plugin

` diff --git a/src/apps/music.ts b/src/apps/music.ts index c7fe8f2..e1bdc7a 100644 --- a/src/apps/music.ts +++ b/src/apps/music.ts @@ -3,15 +3,18 @@ import { App } from '../types.ts' import { FlowWindow } from '../wm.ts' export default class MusicApp implements App { - name = 'Music' - pkg = 'flow.music' - icon = icon - version = '1.0.0' + meta = { + name: 'Music', + description: 'A simple music app.', + pkg: 'flow.music', + version: '1.0.0', + icon + } async open (): Promise { const win = window.wm.createWindow({ - title: this.name, - icon, + title: this.meta.name, + icon: this.meta.icon, width: 700, height: 300 }) diff --git a/src/apps/settings.ts b/src/apps/settings.ts index 42285c2..03263c3 100644 --- a/src/apps/settings.ts +++ b/src/apps/settings.ts @@ -3,15 +3,18 @@ import { App } from '../types.ts' import { FlowWindow } from '../wm.ts' export default class SettingsApp implements App { - name = 'Settings' - pkg = 'flow.settings' - icon = icon - version = '1.0.0' + meta = { + name: 'Settings', + description: 'Modify/customize FlowOS.', + pkg: 'flow.settings', + version: '1.0.0', + icon + } async open (): Promise { const win = window.wm.createWindow({ - title: this.name, - icon, + title: this.meta.name, + icon: this.meta.icon, width: 700, height: 300 }) diff --git a/src/assets/icons/null.png b/src/assets/icons/null.png new file mode 100644 index 0000000..4cb5fd4 Binary files /dev/null and b/src/assets/icons/null.png differ diff --git a/src/files.d.ts b/src/files.d.ts index 5d25086..2101ae6 100644 --- a/src/files.d.ts +++ b/src/files.d.ts @@ -1,2 +1,5 @@ -declare module '*.png' +declare module '*.png' { + const image: string + export = image +} declare module '*.json' diff --git a/src/flow.ts b/src/flow.ts index 151deef..420b3ba 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -1,4 +1,4 @@ -import { App, LoadedApp } from './types.ts' +import { LoadedApp, LoadedPlugin } from './types.ts' class Flow { apps: LoadedApp[] = [] @@ -11,17 +11,30 @@ class Flow { 'manager' ] - async init (): Promise { + plugins: LoadedPlugin[] = [] + pluginList: string[] = [ + 'appLauncher', + 'apps', + 'weather', + 'clock', + 'switcher', + 'battery' + ] + + private async initApps (): Promise { window.preloader.setPending('apps') window.preloader.setStatus('importing default apps...') for (const appPath of this.appList) { - const { default: ImportedApp } = await import(`./apps/${appPath}.ts`) + window.preloader.setStatus(`importing default apps\n${appPath}`) + const { default: ImportedApp } = await import(`./apps/${appPath}.ts`).catch((e: Error) => { + console.error(e) + window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`) + }) const app = new ImportedApp() app.builtin = true - window.preloader.setStatus(`importing default apps\n${appPath}`) - this.add(app) + this.addApp(app) } window.wm.launcher.style.opacity = '0' @@ -31,13 +44,13 @@ class Flow { window.preloader.setStatus('adding apps to app launcher...') this.apps.forEach((app) => { - window.preloader.setStatus(`adding apps to app launcher\n${app.name}`) + 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.pkg) + await window.flow.openApp(app.meta.pkg) window.wm.toggleLauncher() } - appElement.innerHTML = `
${app.name}
` + appElement.innerHTML = `
${app.meta.name}
` window.wm.launcher.querySelector('apps')?.appendChild(appElement) }) @@ -47,17 +60,48 @@ class Flow { 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.`) + private async initPlugins (): Promise { + window.preloader.setPending('plugins') + window.preloader.setStatus('importing default plugins...') + + for (const pluginPath of this.pluginList) { + window.preloader.setStatus(`importing default plugins\n${pluginPath}`) + const plugin = await import(`./plugins/${pluginPath}.ts`).catch((e: Error) => { + console.error(e) + window.preloader.setStatus(`unable to import ${pluginPath}\n${e.name}: ${e.message}`) + }) + const loadedPlugin = { + ...plugin, + builtin: true + } + this.addPlugin(loadedPlugin) + } + } + + async init (): Promise { + await this.initApps() + await this.initPlugins() + } + + addApp (app: LoadedApp): void { + if (this.apps.some(x => x.meta.pkg === app.meta.pkg)) { + console.error(`Unable to register app; ${app.meta.pkg} is already registered.`) return } this.apps.push(app) } + addPlugin (plugin: LoadedPlugin): void { + if (window.flow.plugins.some(x => x.meta.pkg === plugin.meta.pkg)) { + console.error(`Unable to register tool; ${plugin.meta.pkg} is already registered.`) + return + } + this.plugins.push(plugin) + } + async openApp (pkg: string, data?: any): Promise { - const app = this.apps.find(x => x.pkg === pkg) + const app = this.apps.find(x => x.meta.pkg === pkg) const win = app?.open(data) const event = new CustomEvent('app_opened', { detail: { app, win: await win } }) window.dispatchEvent(event) diff --git a/src/index.ts b/src/index.ts index 13febd1..8248f92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,10 +39,9 @@ window.wm = new WM(); window.fs = new (window as any).Filer.FileSystem() await window.preloader.setDone('filesystem') - await window.statusBar.init() await window.wm.init() - await window.flow.init() + await window.statusBar.init() window.preloader.setStatus('') window.preloader.finish() diff --git a/src/modules/appLauncher.ts b/src/plugins/appLauncher.ts similarity index 90% rename from src/modules/appLauncher.ts rename to src/plugins/appLauncher.ts index 3d13dac..7b840a4 100644 --- a/src/modules/appLauncher.ts +++ b/src/plugins/appLauncher.ts @@ -1,7 +1,8 @@ export const meta = { name: 'App Launcher', description: 'Opens the app launcher.', - id: 'applauncher' + pkg: 'flow.applauncher', + version: '1.0.0' } export const run = (element: HTMLDivElement): void => { diff --git a/src/modules/apps.ts b/src/plugins/apps.ts similarity index 88% rename from src/modules/apps.ts rename to src/plugins/apps.ts index 5621bea..f17b3ef 100644 --- a/src/modules/apps.ts +++ b/src/plugins/apps.ts @@ -3,7 +3,8 @@ import { AppOpenedEvent, AppClosedEvent } from '../types' export const meta = { name: 'Apps', description: 'Displays the current apps open.', - id: 'apps' + pkg: 'flow.apps', + version: '1.0.0' } export const run = (element: HTMLDivElement): void => { @@ -19,7 +20,7 @@ export const run = (element: HTMLDivElement): void => { appIcon.style.background = 'var(--surface-0)' appIcon.style.padding = '5px 7.5px' appIcon.style.borderRadius = '5px' - appIcon.innerHTML = ` ${app.name}` + appIcon.innerHTML = ` ${app.meta.name}` appIcon.onclick = async () => { const win = await e.detail.win win.focus() diff --git a/src/modules/battery.ts b/src/plugins/battery.ts similarity index 94% rename from src/modules/battery.ts rename to src/plugins/battery.ts index 7818ec0..d72b443 100644 --- a/src/modules/battery.ts +++ b/src/plugins/battery.ts @@ -1,7 +1,8 @@ export const meta = { name: 'Battery', description: 'Tells you your device\'s battery.', - id: 'battery' + pkg: 'flow.battery', + version: '1.0.0' } export const run = (element: HTMLDivElement): void => { diff --git a/src/modules/clock.ts b/src/plugins/clock.ts similarity index 95% rename from src/modules/clock.ts rename to src/plugins/clock.ts index be3fb90..bab94ec 100644 --- a/src/modules/clock.ts +++ b/src/plugins/clock.ts @@ -1,7 +1,8 @@ export const meta = { name: 'Clock', description: 'Displays the date & time.', - id: 'clock' + pkg: 'flow.clock', + version: '1.0.0' } export const run = (element: HTMLDivElement): void => { diff --git a/src/modules/switcher.ts b/src/plugins/switcher.ts similarity index 91% rename from src/modules/switcher.ts rename to src/plugins/switcher.ts index df4eb14..6d8e9c1 100644 --- a/src/modules/switcher.ts +++ b/src/plugins/switcher.ts @@ -1,7 +1,8 @@ export const meta = { name: 'Desktop Switcher', description: 'Allows you to switch between desktops.', - id: 'switcher' + pkg: 'flow.switcher', + version: '1.0.0' } export const run = (element: HTMLDivElement): void => { diff --git a/src/modules/weather.ts b/src/plugins/weather.ts similarity index 88% rename from src/modules/weather.ts rename to src/plugins/weather.ts index 9ef4707..d3b6e2c 100644 --- a/src/modules/weather.ts +++ b/src/plugins/weather.ts @@ -1,7 +1,8 @@ export const meta = { name: 'Weather', description: 'Tells you the weather.', - id: 'weather' + pkg: 'flow.weather', + version: '1.0.0' } export const run = (element: HTMLDivElement): void => { diff --git a/src/statusbar.ts b/src/statusbar.ts index 74a2146..521583a 100644 --- a/src/statusbar.ts +++ b/src/statusbar.ts @@ -1,17 +1,7 @@ -import { StatusItem } from './types' +import { Plugin } from './types' class StatusBar { - pluginList: string[] = [ - 'appLauncher', - 'apps', - 'weather', - 'clock', - 'switcher', - 'battery' - ] - - plugins: StatusItem[] = [] element: HTMLElement constructor () { @@ -20,29 +10,20 @@ class StatusBar { document.body.appendChild(this.element) } - add (item: StatusItem): void { - if (this.plugins.some(x => x.meta.id === item.meta.id)) { - console.error(`Unable to register tool; ${item.meta.id} is already registered.`) - return - } - + add (item: Plugin): void { const element = document.createElement('div') - element.setAttribute('data-toolbar-id', item.meta.id) + element.setAttribute('data-toolbar-id', item.meta.pkg) - this.plugins.push(item) this.element.appendChild(element) item.run(element) } async init (): Promise { - window.preloader.setPending('plugins') - window.preloader.setStatus('importing default plugins...') + window.preloader.setStatus('adding plugins to statusbar...') - for (const pluginPath of this.pluginList) { - const plugin = await import(`./modules/${pluginPath}.ts`) - - window.preloader.setStatus(`importing default plugins\n${pluginPath}`) + for (const plugin of window.flow.plugins) { + window.preloader.setStatus(`adding plugins to statusbar\n${plugin.meta.pkg}`) this.add(plugin) } diff --git a/src/style.less b/src/style.less index ae5aa87..36be317 100644 --- a/src/style.less +++ b/src/style.less @@ -143,24 +143,36 @@ launcher { max-height: 70vh; padding: 20px; margin: 40px; - display: flex; justify-content: center; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(125px, 1fr)); + grid-template-rows: repeat(auto-fit, minmax(200px, 1fr)); gap: 40px; - flex-wrap: wrap; app { + align-items: center; justify-content: center; display: flex; flex-direction: column; gap: 10px; height: max-content; text-align: center; + overflow: hidden; img { - width: 75px; + width: 100%; border-radius: 40%; aspect-ratio: 1 / 1; } + + div { + overflow: hidden; + text-overflow: ellipsis; + width: fit-content; + height: 100%; + white-space: nowrap; + + } } } diff --git a/src/types.ts b/src/types.ts index be3293b..d44775e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,29 +1,9 @@ import { FlowWindow } from './wm' -export interface StatusItem { - meta: { - name: string - description: string - id: string - } - run: Function -} - export interface PackageJSON { version: string } -export interface App { - name: string - pkg: string - - version: string - - icon: string - - open: (data: any) => Promise -} - export interface AppOpenedEvent extends CustomEvent { detail: { app: App @@ -50,10 +30,42 @@ export interface FlowWindowConfig { minHeight?: number } +export interface App { + meta: { + name: string + description: string + pkg: string + version: string + icon: string + } + + open: (data: any) => Promise +} + +export interface Plugin { + meta: { + name: string + description: string + pkg: string + version: string + icon?: string + } + + run: (element: HTMLDivElement) => void | Promise +} + export interface Apps { [key: string]: App } +export interface Plugins { + [key: string]: Plugin +} + export interface LoadedApp extends App { builtin: boolean } + +export interface LoadedPlugin extends Plugin { + builtin: boolean +}