From 11540b945a5a1eb16d9785f6507699bec914193d Mon Sep 17 00:00:00 2001 From: ThinLiquid Date: Tue, 23 Jan 2024 03:48:34 +0000 Subject: [PATCH 1/6] feat: added support for theming re #155 --- src/assets/style.less | 5 +- src/kernel.ts | 14 +++ src/system/BootLoader.ts | 171 ----------------------------------- src/system/VirtualFS.ts | 101 +++++++++++++++------ src/system/apps/Files.ts | 1 + src/system/apps/Settings.ts | 19 +++- src/system/lib/Components.ts | 17 ++++ src/system/lib/MIMETypes.ts | 6 ++ 8 files changed, 132 insertions(+), 202 deletions(-) delete mode 100644 src/system/BootLoader.ts 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/VirtualFS.ts b/src/system/VirtualFS.ts index a6dbeee..35b6056 100644 --- a/src/system/VirtualFS.ts +++ b/src/system/VirtualFS.ts @@ -1,3 +1,4 @@ +import path from 'path' import { Directory, Errors, File, Permission, Stats } from '../types' export const defaultFS: { root: Directory } = { @@ -176,13 +177,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 +237,63 @@ export const defaultFS: { root: Directory } = { } class VirtualFS { - private fileSystem: { root: Directory } = defaultFS + private fileSystem: { root: Directory } private db: IDBDatabase | null = null + + private readonly addMissingFiles = async (): Promise => { + const addMissingFiles = async (current: Directory, currentPath: string): Promise => { + for (const child in current.children) { + const childPath = path.join(currentPath, child) + if (current.children[child].type === 'directory') { + if (!await this.exists(childPath)) { + await this.mkdir(childPath) + } + await addMissingFiles(current.children[child] as Directory, childPath) + } else if (current.children[child].type === 'file' && !await this.exists(childPath)) { + await this.writeFile(childPath, (current.children[child] as File).content) + } + } + } + await addMissingFiles(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() + console.log(this.fileSystem) 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 +305,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 +418,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/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/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..782e7c4 100644 --- a/src/system/lib/MIMETypes.ts +++ b/src/system/lib/MIMETypes.ts @@ -74,6 +74,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', From 4e4615ecb15688deef901b50157047ebb08caf17 Mon Sep 17 00:00:00 2001 From: ThinLiquid Date: Tue, 23 Jan 2024 13:02:48 +0000 Subject: [PATCH 2/6] feat: added theme maker application --- package.json | 2 +- src/system/UserAccessControl.ts | 2 +- src/system/VirtualFS.ts | 60 ++++++++----------------- src/system/apps/ThemeMaker.ts | 77 +++++++++++++++++++++++++++++++++ src/system/lib/WindowManager.ts | 26 +++++++---- src/types.ts | 2 +- 6 files changed, 114 insertions(+), 55 deletions(-) create mode 100644 src/system/apps/ThemeMaker.ts diff --git a/package.json b/package.json index 9d5c05f..6514682 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flowos", - "version": "1.1.0", + "version": "2.0.0", "description": "The most aesthetic webOS.", "main": "src/bootloader.ts", "scripts": { 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 35b6056..f720555 100644 --- a/src/system/VirtualFS.ts +++ b/src/system/VirtualFS.ts @@ -1,4 +1,3 @@ -import path from 'path' import { Directory, Errors, File, Permission, Stats } from '../types' export const defaultFS: { root: Directory } = { @@ -76,6 +75,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') } } }, @@ -96,47 +101,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') } } }, @@ -240,21 +215,21 @@ class VirtualFS { private fileSystem: { root: Directory } private db: IDBDatabase | null = null - private readonly addMissingFiles = async (): Promise => { - const addMissingFiles = async (current: Directory, currentPath: string): Promise => { - for (const child in current.children) { - const childPath = path.join(currentPath, child) - if (current.children[child].type === 'directory') { - if (!await this.exists(childPath)) { - await this.mkdir(childPath) + private async addMissingFiles (): Promise { + const addDirectoryRecursive = async (directory: Directory, directoryPath: string): Promise => { + for (const [key, value] of Object.entries(directory.children)) { + const path = (await import('path')).join(directoryPath, key) + if (value.type === 'directory') { + if (!await this.exists(path)) { + await this.mkdir(path) } - await addMissingFiles(current.children[child] as Directory, childPath) - } else if (current.children[child].type === 'file' && !await this.exists(childPath)) { - await this.writeFile(childPath, (current.children[child] as File).content) + await addDirectoryRecursive(value, path) + } else if (value.type === 'file' && !await this.exists(path)) { + await this.writeFile(path, Buffer.from(value.content).toString()) } } } - await addMissingFiles(defaultFS.root, '') + await addDirectoryRecursive(defaultFS.root, '/') } async init (dbName = 'virtualfs'): Promise { @@ -275,7 +250,6 @@ class VirtualFS { this.fileSystem = await this.read() if (this.fileSystem == null) await this.write(defaultFS) else await this.addMissingFiles() - console.log(this.fileSystem) resolve(this) } }) diff --git a/src/system/apps/ThemeMaker.ts b/src/system/apps/ThemeMaker.ts new file mode 100644 index 0000000..3f5062c --- /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: 'Task Manager', + 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/WindowManager.ts b/src/system/lib/WindowManager.ts index 516678d..db2d1eb 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: '', @@ -69,14 +69,22 @@ const WindowManager: Library = { 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() - }) + + 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', () => { + resolve(true) + win.close() + }) + } 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 { From 54a92c5d796ea203be0c2147c79d7b2410452629 Mon Sep 17 00:00:00 2001 From: ThinLiquid Date: Tue, 23 Jan 2024 14:33:24 +0000 Subject: [PATCH 3/6] fix: stopped filesystem from breaking at build --- package-lock.json | 4 ++-- src/system/VirtualFS.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 61da784..aa7c080 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "flowos", - "version": "1.1.0", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "flowos", - "version": "1.1.0", + "version": "2.0.0", "license": "MIT", "dependencies": { "ansi-to-html": "^0.7.2", diff --git a/src/system/VirtualFS.ts b/src/system/VirtualFS.ts index f720555..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: { @@ -218,7 +220,7 @@ class VirtualFS { private async addMissingFiles (): Promise { const addDirectoryRecursive = async (directory: Directory, directoryPath: string): Promise => { for (const [key, value] of Object.entries(directory.children)) { - const path = (await import('path')).join(directoryPath, key) + const path = p.join(directoryPath, key) if (value.type === 'directory') { if (!await this.exists(path)) { await this.mkdir(path) From e630ae664c895910175648a84292fc8890026c77 Mon Sep 17 00:00:00 2001 From: ThinLiquid Date: Wed, 24 Jan 2024 11:07:35 +0000 Subject: [PATCH 4/6] fix: fixed Theme Maker title and created popup fix #157 --- src/system/apps/ThemeMaker.ts | 2 +- src/system/lib/WindowManager.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/system/apps/ThemeMaker.ts b/src/system/apps/ThemeMaker.ts index 3f5062c..89fdf76 100644 --- a/src/system/apps/ThemeMaker.ts +++ b/src/system/apps/ThemeMaker.ts @@ -14,7 +14,7 @@ const ThemeConfig: Process = { const { Input, Button } = await process.loadLibrary('lib/Components') const win = wm.createWindow({ - title: 'Task Manager', + title: 'Theme Maker', icon, width: 600, height: 200 diff --git a/src/system/lib/WindowManager.ts b/src/system/lib/WindowManager.ts index db2d1eb..9d424d3 100644 --- a/src/system/lib/WindowManager.ts +++ b/src/system/lib/WindowManager.ts @@ -61,14 +61,15 @@ 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) + 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', () => { @@ -81,8 +82,8 @@ const WindowManager: Library = { }) } else if (type === 'ok') { Button.new().text('OK').appendTo(win.content).on('click', () => { - resolve(true) win.close() + resolve(true) }) } From 0c2ffbbb9fb05ac7b7214193e5884e9511d487b9 Mon Sep 17 00:00:00 2001 From: ThinLiquid Date: Wed, 24 Jan 2024 13:56:54 +0000 Subject: [PATCH 5/6] fix: made editor use theme colors --- src/system/apps/Editor.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/system/apps/Editor.ts b/src/system/apps/Editor.ts index 84743dd..9e2b672 100644 --- a/src/system/apps/Editor.ts +++ b/src/system/apps/Editor.ts @@ -169,6 +169,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 +209,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 } From cc3583bfdce3d34794dc7c7affac086696ccd1a0 Mon Sep 17 00:00:00 2001 From: ThinLiquid Date: Thu, 25 Jan 2024 08:30:59 +0000 Subject: [PATCH 6/6] feat: made editor use MIME types --- src/system/apps/Editor.ts | 62 ++++++++++++++++++++++++------------- src/system/lib/MIMETypes.ts | 30 ++++++++++++++++++ 2 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/system/apps/Editor.ts b/src/system/apps/Editor.ts index 9e2b672..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( diff --git a/src/system/lib/MIMETypes.ts b/src/system/lib/MIMETypes.ts index 782e7c4..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',