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 {
|
window-area {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: calc(100% - 20px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 10px;
|
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 { App } from '../../types'
|
||||||
|
|
||||||
import FlowWindow from '../../structures/FlowWindow'
|
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 { App } from '../../types'
|
||||||
|
|
||||||
import { fullEditor } from 'prism-code-editor/setups'
|
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 { App } from '../../types'
|
||||||
|
|
||||||
import FlowWindow from '../../structures/FlowWindow'
|
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 badge from '../../assets/badge.png'
|
||||||
import { App, PackageJSON } from '../../types'
|
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 { App, LoadedApp, LoadedPlugin } from '../../types'
|
||||||
import FlowWindow from '../../structures/FlowWindow'
|
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 {
|
export default class ManagerApp implements App {
|
||||||
meta = {
|
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 { App } from '../../types'
|
||||||
import FlowWindow from '../../structures/FlowWindow'
|
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 { App } from '../../types'
|
||||||
import FlowWindow from '../../structures/FlowWindow'
|
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 { LoadedApp, LoadedPlugin } from '../types'
|
||||||
|
import nullIcon from '../assets/icons/application-default-icon.svg'
|
||||||
|
|
||||||
class Flow {
|
class Flow {
|
||||||
apps: LoadedApp[] = []
|
apps: LoadedApp[] = []
|
||||||
appList: string[] = [
|
appList: string[] = [
|
||||||
'settings',
|
'../builtin/apps/settings.ts',
|
||||||
'music',
|
'../builtin/apps/music.ts',
|
||||||
'files',
|
'../builtin/apps/files.ts',
|
||||||
'editor',
|
'../builtin/apps/editor.ts',
|
||||||
'info',
|
'../builtin/apps/info.ts',
|
||||||
'manager',
|
'../builtin/apps/manager.ts',
|
||||||
'browser'
|
'../builtin/apps/browser.ts',
|
||||||
|
'../builtin/apps/store.ts'
|
||||||
]
|
]
|
||||||
|
|
||||||
plugins: LoadedPlugin[] = []
|
plugins: LoadedPlugin[] = []
|
||||||
|
|
@ -22,15 +24,22 @@ class Flow {
|
||||||
window.preloader.setPending('apps')
|
window.preloader.setPending('apps')
|
||||||
window.preloader.setStatus('importing default 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) {
|
for (const appPath of this.appList) {
|
||||||
window.preloader.setStatus(`importing default apps\n${appPath}`)
|
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)
|
console.error(e)
|
||||||
await window.preloader.setError('apps')
|
await window.preloader.setError('apps')
|
||||||
window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`)
|
window.preloader.setStatus(`unable to import ${appPath}\n${e.name}: ${e.message}`)
|
||||||
})
|
})
|
||||||
const app = new ImportedApp()
|
const app = new ImportedApp()
|
||||||
app.builtin = true
|
app.builtin = true
|
||||||
|
app.meta.icon = app.meta.icon ?? nullIcon
|
||||||
|
|
||||||
this.addApp(app)
|
this.addApp(app)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { v4 as uuid } from 'uuid'
|
||||||
import WindowManager from '../instances/WindowManager'
|
import WindowManager from '../instances/WindowManager'
|
||||||
import { FlowWindowConfig } from '../types'
|
import { FlowWindowConfig } from '../types'
|
||||||
import { sanitize } from '../utils'
|
import { sanitize } from '../utils'
|
||||||
|
import nullIcon from '../assets/icons/application-default-icon.svg'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes an element draggable.
|
* Makes an element draggable.
|
||||||
|
|
@ -109,9 +110,9 @@ class FlowWindow {
|
||||||
this.element.style.height = `${config.height ?? 200}px`
|
this.element.style.height = `${config.height ?? 200}px`
|
||||||
|
|
||||||
this.header = document.createElement('window-header')
|
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) {
|
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 = () => {
|
(this.header.querySelector('#close') as HTMLElement).onclick = () => {
|
||||||
|
|
|
||||||
11
src/types.ts
|
|
@ -30,6 +30,11 @@ export interface PluginMeta extends BaseMeta {
|
||||||
icon?: string
|
icon?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RepoAppMeta extends BaseMeta {
|
||||||
|
icon?: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface Apps {
|
export interface Apps {
|
||||||
[key: string]: App
|
[key: string]: App
|
||||||
}
|
}
|
||||||
|
|
@ -79,3 +84,9 @@ export interface FlowConfig {
|
||||||
USERNAME: string
|
USERNAME: string
|
||||||
'24HOUR_CLOCK': boolean
|
'24HOUR_CLOCK': boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RepoData {
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
apps: RepoAppMeta[]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
||||||
import dynamicImport from 'vite-plugin-dynamic-import'
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
nodePolyfills(),
|
nodePolyfills()
|
||||||
dynamicImport()
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
||||||