Merge branch 'master' into master
15
src/assets/icons/accessories-screenshot.svg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
16
src/assets/icons/application-default-icon.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
30
src/assets/icons/applications-games.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
18
src/assets/icons/applications-system.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
45
src/assets/icons/calc.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
42
src/assets/icons/cheese.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 44 KiB |
30
src/assets/icons/eog.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
26
src/assets/icons/file-manager.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
60
src/assets/icons/file-roller.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 44 KiB |
48
src/assets/icons/gnome-maps.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
29
src/assets/icons/gnome-music.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
25
src/assets/icons/gnome-todo.svg
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
30
src/assets/icons/indicator-weather.svg
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 34 KiB |
20
src/assets/icons/internet-mail.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
61
src/assets/icons/kvantum.svg
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 70 KiB |
18
src/assets/icons/org.gnome.Totem.svg
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
31
src/assets/icons/polari.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
61
src/assets/icons/preferences-desktop-plasma-theme.svg
Normal file
|
After Width: | Height: | Size: 32 KiB |
19
src/assets/icons/preferences-system-time.svg
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
30
src/assets/icons/preferences-system.svg
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 52 KiB |
114
src/assets/icons/shotwell.svg
Normal file
|
After Width: | Height: | Size: 29 KiB |
48
src/assets/icons/software-properties.svg
Normal file
|
After Width: | Height: | Size: 27 KiB |
36
src/assets/icons/softwarecenter-debian.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
28
src/assets/icons/softwarecenter-manjaro.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
36
src/assets/icons/softwarecenter-ubuntu.svg
Normal file
|
After Width: | Height: | Size: 12 KiB |
39
src/assets/icons/softwarecenter.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
42
src/assets/icons/system-software-install.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
37
src/assets/icons/system-software-update.svg
Normal file
|
After Width: | Height: | Size: 12 KiB |
13
src/assets/icons/terminal.svg
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
18
src/assets/icons/terminix.svg
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
1
src/assets/icons/text-editor.svg
Normal file
|
After Width: | Height: | Size: 37 KiB |
58
src/assets/icons/theme-config.svg
Normal file
|
After Width: | Height: | Size: 19 KiB |
1
src/assets/icons/userinfo.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
25
src/assets/icons/web-browser.svg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
|
|
@ -98,7 +98,7 @@ toolbar {
|
|||
|
||||
window-area {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
width: calc(100% - 20px);
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 10px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import icon from '../../assets/icons/null.png'
|
||||
import icon from '../../assets/icons/web-browser.svg'
|
||||
import { App } from '../../types'
|
||||
|
||||
import FlowWindow from '../../structures/FlowWindow'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import icon from '../../assets/icons/editor.png'
|
||||
import icon from '../../assets/icons/text-editor.svg'
|
||||
import { App } from '../../types'
|
||||
|
||||
import { fullEditor } from 'prism-code-editor/setups'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import icon from '../../assets/icons/files.png'
|
||||
import icon from '../../assets/icons/file-manager.svg'
|
||||
import { App } from '../../types'
|
||||
|
||||
import FlowWindow from '../../structures/FlowWindow'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import icon from '../../assets/icons/info.png'
|
||||
import icon from '../../assets/icons/userinfo.svg'
|
||||
import badge from '../../assets/badge.png'
|
||||
import { App, PackageJSON } from '../../types'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import icon from '../../assets/icons/manager.png'
|
||||
import icon from '../../assets/icons/software-properties.svg'
|
||||
import { App, LoadedApp, LoadedPlugin } from '../../types'
|
||||
import FlowWindow from '../../structures/FlowWindow'
|
||||
import nullIcon from '../../assets/icons/null.png'
|
||||
import nullIcon from '../../assets/icons/application-default-icon.svg'
|
||||
|
||||
export default class ManagerApp implements App {
|
||||
meta = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import icon from '../../assets/icons/music.png'
|
||||
import icon from '../../assets/icons/gnome-music.svg'
|
||||
import { App } from '../../types'
|
||||
import FlowWindow from '../../structures/FlowWindow'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import icon from '../../assets/icons/settings.png'
|
||||
import icon from '../../assets/icons/preferences-system.svg'
|
||||
import { App } from '../../types'
|
||||
import FlowWindow from '../../structures/FlowWindow'
|
||||
|
||||
|
|
|
|||
100
src/builtin/apps/store.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import icon from '../../assets/icons/softwarecenter.svg'
|
||||
import { App, RepoData } from '../../types'
|
||||
import FlowWindow from '../../structures/FlowWindow'
|
||||
import { sanitize } from '../../utils'
|
||||
import nullIcon from '../../assets/icons/application-default-icon.svg'
|
||||
|
||||
export default class MusicApp implements App {
|
||||
meta = {
|
||||
name: 'Store',
|
||||
description: 'A simple store app.',
|
||||
pkg: 'flow.store',
|
||||
version: '1.0.0',
|
||||
icon
|
||||
}
|
||||
|
||||
async open (): Promise<FlowWindow> {
|
||||
const win = window.wm.createWindow({
|
||||
title: this.meta.name,
|
||||
icon: this.meta.icon,
|
||||
width: 500,
|
||||
height: 700
|
||||
})
|
||||
|
||||
win.content.style.background = 'var(--base)'
|
||||
|
||||
const config = await window.config()
|
||||
|
||||
fetch(config.SERVER_URL + '/apps/list/')
|
||||
.then(async (res) => await res.json())
|
||||
.then(handle)
|
||||
.catch(e => console.error(e))
|
||||
|
||||
function handle (repos: RepoData[]): void {
|
||||
win.content.innerHTML = `
|
||||
<div class="repos" style="display: flex;flex-direction: column;gap: 10px;"></div>
|
||||
`
|
||||
|
||||
repos.forEach((repo) => {
|
||||
(win.content.querySelector('.repos') as HTMLElement).innerHTML += `
|
||||
<div data-repo-id="${sanitize(repo.id)}" style="display: flex;flex-direction: column;gap: 10px;background: var(--surface-0);padding: 20px;margin: 10px;border-radius: 10px;">
|
||||
<div style="flex: 1;">
|
||||
<h2 style="margin: 0;margin-bottom: 10px;">${sanitize(repo.name)}</h2>
|
||||
<code style="font-family: monospace;">${sanitize(repo.id)}</code>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="apps"></div>
|
||||
</div>
|
||||
`
|
||||
|
||||
repo.apps.forEach((app) => {
|
||||
(win.content.querySelector(`div[data-repo-id="${sanitize(repo.id)}"] > .apps`) as HTMLElement).innerHTML += `
|
||||
<div data-pkg="${sanitize(app.pkg)}" style="display: flex;gap: 20px;">
|
||||
<img src="${sanitize(app.icon ?? nullIcon)}" height="59.5px" style="border-radius: var(--app-radius);">
|
||||
<div>
|
||||
<h3 style="margin: 0;margin-bottom: 10px;">${sanitize(app.name)}</h3>
|
||||
<div style="display: flex;gap:5px;align-items: center;">
|
||||
<code style="font-family: monospace;">${sanitize(app.pkg)}</code>
|
||||
<span class="material-symbols-rounded">download</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
window.fs.exists(`/Applications/${app.url.split('/').at(-1) as string}`, (exists) => {
|
||||
if (exists) {
|
||||
(win.content.querySelector(`div[data-pkg="${sanitize(app.pkg)}"] div > .material-symbols-rounded`) as HTMLElement).innerHTML = 'delete';
|
||||
|
||||
(win.content.querySelector(`div[data-pkg="${sanitize(app.pkg)}"] div > .material-symbols-rounded`) as HTMLElement).onclick = () => {
|
||||
window.fs.unlink(`/Applications/${app.url.split('/').at(-1) as string}`, () => {
|
||||
window.location.reload()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
(win.content.querySelector(`div[data-pkg="${sanitize(app.pkg)}"] div > .material-symbols-rounded`) as HTMLElement).onclick = () => {
|
||||
install(app.url)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
(win.content.querySelector(`div[data-pkg="${sanitize(app.pkg)}"] div > .material-symbols-rounded`) as HTMLElement).onclick = () => {
|
||||
install(app.url)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function install (url: string): void {
|
||||
fetch(url).then(async (res) => await res.text())
|
||||
.then((data) => {
|
||||
window.fs.exists('/Applications', (exists) => {
|
||||
if (!exists) window.fs.promises.mkdir('/Applications').catch(console.error)
|
||||
|
||||
window.fs.promises.writeFile(`/Applications/${url.split('/').at(-1) as string}`, data).then(() => window.location.reload()).catch(console.error)
|
||||
})
|
||||
}).catch(console.error)
|
||||
}
|
||||
|
||||
return win
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
import { LoadedApp, LoadedPlugin } from '../types'
|
||||
import nullIcon from '../assets/icons/application-default-icon.svg'
|
||||
|
||||
class Flow {
|
||||
apps: LoadedApp[] = []
|
||||
appList: string[] = [
|
||||
'settings',
|
||||
'music',
|
||||
'files',
|
||||
'editor',
|
||||
'info',
|
||||
'manager',
|
||||
'browser'
|
||||
'../builtin/apps/settings.ts',
|
||||
'../builtin/apps/music.ts',
|
||||
'../builtin/apps/files.ts',
|
||||
'../builtin/apps/editor.ts',
|
||||
'../builtin/apps/info.ts',
|
||||
'../builtin/apps/manager.ts',
|
||||
'../builtin/apps/browser.ts',
|
||||
'../builtin/apps/store.ts'
|
||||
]
|
||||
|
||||
plugins: LoadedPlugin[] = []
|
||||
|
|
@ -22,15 +24,22 @@ class Flow {
|
|||
window.preloader.setPending('apps')
|
||||
window.preloader.setStatus('importing default apps...')
|
||||
|
||||
await (await window.fs.promises.readdir('/Applications')).forEach((file) => {
|
||||
window.fs.promises.readFile('/Applications/' + file).then(content => {
|
||||
this.appList.push(`data:text/javascript;base64,${btoa(content.toString())}`)
|
||||
}).catch(console.error)
|
||||
})
|
||||
|
||||
for (const appPath of this.appList) {
|
||||
window.preloader.setStatus(`importing default apps\n${appPath}`)
|
||||
const { default: ImportedApp } = await import(`../builtin/apps/${appPath}.ts`).catch(async (e: Error) => {
|
||||
const { default: ImportedApp } = await import(`${appPath}`).catch(async (e: Error) => {
|
||||
console.error(e)
|
||||
await window.preloader.setError('apps')
|
||||
window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`)
|
||||
})
|
||||
const app = new ImportedApp()
|
||||
app.builtin = true
|
||||
app.meta.icon = app.meta.icon ?? nullIcon
|
||||
|
||||
this.addApp(app)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { v4 as uuid } from 'uuid'
|
|||
import WindowManager from '../instances/WindowManager'
|
||||
import { FlowWindowConfig } from '../types'
|
||||
import { sanitize } from '../utils'
|
||||
import nullIcon from '../assets/icons/application-default-icon.svg'
|
||||
|
||||
/**
|
||||
* Makes an element draggable.
|
||||
|
|
@ -109,9 +110,9 @@ class FlowWindow {
|
|||
this.element.style.height = `${config.height ?? 200}px`
|
||||
|
||||
this.header = document.createElement('window-header')
|
||||
this.header.innerHTML = `<img src="${sanitize(config.icon)}"></img> <div class="title">${sanitize(config.title)}</div><div style="flex:1;"></div><i id="min" class='material-symbols-rounded' style="margin-bottom: 5px;">minimize</i><i id="close" class='material-symbols-rounded'>close</i>`
|
||||
this.header.innerHTML = `<img src="${sanitize(config.icon === '' ? nullIcon : config.icon)}"></img> <div class="title">${sanitize(config.title)}</div><div style="flex:1;"></div><i id="min" class='material-symbols-rounded' style="margin-bottom: 5px;">minimize</i><i id="close" class='material-symbols-rounded'>close</i>`
|
||||
if (config.canResize) {
|
||||
this.header.innerHTML = `<img src="${sanitize(config.icon)}"></img> <div class="title">${sanitize(config.title)}</div><div style="flex:1;"></div><i id="min" class='material-symbols-rounded' style="margin-bottom: 5px;">minimize</i><i id="max" class='material-symbols-rounded' style="font-size: 20px;">square</i><i id="close" class='material-symbols-rounded'>close</i>`
|
||||
this.header.innerHTML = `<img src="${sanitize(config.icon === '' ? nullIcon : config.icon)}"></img> <div class="title">${sanitize(config.title)}</div><div style="flex:1;"></div><i id="min" class='material-symbols-rounded' style="margin-bottom: 5px;">minimize</i><i id="max" class='material-symbols-rounded' style="font-size: 20px;">square</i><i id="close" class='material-symbols-rounded'>close</i>`
|
||||
}
|
||||
|
||||
(this.header.querySelector('#close') as HTMLElement).onclick = () => {
|
||||
|
|
|
|||
11
src/types.ts
|
|
@ -30,6 +30,11 @@ export interface PluginMeta extends BaseMeta {
|
|||
icon?: string
|
||||
}
|
||||
|
||||
export interface RepoAppMeta extends BaseMeta {
|
||||
icon?: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface Apps {
|
||||
[key: string]: App
|
||||
}
|
||||
|
|
@ -79,3 +84,9 @@ export interface FlowConfig {
|
|||
USERNAME: string
|
||||
'24HOUR_CLOCK': boolean
|
||||
}
|
||||
|
||||
export interface RepoData {
|
||||
name: string
|
||||
id: string
|
||||
apps: RepoAppMeta[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
||||
import dynamicImport from 'vite-plugin-dynamic-import'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
nodePolyfills(),
|
||||
dynamicImport()
|
||||
nodePolyfills()
|
||||
]
|
||||
})
|
||||
|
|
|
|||