Merge pull request #136 from Flow-Works/dev

[🪢] Merge `dev` into `master`
This commit is contained in:
ThinLiquid 2024-01-17 17:15:44 +00:00 committed by GitHub
commit 3a292d68bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 136 additions and 67 deletions

View file

@ -1,9 +1,9 @@
name: build
on:
push:
branches: [master]
branches: [master, dev]
pull_request:
branches: [master]
branches: [master, dev]
jobs:
build:
runs-on: ubuntu-latest

View file

@ -1,9 +1,9 @@
name: ts-standard
on:
push:
branches: [master]
branches: [master, dev]
pull_request:
branches: [master]
branches: [master, dev]
jobs:
ts-standard:
runs-on: ubuntu-latest

View file

@ -1,10 +1,9 @@
import './assets/style.less'
import { version } from '../package.json'
import { v4 as uuid } from 'uuid'
import { Permission } from './system/lib/VirtualFS'
import ProcessLib from './structures/ProcessLib'
import ProcLib from './structures/ProcLib'
import { Executable, Process, Package, ProcessInfo, KernelConfig } from './types'
import { Executable, Process, Package, ProcessInfo, KernelConfig, Permission } from './types'
import semver from 'semver'
declare global {

View file

@ -1,7 +1,6 @@
import semver from 'semver'
import Kernel from '../kernel'
import { Permission } from '../system/lib/VirtualFS'
import { Process, Executable, LibraryData, Package, Library } from '../types'
import { Process, Executable, Package, Library, Permission, LoadedLibrary, LibraryPath } from '../types'
import FlowWindow from './FlowWindow'
import LibraryLib from './LibraryLib'
import ProcLib from './ProcLib'
@ -58,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
try {
const comps = import.meta.glob('../system/**/*.ts')

View file

@ -1,5 +1,5 @@
import HTML from '../HTML'
import { AppClosedEvent, AppOpenedEvent, Process } from '../types'
import { AppClosedEvent, AppOpenedEvent, Directory, FileSystemObject, Process } from '../types'
import { getTime } from '../utils'
import { db, defaultFS, initializeDatabase, read, setFileSystem, write } from './lib/VirtualFS'
import nullIcon from '../assets/icons/application-default-icon.svg'
@ -32,10 +32,19 @@ const BootLoader: Process = {
} else {
console.warn('Persistent storage is not supported.')
}
const fileSystem = await read()
const fileSystem = await read() as FileSystemObject
if (fileSystem === undefined) {
await write(defaultFS)
} else {
const appsDirectory = ((fileSystem.root.children.home as Directory).children.Applications as Directory).children
const defaultAppsDirectory = ((defaultFS.root.children.home as Directory).children.Applications as Directory).children
for (const file in defaultAppsDirectory) {
if (appsDirectory[file] === undefined && defaultAppsDirectory[file] !== undefined) {
console.log(file)
appsDirectory[file] = defaultAppsDirectory[file]
}
}
await write(fileSystem)
await setFileSystem(fileSystem)
}
@ -74,7 +83,7 @@ const BootLoader: Process = {
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(async (file: string) => {
.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
@ -88,7 +97,7 @@ const BootLoader: Process = {
alt: `${executable.config.name} icon`
}).appendTo(appElement)
new HTML('div').text(executable.config.name).appendTo(appElement)
})
}).catch((e: any) => console.error(e))
})
}
@ -120,11 +129,11 @@ const BootLoader: Process = {
setInterval((): any => {
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)
}, 1000)
statusBar.element.qs('div[data-toolbar-id="start"]').on('click', () => {
statusBar.element.qs('div[data-toolbar-id="start"]')?.on('click', () => {
launcher.toggle()
})
@ -171,11 +180,11 @@ const BootLoader: Process = {
e.detail.win.focus()
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 => {
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'

View file

@ -16,7 +16,7 @@ const UserAccessControl: Process = {
const target = process.data.executable
return await new Promise((resolve) => {
process.loadLibrary('lib/WindowManager').then(async wm => {
let message
let message = 'Unknown action'
switch (process.data.type) {
case 'launch': {
message = `${initiator.config.name as string} wants to launch ${target.config.name as string}`
@ -42,7 +42,7 @@ const UserAccessControl: Process = {
} else {
resolve(false)
}
})
}).catch((e) => console.error(e))
}).catch((e) => console.error(e))
})
}

View file

@ -9,7 +9,7 @@ const BrowserApp: Process = {
targetVer: '1.0.0-indev.0'
},
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({
title: 'Browser',
icon,
@ -75,7 +75,7 @@ const BrowserApp: Process = {
iframe: HTMLIFrameElement = document.createElement('iframe')
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.header.innerHTML = `
@ -97,7 +97,7 @@ const BrowserApp: Process = {
if (this === tabManager.activeTab) {
(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) => {
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
}
});

View file

@ -56,7 +56,7 @@ const Files: Process = {
(win.content.querySelector('.folder') as HTMLElement).onclick = async () => {
const title = prompt('Enter folder name')
if (title != null) {
await fs.mkdir(`${dir}/${title}`, '')
await fs.mkdir(`${dir}/${title}`)
}
}
@ -69,7 +69,7 @@ const Files: Process = {
const genIcon = (): string => {
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.querySelector('.rename') as HTMLElement).onclick = async () => {
@ -79,7 +79,7 @@ const Files: Process = {
}
}
(element.querySelector('.delete') as HTMLElement).onclick = async () => {
if (fileStat.isDirectory() as boolean) {
if (fileStat.isDirectory()) {
await fs.rmdir(dir + seperator + file)
} else {
await fs.unlink(dir + seperator + file)
@ -101,7 +101,7 @@ const Files: Process = {
}
element.ondblclick = async () => {
if (fileStat.isDirectory() as boolean) {
if (fileStat.isDirectory()) {
await setDir(dir + seperator + file)
} else {
await run(dir + seperator + file)

View file

@ -43,7 +43,7 @@ const Info: Process = {
}).text(`v${String(process.sysInfo.version)}`),
new HTML('br'),
new HTML('a').attr({
href: ''
href: 'https://discord.gg/nj93ywpyRy'
}).append(
new HTML('img').attr({
src: badge,

View file

@ -81,7 +81,7 @@ const Store: Process = {
install(app.url)
}
}
})
}).catch((e: any) => console.error(e))
}).catch((e: any) => console.error(e))
})
})

View file

@ -1,6 +1,6 @@
import { Library } from '../../types'
const MIME: Library = {
const MIMETypes: Library = {
config: {
name: 'MIMETypes',
type: 'library',
@ -83,4 +83,4 @@ const MIME: Library = {
}
}
export default MIME
export default MIMETypes

View file

@ -1,42 +1,11 @@
import Kernel from '../../kernel'
import ProcessLib from '../../structures/ProcessLib'
import { Library } from '../../types'
import { Directory, Errors, File, Library, Permission, Stats } from '../../types'
console.debug = (...args: any[]) => {
console.log('[VirtualFS]', ...args)
}
export enum Errors {
ENOENT = 'ENOENT',
EISDIR = 'EISDIR',
EEXIST = 'EEXIST',
EPERM = 'EPERM',
ENOTDIR = 'ENOTDIR',
EACCES = 'EACCES'
}
export enum Permission {
USER,
ELEVATED,
SYSTEM
}
export interface Directory {
type: 'directory'
permission: Permission
deleteable: boolean
children: {
[key: string]: Directory | File
}
}
export interface File {
type: 'file'
permission: Permission
deleteable: boolean
content: Buffer
}
export const defaultFS: { root: Directory } = {
root: {
type: 'directory',
@ -452,7 +421,7 @@ const VirtualFS: Library = {
console.debug(`readdir ${path}`)
return result
},
stat: async (path: string): Promise<{ isDirectory: () => boolean, isFile: () => boolean }> => {
stat: async (path: string): Promise<Stats> => {
const { current } = await navigatePath(path)
console.debug(`stat ${path}`)

View file

@ -8,9 +8,6 @@ const XOR: Library = {
},
init: (l, k, p) => {},
data: {
randomMax: 100,
randomMin: -100,
encode: (str: string): string => {
return encodeURIComponent(
str

View file

@ -1,7 +1,10 @@
import HTML from './HTML'
import Kernel from './kernel'
import FlowWindow from './structures/FlowWindow'
import LibraryLib from './structures/LibraryLib'
import ProcessLib from './structures/ProcessLib'
import Components from './system/lib/Components'
import MIMETypes from './system/lib/MIMETypes'
export interface AppClosedEvent extends CustomEvent {
detail: {
@ -9,6 +12,41 @@ export interface AppClosedEvent extends CustomEvent {
}
}
export enum Errors {
ENOENT = 'ENOENT',
EISDIR = 'EISDIR',
EEXIST = 'EEXIST',
EPERM = 'EPERM',
ENOTDIR = 'ENOTDIR',
EACCES = 'EACCES'
}
export enum Permission {
USER,
ELEVATED,
SYSTEM
}
export interface Directory {
type: 'directory'
permission: Permission
deleteable: boolean
children: {
[key: string]: Directory | File
}
}
export interface File {
type: 'file'
permission: Permission
deleteable: boolean
content: Buffer
}
export interface FileSystemObject {
root: Directory
}
export interface AppOpenedEvent extends CustomEvent {
detail: {
proc: Process
@ -89,3 +127,61 @@ export interface KernelConfig {
SERVER: string
[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