diff --git a/src/assets/style.less b/src/assets/style.less index 3418b8c..322e516 100644 --- a/src/assets/style.less +++ b/src/assets/style.less @@ -115,7 +115,8 @@ window-area { border-radius: 5px; box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2), 0 0px 10px rgba(0, 0, 0, 0.2); overflow: hidden; - border: 1px solid rgba(255, 255, 255, 0.2); + border: 1px solid; + border-color: color-mix(in srgb, var(--text) 20%, transparent); background: var(--crust); transition: 0.2s opacity, 0.2s transform; @@ -154,7 +155,7 @@ launcher { justify-content: center; align-items: center; top: 0; - background: rgba(0, 0, 0, 0.5); + background: color-mix(in srgb, var(--crust) 20%, transparent); z-index: 99999999999999999999999; width: calc(100vw + 20px); height: calc(100vh + 20px); diff --git a/src/kernel.ts b/src/kernel.ts index 96044d4..0d25438 100644 --- a/src/kernel.ts +++ b/src/kernel.ts @@ -57,6 +57,15 @@ export default class Kernel { this.version = pkg.version } + private async setTheme (themeName: string): Promise { + if (this.fs === false) throw new Error('Filesystem hasn\'t been initiated.') + const file = await this.fs.readFile(`/etc/themes/${themeName}.theme`) + const { colors } = JSON.parse(Buffer.from(file).toString()) + for (const color in colors) { + document.documentElement.style.setProperty(`--${color}`, colors[color]) + } + } + async boot (boot: HTML, progress: HTML, args: URLSearchParams): Promise { progress.style({ width: '0%' }) const bootArgs = args.toString().replace(/=($|&)/g, '=true ') @@ -79,6 +88,11 @@ export default class Kernel { }) if (this.config === false) return else progress.style({ width: '40%' }) + await this.setTheme(this.config.THEME) + document.addEventListener('theme_update', () => { + if (this.config === false) return + this.setTheme(this.config.THEME).catch(e => console.error(e)) + }) const tmp = await handle('mount', 'Temporary Directory (/tmp)', { init: async () => { if (this.fs === false) return false diff --git a/src/system/BootLoader.ts b/src/system/BootLoader.ts deleted file mode 100644 index 14a4ef4..0000000 --- a/src/system/BootLoader.ts +++ /dev/null @@ -1,171 +0,0 @@ -import HTML from '../HTML' -import { AppClosedEvent, AppOpenedEvent, Process } from '../types' -import { getTime } from '../utils' -import nullIcon from '../assets/icons/application-default-icon.svg' -import { parse } from 'js-ini' -import { v4 as uuid } from 'uuid' - -const BootLoader: Process = { - config: { - name: 'Bootloader', - type: 'process', - targetVer: '1.0.0-indev.0' - }, - run: async (process) => { - const splashScreen = await process.loadLibrary('lib/SplashScreen') - const splashElement = splashScreen.getElement() - splashElement.appendTo(document.body) - - const { fs } = process - const wm = await process.loadLibrary('lib/WindowManager') - const launcher = await process.loadLibrary('lib/Launcher') - - const config = Buffer.from(await fs.readFile('/etc/flow')).toString() - process.kernel.setConfig(parse(config)) - - if ('serviceWorker' in navigator) { - const registrations = await navigator.serviceWorker.getRegistrations() - for (const registration of registrations) { - await registration.unregister() - } - - try { - await navigator.serviceWorker.register(`/uv-sw.js?url=${encodeURIComponent(btoa(process.kernel.config.SERVER))}&e=${uuid()}`, { - scope: '/service/' - }) - } catch (e) { - console.error(e) - } - } else { - console.warn('Service workers are not supported.') - } - - const input = new HTML('input').attr({ - type: 'text', - placeholder: 'Search' - }).on('keyup', () => { - apps.elm.innerHTML = '' - renderApps().catch(e => console.error(e)) - }).appendTo(launcher.element) - const apps = new HTML('apps').appendTo(launcher.element) - - const renderApps = async (): Promise => { - apps.html('') - const files = await fs.readdir('/home/Applications/') - files - .filter((x: string) => x.endsWith('.app') && ((input.elm as HTMLInputElement) !== null ? x.toLowerCase().includes((input.elm as HTMLInputElement).value.toLowerCase()) : true)) - .forEach((file: string) => { - fs.readFile(`/home/Applications/${file}`).then(async (data: Uint8Array) => { - const path = Buffer.from(data).toString() - const executable = await process.kernel.getExecutable(path) as Process - - const appElement = new HTML('app').on('click', () => { - process.launch(path).catch((e: any) => console.error(e)) - launcher.toggle() - }).appendTo(apps) - new HTML('img').attr({ - src: executable.config.icon ?? nullIcon, - alt: `${executable.config.name} icon` - }).appendTo(appElement) - new HTML('div').text(executable.config.name).appendTo(appElement) - }).catch((e: any) => console.error(e)) - }) - } - - await renderApps() - document.addEventListener('fs_update', () => { - renderApps().catch(e => console.error(e)) - }) - - launcher.element.on('click', (e: Event) => { - if (e.target !== e.currentTarget) return - launcher.toggle() - }) - - const statusBar = await process.loadLibrary('lib/StatusBar') - - statusBar.element.html(` -
space_dashboard
- -
- -
expand_less
-
- battery_2_bar - signal_cellular_4_bar -
-
- - `) - - setInterval((): any => { - getTime().then((time) => { - statusBar.element.qs('div[data-toolbar-id="calendar"]')?.text(time) - }).catch(e => console.error) - }, 1000) - - statusBar.element.qs('div[data-toolbar-id="start"]')?.on('click', () => { - launcher.toggle() - }) - - if ('getBattery' in navigator) { - (navigator as any).getBattery().then((battery: any) => { - statusBar.updateBatteryIcon(battery) - - battery.addEventListener('levelchange', () => { - statusBar.updateBatteryIcon(battery) - }) - - battery.addEventListener('chargingchange', () => { - statusBar.updateBatteryIcon(battery) - }) - }) - } else { - const batteryDiv = document.querySelector('div[data-toolbar-id="controls"] > .battery') - if (batteryDiv != null) { - batteryDiv.innerHTML = 'battery_unknown' - } - } - - async function ping (startTime: number): Promise { - fetch(`${process.kernel.config.SERVER as string}/bare/`) - .then(() => { - const endTime = performance.now() - const pingTime = endTime - startTime - statusBar.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()), 10_000) - - document.addEventListener('app_opened', (e: AppOpenedEvent): void => { - new HTML('app').appendMany( - new HTML('img').attr({ - alt: `${e.detail.proc.config.name} icon`, - 'data-id': e.detail.token, - src: e.detail.proc.config.icon ?? nullIcon - }).on('click', () => { - e.detail.win.focus() - e.detail.win.toggleMin() - }) - ).appendTo(statusBar.element.qs('div[data-toolbar-id="apps"]')?.elm as HTMLElement) - }) - - document.addEventListener('app_closed', (e: AppClosedEvent): void => { - statusBar.element.qs('div[data-toolbar-id="apps"]')?.qs(`img[data-id="${e.detail.token}"]`)?.elm.parentElement?.remove() - }) - - document.body.style.flexDirection = 'column-reverse' - - await statusBar.element.appendTo(document.body) - await launcher.element.appendTo(document.body) - await wm.windowArea.appendTo(document.body) - - splashElement.cleanup() - } -} - -export default BootLoader diff --git a/src/system/UserAccessControl.ts b/src/system/UserAccessControl.ts index 3b918e6..b0f97ec 100644 --- a/src/system/UserAccessControl.ts +++ b/src/system/UserAccessControl.ts @@ -32,7 +32,7 @@ const UserAccessControl: Process = { } } - wm.createModal('User Account Control', message, process) + wm.createModal('allow', 'User Account Control', message, process) .then(async ({ value, win }: { value: boolean win: FlowWindow diff --git a/src/system/VirtualFS.ts b/src/system/VirtualFS.ts index a6dbeee..373f81d 100644 --- a/src/system/VirtualFS.ts +++ b/src/system/VirtualFS.ts @@ -1,4 +1,6 @@ import { Directory, Errors, File, Permission, Stats } from '../types' +import path from 'path' +const p = path export const defaultFS: { root: Directory } = { root: { @@ -75,6 +77,12 @@ export const defaultFS: { root: Directory } = { deleteable: true, permission: Permission.USER, content: Buffer.from('apps/Settings') + }, + 'ThemeMaker.app': { + type: 'file', + deleteable: true, + permission: Permission.USER, + content: Buffer.from('apps/ThemeMaker') } } }, @@ -95,47 +103,17 @@ export const defaultFS: { root: Directory } = { permission: Permission.USER, content: Buffer.from('/home/Applications/Info.app') }, - 'Manager.lnk': { - type: 'file', - deleteable: true, - permission: Permission.USER, - content: Buffer.from('/home/Applications/Manager.app') - }, - 'Store.lnk': { - type: 'file', - deleteable: true, - permission: Permission.USER, - content: Buffer.from('/home/Applications/Store.app') - }, - 'TaskManager.lnk': { - type: 'file', - deleteable: true, - permission: Permission.USER, - content: Buffer.from('/home/Applications/TaskManager.app') - }, 'Browser.lnk': { type: 'file', deleteable: true, permission: Permission.USER, content: Buffer.from('/home/Applications/Browser.app') }, - 'ImageViewer.lnk': { - type: 'file', - deleteable: true, - permission: Permission.USER, - content: Buffer.from('/home/Applications/ImageViewer.app') - }, 'Files.lnk': { type: 'file', deleteable: true, permission: Permission.USER, content: Buffer.from('/home/Applications/Files.app') - }, - 'Editor.lnk': { - type: 'file', - deleteable: true, - permission: Permission.USER, - content: Buffer.from('/home/Applications/Editor.app') } } }, @@ -176,13 +154,38 @@ export const defaultFS: { root: Directory } = { deleteable: false, permission: Permission.SYSTEM, children: { + themes: { + type: 'directory', + deleteable: false, + permission: Permission.SYSTEM, + children: { + 'Mocha.theme': { + type: 'file', + deleteable: true, + permission: Permission.USER, + content: Buffer.from(JSON.stringify({ + name: 'Catpuccin Mocha', + colors: { + text: '#cdd6f4', + 'surface-2': '#585b70', + 'surface-1': '#45475a', + 'surface-0': '#313244', + base: '#1e1e2e', + mantle: '#181825', + crust: '#11111b' + } + })) + } + } + }, flow: { type: 'file', deleteable: false, permission: Permission.ELEVATED, content: Buffer.from([ 'SERVER=https://server.flow-works.me', - '24HOUR=FALSE' + '24HOUR=FALSE', + 'THEME=Mocha' ].join('\n')) }, hostname: { @@ -211,42 +214,62 @@ export const defaultFS: { root: Directory } = { } class VirtualFS { - private fileSystem: { root: Directory } = defaultFS + private fileSystem: { root: Directory } private db: IDBDatabase | null = null + + private async addMissingFiles (): Promise { + const addDirectoryRecursive = async (directory: Directory, directoryPath: string): Promise => { + for (const [key, value] of Object.entries(directory.children)) { + const path = p.join(directoryPath, key) + if (value.type === 'directory') { + if (!await this.exists(path)) { + await this.mkdir(path) + } + await addDirectoryRecursive(value, path) + } else if (value.type === 'file' && !await this.exists(path)) { + await this.writeFile(path, Buffer.from(value.content).toString()) + } + } + } + await addDirectoryRecursive(defaultFS.root, '/') + } + async init (dbName = 'virtualfs'): Promise { return await new Promise((resolve, reject) => { - indexedDB.deleteDatabase(dbName) const request = indexedDB.open(dbName) + request.onupgradeneeded = (event) => { + const target = event.target as IDBRequest + const db = target.result + db.createObjectStore('fs') + } request.onerror = () => { reject(new Error('Failed to open database')) } - request.onsuccess = () => { - this.db = request.result + request.onsuccess = async (event) => { + const target = event.target as IDBRequest + this.db = target.result + await navigator.storage.persist() + this.fileSystem = await this.read() + if (this.fileSystem == null) await this.write(defaultFS) + else await this.addMissingFiles() resolve(this) } - request.onupgradeneeded = () => { - const db = request.result - db.createObjectStore('fs') - } }) } - private setFileSystem (fileSystemObject: { root: Directory }): void { - this.fileSystem = fileSystemObject - } - - private readonly read = async (): Promise => { - const transaction = this.db?.transaction(['fs'], 'readonly') - const store = transaction?.objectStore('fs') - const getRequest = store?.get('fs') + private readonly read = async (): Promise<{ root: Directory }> => { + if (this.db == null) throw new Error('Database is null') + const transaction = this.db.transaction(['fs'], 'readonly') + const store = transaction.objectStore('fs') + const getRequest = store.get('fs') return await new Promise((resolve, reject) => { - if (getRequest == null) return - getRequest.onsuccess = () => { - resolve(getRequest.result) + getRequest.onsuccess = (event) => { + const target = event.target as IDBRequest + resolve(target.result) } - getRequest.onerror = () => { + getRequest.onerror = (event) => { reject(getRequest.error) } }) @@ -258,12 +281,12 @@ class VirtualFS { } private readonly save = async (): Promise => { - const transaction = this.db?.transaction(['fs'], 'readwrite') - const store = transaction?.objectStore('fs') - const putRequest = store?.put(this.fileSystem, 'fs') + if (this.db == null) throw new Error('Database is null') + const transaction = this.db.transaction(['fs'], 'readwrite') + const store = transaction.objectStore('fs') + const putRequest = store.put(this.fileSystem, 'fs') return await new Promise((resolve, reject) => { - if (putRequest == null) return putRequest.onsuccess = () => { document.dispatchEvent(new CustomEvent('fs_update', {})) resolve() @@ -371,8 +394,8 @@ class VirtualFS { rmdir = async (path: string): Promise => { const { current, filename } = await this.navigatePathParent(path) - if (!current.deleteable) throw new Error(Errors.EPERM) - await this.handlePermissions(path) + if (!current.deleteable && path !== '/tmp') throw new Error(Errors.EPERM) + if (path !== '/tmp') await this.handlePermissions(path) if (current.children[filename].type !== 'directory') throw new Error(Errors.ENOTDIR) diff --git a/src/system/apps/Editor.ts b/src/system/apps/Editor.ts index 84743dd..827a9e9 100644 --- a/src/system/apps/Editor.ts +++ b/src/system/apps/Editor.ts @@ -12,26 +12,6 @@ interface EditorConfig { path: string } -const fileLanguageMap: { - [key: string]: string -} = { - c: 'clike', - cpp: 'clike', - java: 'clike', - cs: 'clike', - ts: 'typescript', - js: 'javascript', - mjs: 'javascript', - cjs: 'javascript', - jsx: 'jsx', - tsx: 'tsx', - html: 'html', - md: 'markdown', - css: 'css', - xml: 'xml', - py: 'python' -} - const Editor: Process = { config: { name: 'Editor', @@ -40,6 +20,8 @@ const Editor: Process = { targetVer: '1.0.0-indev.0' }, run: async (process) => { + const MIMETypes = await process.loadLibrary('lib/MIMETypes') + if (Object.keys(process.data).length > 0) { const win = await process.loadLibrary('lib/WindowManager').then((wm: any) => { return wm.createWindow({ @@ -153,8 +135,44 @@ const Editor: Process = { } }) - const fileExtension = data.path.split('.').pop()?.toLowerCase() as string - const language = fileLanguageMap[fileExtension] ?? 'text' + const fileExtension = (data.path.split('.').pop() as string).toLowerCase() + console.log('owo ' + fileExtension, MIMETypes) + const mime = fileExtension in MIMETypes ? MIMETypes[fileExtension].type : 'text/plain' + let language = 'text' + + switch (mime) { + case 'text/markdown': + language = 'markdown' + break + case 'text/css': + language = 'css' + break + case 'text/html': + language = 'html' + break + case 'text/javascript': + language = 'javascript' + break + case 'text/jsx': + language = 'jsx' + break + case 'application/x-flow-theme': + case 'application/json': + language = 'clike' + break + case 'text/typescript': + language = 'typescript' + break + case 'text/tsx': + language = 'tsx' + break + case 'application/python': + language = 'python' + break + default: + language = 'text' + break + } const value = Buffer.from(await fs.readFile(data.path)).toString() const editor = fullEditor( @@ -169,6 +187,7 @@ const Editor: Process = { const style = document.createElement('style') style.textContent = ` .prism-code-editor { + color: var(--text); border-radius: 10px 10px 0 0; caret-color: var(--text); font-weight: 400; @@ -208,6 +227,9 @@ const Editor: Process = { document.addEventListener('fs_update', () => { render().catch(e => console.error(e)) }) + document.addEventListener('theme_update', () => { + render().catch(e => console.error(e)) + }) } return } diff --git a/src/system/apps/Files.ts b/src/system/apps/Files.ts index a391426..a64e7f1 100644 --- a/src/system/apps/Files.ts +++ b/src/system/apps/Files.ts @@ -74,6 +74,7 @@ const Files: Process = { element.innerHTML += `${icon} ${file}delete_foreveredit`; (element.querySelector('.rename') as HTMLElement).onclick = async () => { const value = prompt('Rename') + console.log(value) if (value != null) { await fs.rename(dir + seperator + file, dir + seperator + value) } diff --git a/src/system/apps/Settings.ts b/src/system/apps/Settings.ts index 76a6eb5..a32a947 100644 --- a/src/system/apps/Settings.ts +++ b/src/system/apps/Settings.ts @@ -27,12 +27,24 @@ const Settings: Process = { const { fs } = process const HTML = await process.loadLibrary('lib/HTML') - const { Input, Button } = await process.loadLibrary('lib/Components') + const { Input, Button, Dropdown } = await process.loadLibrary('lib/Components') const render = async (config: any): Promise => { win.content.innerHTML = '' for (const item in config) { - const input = Input.new().attr({ + console.log(config[item]) + const input = item === 'THEME' + ? Dropdown.new((await fs.readdir('/etc/themes')).map((theme: string) => theme.replace('.theme', ''))) + : Input.new() + + if (item === 'THEME') { + const text = config[item] + const $select = input.elm as HTMLSelectElement + const $options = Array.from($select.options) + const optionToSelect = $options.find(item => item.text === text) + if (optionToSelect != null) optionToSelect.selected = true + } + input.attr({ value: config[item] }) new HTML('div') @@ -62,6 +74,9 @@ const Settings: Process = { } }) ) + if (item === 'THEME') { + document.dispatchEvent(new CustomEvent('theme_update', {})) + } }) .catch(e => console.error(e)) }) diff --git a/src/system/apps/ThemeMaker.ts b/src/system/apps/ThemeMaker.ts new file mode 100644 index 0000000..89fdf76 --- /dev/null +++ b/src/system/apps/ThemeMaker.ts @@ -0,0 +1,77 @@ +import { Process } from '../../types' +import icon from '../../assets/icons/theme-config.svg' + +const ThemeConfig: Process = { + config: { + name: 'Theme Maker', + type: 'process', + icon, + targetVer: '2.0.0' + }, + run: async (process) => { + const wm = await process.loadLibrary('lib/WindowManager') + const HTML = await process.loadLibrary('lib/HTML') + const { Input, Button } = await process.loadLibrary('lib/Components') + + const win = wm.createWindow({ + title: 'Theme Maker', + icon, + width: 600, + height: 200 + }, process) + + const content = new HTML(win.content) + + const items = [ + 'crust', + 'mantle', + 'base', + 'surface-0', + 'surface-1', + 'surface-2', + 'text' + ] + + const name = Input.new().attr({ + value: 'My Theme' + }) + + content.appendMany( + new HTML('div') + .appendMany( + new HTML('label').text('Name: '), + name + ), + ...items.map((item) => { + return new HTML('div') + .appendMany( + new HTML('label').text(`${item[0].toUpperCase() + item.slice(1)}: `), + Input.new().attr({ + type: 'color', + id: item, + value: '#000000' + }) + ) + }), + Button.new().text('Create').on('click', () => { + const theme: { + [key: string]: any + } = { + name: name.getValue(), + colors: {} + } + items.forEach((item) => { + theme.colors[item] = content.qs(`#${item}`)?.getValue() + }) + process.fs.writeFile(`/etc/themes/${theme.name.replace(/\s/g, '') as string}.theme`, JSON.stringify(theme)) + .then(() => { + wm.createModal('ok', 'Theme Manager', 'Theme created successfully.', process) + .catch(e => console.error(e)) + }) + .catch(e => console.error(e)) + }) + ) + } +} + +export default ThemeConfig diff --git a/src/system/lib/Components.ts b/src/system/lib/Components.ts index 3647015..ce2f182 100644 --- a/src/system/lib/Components.ts +++ b/src/system/lib/Components.ts @@ -48,6 +48,23 @@ const Components: Library = { 'font-size': size }) } + }, + Dropdown: { + new: (options: string[]) => { + const { HTML } = library + const dropdown = new HTML('select') + dropdown.style({ + 'border-radius': '5px', + padding: '2.5px', + background: 'var(--base)', + border: '1px solid var(--surface-1)' + }).appendMany( + ...options.map((option) => { + return new HTML('option').text(option) + }) + ) + return dropdown + } } } } diff --git a/src/system/lib/MIMETypes.ts b/src/system/lib/MIMETypes.ts index dca701b..eeb4916 100644 --- a/src/system/lib/MIMETypes.ts +++ b/src/system/lib/MIMETypes.ts @@ -44,6 +44,30 @@ const MIMETypes: Library = { opensWith: ['apps/ImageViewer'], icon: 'image' }, + cjs: { + type: 'application/javascript', + description: 'CommonJS Module', + opensWith: ['apps/Editor'], + icon: 'code' + }, + htm: { + type: 'text/html', + description: 'HTML Document', + opensWith: ['apps/Editor'], + icon: 'code' + }, + html: { + type: 'text/html', + description: 'HTML Document', + opensWith: ['apps/Editor'], + icon: 'code' + }, + js: { + type: 'text/javascript', + description: 'JavaScript File', + opensWith: ['apps/Editor'], + icon: 'code' + }, lnk: { type: 'application/x-ms-shortcut', description: 'Windows Shortcut', @@ -56,6 +80,12 @@ const MIMETypes: Library = { opensWith: ['apps/Editor'], icon: 'markdown' }, + mjs: { + type: 'text/javascript', + description: 'JavaScript Module', + opensWith: ['apps/Editor'], + icon: 'code' + }, mp4: { type: 'video/mp4', description: 'MP4 Video', @@ -74,6 +104,12 @@ const MIMETypes: Library = { opensWith: ['apps/ImageViewer'], icon: 'image' }, + theme: { + type: 'application/x-flow-theme', + description: 'FlowOS Theme', + opensWith: ['apps/Editor'], + icon: 'palette' + }, txt: { type: 'text/plain', description: 'Text Document', diff --git a/src/system/lib/WindowManager.ts b/src/system/lib/WindowManager.ts index 516678d..9d424d3 100644 --- a/src/system/lib/WindowManager.ts +++ b/src/system/lib/WindowManager.ts @@ -46,7 +46,7 @@ const WindowManager: Library = { WindowManager.data.windowArea.elm.appendChild(win.element) return win }, - createModal: async (title: string, text: string, process: ProcessLib) => { + createModal: async (type: 'allow' | 'ok', title: string, text: string, process: ProcessLib) => { const win = new FlowWindow(process, WindowManager.data, { title, icon: '', @@ -61,22 +61,31 @@ const WindowManager: Library = { win } } - document.dispatchEvent(new CustomEvent('app_opened', appOpenedEvent)) const { Button } = await process.loadLibrary('lib/Components') return { value: await new Promise((resolve) => { - new HTML('h3').text(title).appendTo(win.content) - new HTML('p').text(text).appendTo(win.content) - Button.new().text('Allow').appendTo(win.content).on('click', () => { - resolve(true) - win.close() - }) - Button.new().text('Deny').appendTo(win.content).on('click', () => { - resolve(false) - win.close() - }) + new HTML('h3').text(title).style({ margin: '0' }).appendTo(win.content) + new HTML('p').text(text).style({ margin: '0' }).appendTo(win.content) + + if (type === 'allow') document.dispatchEvent(new CustomEvent('app_opened', appOpenedEvent)) + + if (type === 'allow') { + Button.new().text('Allow').appendTo(win.content).on('click', () => { + resolve(true) + win.close() + }) + Button.new().text('Deny').appendTo(win.content).on('click', () => { + resolve(false) + win.close() + }) + } else if (type === 'ok') { + Button.new().text('OK').appendTo(win.content).on('click', () => { + win.close() + resolve(true) + }) + } WindowManager.data.windows.push(win) WindowManager.data.windowArea.elm.appendChild(win.element) diff --git a/src/types.ts b/src/types.ts index 67e54b7..83a864c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -153,7 +153,7 @@ export interface WindowManager { windows: FlowWindow[] getHighestZIndex: () => number createWindow: (config: FlowWindowConfig, process: ProcessLib) => FlowWindow - createModal: (title: string, text: string, process: ProcessLib) => Promise + createModal: (type: 'allow' | 'ok', title: string, text: string, process: ProcessLib) => Promise } export interface Launcher {