Merge pull request #134 from Flow-Works/feat/add-conditional-types-for-loadlibrary
Feat/add-conditional-types-for-loadlibrary
This commit is contained in:
commit
092a25d33a
10 changed files with 84 additions and 26 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import semver from 'semver'
|
import semver from 'semver'
|
||||||
import Kernel from '../kernel'
|
import Kernel from '../kernel'
|
||||||
import { Process, Executable, LibraryData, Package, Library, Permission } from '../types'
|
import { Process, Executable, Package, Library, Permission, LoadedLibrary, LibraryPath } from '../types'
|
||||||
import FlowWindow from './FlowWindow'
|
import FlowWindow from './FlowWindow'
|
||||||
import LibraryLib from './LibraryLib'
|
import LibraryLib from './LibraryLib'
|
||||||
import ProcLib from './ProcLib'
|
import ProcLib from './ProcLib'
|
||||||
|
|
@ -57,7 +57,7 @@ export default class ProcessLib {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadLibrary (url: string): Promise<LibraryData> {
|
async loadLibrary <T extends LibraryPath>(url: T): Promise<LoadedLibrary<T>> {
|
||||||
let executable: Executable
|
let executable: Executable
|
||||||
try {
|
try {
|
||||||
const comps = import.meta.glob('../system/**/*.ts')
|
const comps = import.meta.glob('../system/**/*.ts')
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ const BootLoader: Process = {
|
||||||
const files = await fs.readdir('/home/Applications/')
|
const files = await fs.readdir('/home/Applications/')
|
||||||
files
|
files
|
||||||
.filter((x: string) => x.endsWith('.app') && ((input.elm as HTMLInputElement) !== null ? x.toLowerCase().includes((input.elm as HTMLInputElement).value.toLowerCase()) : true))
|
.filter((x: string) => x.endsWith('.app') && ((input.elm as HTMLInputElement) !== null ? x.toLowerCase().includes((input.elm as HTMLInputElement).value.toLowerCase()) : true))
|
||||||
.forEach(async (file: string) => {
|
.forEach((file: string) => {
|
||||||
fs.readFile(`/home/Applications/${file}`).then(async (data: Uint8Array) => {
|
fs.readFile(`/home/Applications/${file}`).then(async (data: Uint8Array) => {
|
||||||
const path = Buffer.from(data).toString()
|
const path = Buffer.from(data).toString()
|
||||||
const executable = await process.kernel.getExecutable(path) as Process
|
const executable = await process.kernel.getExecutable(path) as Process
|
||||||
|
|
@ -97,7 +97,7 @@ const BootLoader: Process = {
|
||||||
alt: `${executable.config.name} icon`
|
alt: `${executable.config.name} icon`
|
||||||
}).appendTo(appElement)
|
}).appendTo(appElement)
|
||||||
new HTML('div').text(executable.config.name).appendTo(appElement)
|
new HTML('div').text(executable.config.name).appendTo(appElement)
|
||||||
})
|
}).catch((e: any) => console.error(e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,11 +129,11 @@ const BootLoader: Process = {
|
||||||
|
|
||||||
setInterval((): any => {
|
setInterval((): any => {
|
||||||
getTime().then((time) => {
|
getTime().then((time) => {
|
||||||
statusBar.element.qs('div[data-toolbar-id="calendar"]').text(time)
|
statusBar.element.qs('div[data-toolbar-id="calendar"]')?.text(time)
|
||||||
}).catch(e => console.error)
|
}).catch(e => console.error)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
statusBar.element.qs('div[data-toolbar-id="start"]').on('click', () => {
|
statusBar.element.qs('div[data-toolbar-id="start"]')?.on('click', () => {
|
||||||
launcher.toggle()
|
launcher.toggle()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -180,11 +180,11 @@ const BootLoader: Process = {
|
||||||
e.detail.win.focus()
|
e.detail.win.focus()
|
||||||
e.detail.win.toggleMin()
|
e.detail.win.toggleMin()
|
||||||
})
|
})
|
||||||
).appendTo(statusBar.element.qs('div[data-toolbar-id="apps"]'))
|
).appendTo(statusBar.element.qs('div[data-toolbar-id="apps"]')?.elm as HTMLElement)
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('app_closed', (e: AppClosedEvent): void => {
|
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()
|
statusBar.element.qs('div[data-toolbar-id="apps"]')?.qs(`img[data-id="${e.detail.token}"]`)?.elm.parentElement?.remove()
|
||||||
})
|
})
|
||||||
|
|
||||||
document.body.style.flexDirection = 'column-reverse'
|
document.body.style.flexDirection = 'column-reverse'
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const UserAccessControl: Process = {
|
||||||
const target = process.data.executable
|
const target = process.data.executable
|
||||||
return await new Promise((resolve) => {
|
return await new Promise((resolve) => {
|
||||||
process.loadLibrary('lib/WindowManager').then(async wm => {
|
process.loadLibrary('lib/WindowManager').then(async wm => {
|
||||||
let message
|
let message = 'Unknown action'
|
||||||
switch (process.data.type) {
|
switch (process.data.type) {
|
||||||
case 'launch': {
|
case 'launch': {
|
||||||
message = `${initiator.config.name as string} wants to launch ${target.config.name as string}`
|
message = `${initiator.config.name as string} wants to launch ${target.config.name as string}`
|
||||||
|
|
@ -42,7 +42,7 @@ const UserAccessControl: Process = {
|
||||||
} else {
|
} else {
|
||||||
resolve(false)
|
resolve(false)
|
||||||
}
|
}
|
||||||
})
|
}).catch((e) => console.error(e))
|
||||||
}).catch((e) => console.error(e))
|
}).catch((e) => console.error(e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const BrowserApp: Process = {
|
||||||
targetVer: '1.0.0-indev.0'
|
targetVer: '1.0.0-indev.0'
|
||||||
},
|
},
|
||||||
run: async (process) => {
|
run: async (process) => {
|
||||||
const win = await process.loadLibrary('lib/WindowManager').then((wm: any) => {
|
const win = await process.loadLibrary('lib/WindowManager').then(wm => {
|
||||||
return wm.createWindow({
|
return wm.createWindow({
|
||||||
title: 'Browser',
|
title: 'Browser',
|
||||||
icon,
|
icon,
|
||||||
|
|
@ -75,7 +75,7 @@ const BrowserApp: Process = {
|
||||||
iframe: HTMLIFrameElement = document.createElement('iframe')
|
iframe: HTMLIFrameElement = document.createElement('iframe')
|
||||||
|
|
||||||
constructor (url: string) {
|
constructor (url: string) {
|
||||||
this.iframe.src = `/service/${xor.encode(url) as string}`
|
this.iframe.src = `/service/${xor.encode(url)}`
|
||||||
this.iframe.style.display = 'none'
|
this.iframe.style.display = 'none'
|
||||||
|
|
||||||
this.header.innerHTML = `
|
this.header.innerHTML = `
|
||||||
|
|
@ -97,7 +97,7 @@ const BrowserApp: Process = {
|
||||||
if (this === tabManager.activeTab) {
|
if (this === tabManager.activeTab) {
|
||||||
(win.content.querySelector('.toggle') as HTMLElement).innerHTML = 'toggle_on'
|
(win.content.querySelector('.toggle') as HTMLElement).innerHTML = 'toggle_on'
|
||||||
}
|
}
|
||||||
this.iframe.src = `/service/${xor.encode(win.content.querySelector('input').value) as string}`
|
this.iframe.src = `/service/${xor.encode(win.content.querySelector('input')?.value ?? '')}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +167,7 @@ const BrowserApp: Process = {
|
||||||
|
|
||||||
win.content.querySelector('.inp')?.addEventListener('keydown', (event: KeyboardEvent) => {
|
win.content.querySelector('.inp')?.addEventListener('keydown', (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
tabManager.activeTab.iframe.src = tabManager.activeTab.proxy ? `/service/${xor.encode((win.content.querySelector('.inp') as HTMLInputElement).value) as string}` : (win.content.querySelector('.inp') as HTMLInputElement).value
|
tabManager.activeTab.iframe.src = tabManager.activeTab.proxy ? `/service/${xor.encode((win.content.querySelector('.inp') as HTMLInputElement).value)}` : (win.content.querySelector('.inp') as HTMLInputElement).value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ const Files: Process = {
|
||||||
(win.content.querySelector('.folder') as HTMLElement).onclick = async () => {
|
(win.content.querySelector('.folder') as HTMLElement).onclick = async () => {
|
||||||
const title = prompt('Enter folder name')
|
const title = prompt('Enter folder name')
|
||||||
if (title != null) {
|
if (title != null) {
|
||||||
await fs.mkdir(`${dir}/${title}`, '')
|
await fs.mkdir(`${dir}/${title}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ const Files: Process = {
|
||||||
const genIcon = (): string => {
|
const genIcon = (): string => {
|
||||||
return `<span class="material-symbols-rounded">${(MIMETypes[file.split('.')[1]] === undefined ? 'draft' : MIMETypes[file.split('.')[1]].icon) as string}</span>`
|
return `<span class="material-symbols-rounded">${(MIMETypes[file.split('.')[1]] === undefined ? 'draft' : MIMETypes[file.split('.')[1]].icon) as string}</span>`
|
||||||
}
|
}
|
||||||
const icon = fileStat.isDirectory() as boolean ? '<span class="material-symbols-rounded">folder</span>' : genIcon()
|
const icon = fileStat.isDirectory() ? '<span class="material-symbols-rounded">folder</span>' : genIcon()
|
||||||
|
|
||||||
element.innerHTML += `${icon} <span style="flex:1;">${file}</span><span class="material-symbols-rounded delete">delete_forever</span><span class="material-symbols-rounded rename">edit</span>`;
|
element.innerHTML += `${icon} <span style="flex:1;">${file}</span><span class="material-symbols-rounded delete">delete_forever</span><span class="material-symbols-rounded rename">edit</span>`;
|
||||||
(element.querySelector('.rename') as HTMLElement).onclick = async () => {
|
(element.querySelector('.rename') as HTMLElement).onclick = async () => {
|
||||||
|
|
@ -79,7 +79,7 @@ const Files: Process = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(element.querySelector('.delete') as HTMLElement).onclick = async () => {
|
(element.querySelector('.delete') as HTMLElement).onclick = async () => {
|
||||||
if (fileStat.isDirectory() as boolean) {
|
if (fileStat.isDirectory()) {
|
||||||
await fs.rmdir(dir + seperator + file)
|
await fs.rmdir(dir + seperator + file)
|
||||||
} else {
|
} else {
|
||||||
await fs.unlink(dir + seperator + file)
|
await fs.unlink(dir + seperator + file)
|
||||||
|
|
@ -101,7 +101,7 @@ const Files: Process = {
|
||||||
}
|
}
|
||||||
|
|
||||||
element.ondblclick = async () => {
|
element.ondblclick = async () => {
|
||||||
if (fileStat.isDirectory() as boolean) {
|
if (fileStat.isDirectory()) {
|
||||||
await setDir(dir + seperator + file)
|
await setDir(dir + seperator + file)
|
||||||
} else {
|
} else {
|
||||||
await run(dir + seperator + file)
|
await run(dir + seperator + file)
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ const Store: Process = {
|
||||||
install(app.url)
|
install(app.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}).catch((e: any) => console.error(e))
|
||||||
}).catch((e: any) => console.error(e))
|
}).catch((e: any) => console.error(e))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Library } from '../../types'
|
import { Library } from '../../types'
|
||||||
|
|
||||||
const MIME: Library = {
|
const MIMETypes: Library = {
|
||||||
config: {
|
config: {
|
||||||
name: 'MIMETypes',
|
name: 'MIMETypes',
|
||||||
type: 'library',
|
type: 'library',
|
||||||
|
|
@ -83,4 +83,4 @@ const MIME: Library = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MIME
|
export default MIMETypes
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import Kernel from '../../kernel'
|
import Kernel from '../../kernel'
|
||||||
import ProcessLib from '../../structures/ProcessLib'
|
import ProcessLib from '../../structures/ProcessLib'
|
||||||
import { Directory, Errors, File, Library, Permission } from '../../types'
|
import { Directory, Errors, File, Library, Permission, Stats } from '../../types'
|
||||||
|
|
||||||
console.debug = (...args: any[]) => {
|
console.debug = (...args: any[]) => {
|
||||||
console.log('[VirtualFS]', ...args)
|
console.log('[VirtualFS]', ...args)
|
||||||
|
|
@ -421,7 +421,7 @@ const VirtualFS: Library = {
|
||||||
console.debug(`readdir ${path}`)
|
console.debug(`readdir ${path}`)
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
stat: async (path: string): Promise<{ isDirectory: () => boolean, isFile: () => boolean }> => {
|
stat: async (path: string): Promise<Stats> => {
|
||||||
const { current } = await navigatePath(path)
|
const { current } = await navigatePath(path)
|
||||||
|
|
||||||
console.debug(`stat ${path}`)
|
console.debug(`stat ${path}`)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@ const XOR: Library = {
|
||||||
},
|
},
|
||||||
init: (l, k, p) => {},
|
init: (l, k, p) => {},
|
||||||
data: {
|
data: {
|
||||||
randomMax: 100,
|
|
||||||
randomMin: -100,
|
|
||||||
|
|
||||||
encode: (str: string): string => {
|
encode: (str: string): string => {
|
||||||
return encodeURIComponent(
|
return encodeURIComponent(
|
||||||
str
|
str
|
||||||
|
|
|
||||||
61
src/types.ts
61
src/types.ts
|
|
@ -1,7 +1,10 @@
|
||||||
|
import HTML from './HTML'
|
||||||
import Kernel from './kernel'
|
import Kernel from './kernel'
|
||||||
import FlowWindow from './structures/FlowWindow'
|
import FlowWindow from './structures/FlowWindow'
|
||||||
import LibraryLib from './structures/LibraryLib'
|
import LibraryLib from './structures/LibraryLib'
|
||||||
import ProcessLib from './structures/ProcessLib'
|
import ProcessLib from './structures/ProcessLib'
|
||||||
|
import Components from './system/lib/Components'
|
||||||
|
import MIMETypes from './system/lib/MIMETypes'
|
||||||
|
|
||||||
export interface AppClosedEvent extends CustomEvent {
|
export interface AppClosedEvent extends CustomEvent {
|
||||||
detail: {
|
detail: {
|
||||||
|
|
@ -124,3 +127,61 @@ export interface KernelConfig {
|
||||||
SERVER: string
|
SERVER: string
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Stats {
|
||||||
|
isDirectory: () => boolean
|
||||||
|
isFile: () => boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileSystem {
|
||||||
|
unlink: (path: string) => Promise<void>
|
||||||
|
readFile: (path: string) => Promise<Buffer>
|
||||||
|
writeFile: (path: string, content: string | Buffer) => Promise<void>
|
||||||
|
mkdir: (path: string) => Promise<void>
|
||||||
|
rmdir: (path: string) => Promise<void>
|
||||||
|
readdir: (path: string) => Promise<string[]>
|
||||||
|
stat: (path: string) => Promise<Stats>
|
||||||
|
rename: (oldPath: string, newPath: string) => Promise<void>
|
||||||
|
exists: (path: string) => Promise<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalData {
|
||||||
|
value: boolean
|
||||||
|
win: FlowWindow
|
||||||
|
}
|
||||||
|
export interface WindowManager {
|
||||||
|
windowArea: HTML
|
||||||
|
windows: FlowWindow[]
|
||||||
|
getHighestZIndex: () => number
|
||||||
|
createWindow: (config: FlowWindowConfig, process: ProcessLib) => FlowWindow
|
||||||
|
createModal: (title: string, text: string, process: ProcessLib) => Promise<ModalData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Launcher {
|
||||||
|
element: HTML
|
||||||
|
toggle: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface XOR {
|
||||||
|
encode: (str: string) => string
|
||||||
|
decode: (str: string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatusBar {
|
||||||
|
element: HTML
|
||||||
|
updateBatteryIcon: (battery: any) => void
|
||||||
|
updateIcon: (ms: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LoadedLibrary<T> =
|
||||||
|
T extends 'lib/VirtualFS' ? FileSystem :
|
||||||
|
T extends 'lib/WindowManager' ? WindowManager :
|
||||||
|
T extends 'lib/HTML' ? typeof HTML :
|
||||||
|
T extends 'lib/Launcher' ? Launcher :
|
||||||
|
T extends 'lib/XOR' ? XOR :
|
||||||
|
T extends 'lib/StatusBar' ? StatusBar :
|
||||||
|
T extends 'lib/MIMETypes' ? typeof MIMETypes.data :
|
||||||
|
T extends 'lib/Components' ? typeof Components.data :
|
||||||
|
any
|
||||||
|
|
||||||
|
export type LibraryPath = 'lib/VirtualFS' | 'lib/WindowManager' | string
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue