Compare commits

...

19 Commits

10
.gitignore vendored

@ -122,12 +122,4 @@ dist
/bin
/dist
/build
# Neutralinojs client (minified)
neutralino.js
# Neutralinojs related files
.storage
*.log
.tmp/*
/out

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Neutralinojs and contributors
Copyright (c) 2022 Björn Hase <me@herr-hase.wtf>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

@ -1,12 +1,19 @@
# Potato launcher
I know there a many Launcher on the Market, and they are all damn Beautiful, this
App is not Beautiful, it's only starts App, it is really simple, no fancy dancy Functions.
There are many Launchers for Desktops and they are all fancy. But this one
is damn simple, no fancy dancy functions only add Apps from your file
system or use commands. Each entry can have a description, tags and also an
image. Easy Filtering and Sort by name or last Time you started a App.
And it is not finish!
This Launcher used [Electron](https://www.electronjs.org/), [Riot.js](https://riot.js.org/), [Plain UI](https://plain-ui.com) and [Pouchdb](https://pouchdb.com/).
## Why Electron?
Electron was not the first choice, but it works, and with [Electron Forge](https://www.electronforge.io/) there many possibilities
for deploying. But Electron is also known for his need of resources. But there is a Branch for [neutralino.js](https://neutralino.js.org/),
there will be another attempt to use neutralino.js, because it is more lightweight.
## Next Goals
* Change Format of Images
* Adding Function to Filter Results
* Keyboard- and Controller-Control
* Keyboard- and Controller-Support for navigation
* Kiosk-Mode

@ -1,81 +0,0 @@
import minimist from 'minimist'
import websocket from 'websocket'
import { v4 as uuidv4 } from 'uuid'
/**
* connector creating client and routing to handlers
*
* @author Björn Hase
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
*/
class Connector {
/**
* getting
*
*
* @param {object} handlers
*
*/
constructor(handlers) {
this.handlers = handlers
// getting arguments
const argv = minimist(process.argv.slice(2))
this.NL_PORT = argv['nl-port']
this.NL_TOKEN = argv['nl-token']
this.NL_EXTID = argv['nl-extension-id']
// create socket connection for receive and send events
this.client = new websocket.w3cwebsocket(`ws://localhost:${this.NL_PORT}?extensionId=${this.NL_EXTID}`)
// add events for on message to
this.client.onmessage = (event) => {
this.handleMessage(event)
}
}
/**
* send to app
*
* @param {string} eventName
* @param {array} data
*
*/
send(eventName, data) {
this.client.send(JSON.stringify({
id: uuidv4(),
method: 'app.broadcast',
accessToken: this.NL_TOKEN,
data: {
event: eventName,
data: data
}
}))
}
/**
* handle messages from app
*
* @param {object} event
*
*/
handleMessage(event) {
if (typeof event.data === 'string') {
// parse message
const message = JSON.parse(event.data)
// if event is in handlers-object mapped call it
if (this.handlers.hasOwnProperty(message.event)) {
this.handlers[message.event].call(null, this, message.data)
}
}
}
}
export default Connector

@ -1,81 +0,0 @@
import AppsStore from './../stores/apps.js'
/**
* handler for apps
*
* @author Björn Hase
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
*/
class AppsHandler {
/**
* use create in appStore and send event with result to app
*
* @param {object} data
* @return {object}
*
*/
create(connector, data) {
const appsStore = new AppsStore()
appsStore.create(data)
.then((data) => {
connector.send('pouchdb.apps.success', data)
})
}
/**
* use update in appStore and send event with result to app
*
* @param {object} data
* @return {object}
*
*/
update(connector, data) {
const appsStore = new AppsStore()
appsStore.update(data)
.then((data) => {
connector.send('pouchdb.apps.success', data)
})
}
/**
* use update in appStore and send event with result to app
*
* @param {object} data
* @return {object}
*
*/
findOne(connector, data) {
const appsStore = new AppsStore()
appsStore.findOneById(data.id)
.then((data) => {
if (data) {
connector.send('pouchdb.apps.readyOne', data)
}
})
}
/**
* use find in appStore and send event with result to app
*
* @param {object} connector
* @param {object} data
*
*/
find(connector, data) {
const appsStore = new AppsStore()
appsStore.find()
.then((data) => {
console.error('send result', data)
connector.send('pouchdb.apps.ready', data)
})
}
}
export default AppsHandler

@ -1,30 +0,0 @@
import Connector from './connector.js'
import AppsStore from './stores/apps.js'
import AppsHandler from './handlers/apps.js'
// create handlers
const appsHandler = new AppsHandler
// create connector and add handlers for events
const connector = new Connector({
'pouchdb.apps.create': appsHandler.create,
'pouchdb.apps.update': appsHandler.update,
'pouchdb.apps.findOne': appsHandler.findOne,
'pouchdb.apps.find': appsHandler.find
})
//
connector.client.onerror = function() {
}
//
connector.client.onopen = function() {
}
//
connector.client.onclose = function() {
process.exit()
}

@ -1,702 +0,0 @@
{
"name": "extension-pouchdb",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"abstract-leveldown": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
"integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==",
"requires": {
"buffer": "^5.5.0",
"immediate": "^3.2.3",
"level-concat-iterator": "~2.0.0",
"level-supports": "~1.0.0",
"xtend": "~4.0.0"
}
},
"argsarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz",
"integrity": "sha512-u96dg2GcAKtpTrBdDoFIM7PjcBA+6rSP0OR94MOReNRyUECL6MtQt5XXmRr4qrftYaef9+l5hcpO5te7sML1Cg=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"bufferutil": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz",
"integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==",
"requires": {
"node-gyp-build": "^4.3.0"
}
},
"clone-buffer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
"integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g=="
},
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"d": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
"requires": {
"es5-ext": "^0.10.50",
"type": "^1.0.1"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"deferred-leveldown": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz",
"integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==",
"requires": {
"abstract-leveldown": "~6.2.1",
"inherits": "^2.0.3"
}
},
"double-ended-queue": {
"version": "2.1.0-0",
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
"integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ=="
},
"encoding-down": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz",
"integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==",
"requires": {
"abstract-leveldown": "^6.2.1",
"inherits": "^2.0.3",
"level-codec": "^9.0.0",
"level-errors": "^2.0.0"
}
},
"end-stream": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/end-stream/-/end-stream-0.1.0.tgz",
"integrity": "sha512-Brl10T8kYnc75IepKizW6Y9liyW8ikz1B7n/xoHrJxoVSSjoqPn30sb7XVFfQERK4QfUMYRGs9dhWwtt2eu6uA==",
"requires": {
"write-stream": "~0.4.3"
}
},
"errno": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"requires": {
"prr": "~1.0.1"
}
},
"es5-ext": {
"version": "0.10.62",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
"requires": {
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.3",
"next-tick": "^1.1.0"
}
},
"es6-iterator": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
"requires": {
"d": "1",
"es5-ext": "^0.10.35",
"es6-symbol": "^3.1.1"
}
},
"es6-symbol": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
"requires": {
"d": "^1.0.1",
"ext": "^1.1.2"
}
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"ext": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
"integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
"requires": {
"type": "^2.5.0"
},
"dependencies": {
"type": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
}
}
},
"fetch-cookie": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz",
"integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==",
"requires": {
"tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"immediate": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q=="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
},
"level": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz",
"integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==",
"requires": {
"level-js": "^5.0.0",
"level-packager": "^5.1.0",
"leveldown": "^5.4.0"
}
},
"level-codec": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz",
"integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==",
"requires": {
"buffer": "^5.6.0"
}
},
"level-concat-iterator": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz",
"integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw=="
},
"level-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz",
"integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==",
"requires": {
"errno": "~0.1.1"
}
},
"level-iterator-stream": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz",
"integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==",
"requires": {
"inherits": "^2.0.4",
"readable-stream": "^3.4.0",
"xtend": "^4.0.2"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"level-js": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz",
"integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==",
"requires": {
"abstract-leveldown": "~6.2.3",
"buffer": "^5.5.0",
"inherits": "^2.0.3",
"ltgt": "^2.1.2"
}
},
"level-packager": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz",
"integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==",
"requires": {
"encoding-down": "^6.3.0",
"levelup": "^4.3.2"
}
},
"level-supports": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz",
"integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==",
"requires": {
"xtend": "^4.0.2"
}
},
"level-write-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/level-write-stream/-/level-write-stream-1.0.0.tgz",
"integrity": "sha512-bBNKOEOMl8msO+uIM9YX/gUO6ckokZ/4pCwTm/lwvs46x6Xs8Zy0sn3Vh37eDqse4mhy4fOMIb/JsSM2nyQFtw==",
"requires": {
"end-stream": "~0.1.0"
}
},
"leveldown": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz",
"integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==",
"requires": {
"abstract-leveldown": "~6.2.1",
"napi-macros": "~2.0.0",
"node-gyp-build": "~4.1.0"
},
"dependencies": {
"node-gyp-build": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz",
"integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ=="
}
}
},
"levelup": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz",
"integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==",
"requires": {
"deferred-leveldown": "~5.3.0",
"level-errors": "~2.0.0",
"level-iterator-stream": "~4.0.0",
"level-supports": "~1.0.0",
"xtend": "~4.0.0"
}
},
"ltgt": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
"integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA=="
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"napi-macros": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
"integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg=="
},
"next-tick": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"node-gyp-build": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
"integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg=="
},
"pouchdb": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb/-/pouchdb-7.3.0.tgz",
"integrity": "sha512-OwsIQGXsfx3TrU1pLruj6PGSwFH+h5k4hGNxFkZ76Um7/ZI8F5TzUHFrpldVVIhfXYi2vP31q0q7ot1FSLFYOw==",
"requires": {
"abort-controller": "3.0.0",
"argsarray": "0.0.1",
"buffer-from": "1.1.2",
"clone-buffer": "1.0.0",
"double-ended-queue": "2.1.0-0",
"fetch-cookie": "0.11.0",
"immediate": "3.3.0",
"inherits": "2.0.4",
"level": "6.0.1",
"level-codec": "9.0.2",
"level-write-stream": "1.0.0",
"leveldown": "5.6.0",
"levelup": "4.4.0",
"ltgt": "2.2.1",
"node-fetch": "2.6.7",
"readable-stream": "1.1.14",
"spark-md5": "3.0.2",
"through2": "3.0.2",
"uuid": "8.3.2",
"vuvuzela": "1.0.3"
}
},
"pouchdb-abstract-mapreduce": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.3.0.tgz",
"integrity": "sha512-+2fVt3SDh7D776lIGbYZOsKX5js1aUyUw7iJaTGitxSdQ2ObWSTrr3SUrj5Qo1CkgPXwRM3Tdoq/53JYAa2qCA==",
"requires": {
"pouchdb-binary-utils": "7.3.0",
"pouchdb-collate": "7.3.0",
"pouchdb-collections": "7.3.0",
"pouchdb-errors": "7.3.0",
"pouchdb-fetch": "7.3.0",
"pouchdb-mapreduce-utils": "7.3.0",
"pouchdb-md5": "7.3.0",
"pouchdb-utils": "7.3.0"
}
},
"pouchdb-binary-utils": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.0.tgz",
"integrity": "sha512-xvBH/XGHGcou2vkEzszJxkCc7YElfRUrkLUg51Jbdmh1mogLDUO0bU3Tj6TOIIJfRkQrU/HV+dDkMAhsil0amQ==",
"requires": {
"buffer-from": "1.1.2"
}
},
"pouchdb-collate": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-collate/-/pouchdb-collate-7.3.0.tgz",
"integrity": "sha512-ys7rXKtEr6cfghgUjknwFJiOkITebV6JmeTybJKCzMV0r2luXu0OoPQsKVpE/wbM/3F5LxfpbFKGFpPcfGMvTA=="
},
"pouchdb-collections": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.0.tgz",
"integrity": "sha512-Xr54m2+fErShXn+qAT4xwqJ+8NwddNPeTMJT4z4k1sZsrwfHmZsWbsKAyGPMF04eQaaU+7DDRMciu2VzaBUXyg=="
},
"pouchdb-errors": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.0.tgz",
"integrity": "sha512-dTBbIC1BbCy6J9W/Csg5xROgb3wJN3HpbgAJHHSEtAkb8oA45KZmU3ZwEpNhf0AfPuQm4XgW1936PvlDlGgJiw==",
"requires": {
"inherits": "2.0.4"
}
},
"pouchdb-fetch": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-fetch/-/pouchdb-fetch-7.3.0.tgz",
"integrity": "sha512-8/lcg8iMDG+GVs1dHNXA4ktJSEpH71dHU3xesMJ25tNQOqfAaaWrkfz9j71ZYDDkveLYE6UjUzl/sDacu2hSjw==",
"requires": {
"abort-controller": "3.0.0",
"fetch-cookie": "0.11.0",
"node-fetch": "2.6.7"
}
},
"pouchdb-find": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-find/-/pouchdb-find-7.3.0.tgz",
"integrity": "sha512-EwhnfyxCAkKf8PG4tfndTTygEmtuz+o1LiZkxfPrflfXA3m1jo1ithib0hwBYtEwEYWuZxH6B8pRZutbLoQCGA==",
"requires": {
"pouchdb-abstract-mapreduce": "7.3.0",
"pouchdb-collate": "7.3.0",
"pouchdb-errors": "7.3.0",
"pouchdb-fetch": "7.3.0",
"pouchdb-md5": "7.3.0",
"pouchdb-selector-core": "7.3.0",
"pouchdb-utils": "7.3.0"
}
},
"pouchdb-mapreduce-utils": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-7.3.0.tgz",
"integrity": "sha512-KDVSd+H2r+XWTrQfKWV71SknDDYRjYXoeWs0ZQl3xITHCcTl+fIgqyagg/XN+Zy/U9LeLPGMe2JdgPx9H8lJgw==",
"requires": {
"argsarray": "0.0.1",
"inherits": "2.0.4",
"pouchdb-collections": "7.3.0",
"pouchdb-utils": "7.3.0"
}
},
"pouchdb-md5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.0.tgz",
"integrity": "sha512-wL04QgoKyd/L/TV5gxgcvlEyCJiZoXCOEFJklTzkdza/kBQNJGPH7i0ZhKa7Sb+AvZYoWZHddf1Zgv7rBScHkA==",
"requires": {
"pouchdb-binary-utils": "7.3.0",
"spark-md5": "3.0.2"
}
},
"pouchdb-selector-core": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-selector-core/-/pouchdb-selector-core-7.3.0.tgz",
"integrity": "sha512-sK/cCrIGeL9ImcMhKGcwa54+bzX7Wv4hhVV+oUW3T1Nasaoxh+Muem1GuA+x1+SbTCE8y37rUg8i6DIOhX51ew==",
"requires": {
"pouchdb-collate": "7.3.0",
"pouchdb-utils": "7.3.0"
}
},
"pouchdb-utils": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.0.tgz",
"integrity": "sha512-HH+5IXXWn/ZgVCSnrlydBMYn6MabT7RS7SNoo9w8qVH9efpZSp3eLchw6yMQNLw8LQefWmbbskiHV9VgJmSVWQ==",
"requires": {
"argsarray": "0.0.1",
"clone-buffer": "1.0.0",
"immediate": "3.3.0",
"inherits": "2.0.4",
"pouchdb-collections": "7.3.0",
"pouchdb-errors": "7.3.0",
"pouchdb-md5": "7.3.0",
"uuid": "8.3.2"
}
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
},
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
},
"dependencies": {
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
}
}
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"spark-md5": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz",
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"requires": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"tough-cookie": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
"requires": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
}
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"type": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
},
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"requires": {
"is-typedarray": "^1.0.0"
}
},
"universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="
},
"url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"utf-8-validate": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz",
"integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==",
"requires": {
"node-gyp-build": "^4.3.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"vuvuzela": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/vuvuzela/-/vuvuzela-1.0.3.tgz",
"integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"websocket": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
"requires": {
"bufferutil": "^4.0.1",
"debug": "^2.2.0",
"es5-ext": "^0.10.50",
"typedarray-to-buffer": "^3.1.5",
"utf-8-validate": "^5.0.2",
"yaeti": "^0.0.6"
}
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"write-stream": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/write-stream/-/write-stream-0.4.3.tgz",
"integrity": "sha512-IJrvkhbAnj89W/GAVdVgbnPiVw5Ntg/B4tc/MUCIEwj/g6JIww1DWJyB/yBMT3yw2/TkT6IUZ0+IYef3flEw8A==",
"requires": {
"readable-stream": "~0.0.2"
},
"dependencies": {
"readable-stream": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-0.0.4.tgz",
"integrity": "sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw=="
}
}
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"yaeti": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug=="
}
}
}

@ -1,14 +0,0 @@
{
"private": true,
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "module",
"dependencies": {
"minimist": "^1.2.6",
"pouchdb": "^7.3.0",
"pouchdb-find": "^7.3.0",
"uuid": "^8.3.2",
"websocket": "^1.0.34"
}
}

@ -1,125 +0,0 @@
import PouchdbHandler from './pouchdbHandler.js'
/**
* apps
*
* @author Björn Hase
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/tellme-bot.git
*
*/
class AppsStore extends PouchdbHandler {
constructor() {
super()
// add index for apps
this.createIndex([
'name',
'description',
'tags'
])
}
/**
*
* @param {object} data
* @return {object}
*
*/
create(data) {
return this.db.post(data)
.then((response) => {
return this.findOneById(response._id)
}).catch((error) => {
console.error(error)
})
}
/**
*
* @param {object} data
* @return {object}
*
*/
update(data) {
return this.db.put(data)
.then((response) => {
return this.findOneById(response._id)
}).catch((error) => {
console.error(error)
})
}
/**
* find one app by id
*
* @param {string} id
* @return {mixed}
*
*/
findOneById(id) {
const query = {
'fields': [
'_id',
'_rev',
'name',
'command',
'description',
'thumbnail',
'tags'
],
'selector': {
'_id' : id
}
}
return this.db.find(query).then((documents) => {
if (documents.docs.length === 0) {
return null
} else {
return documents.docs[0]
}
}).catch((error) => {
console.error(error)
})
}
/**
* find apps
*
* @return {mixed}
*
*/
find() {
const query = {
'fields': [
'_id',
'name',
'command',
'description',
'thumbnail',
'tags',
'started_date'
],
'selector': {
'name': {
'$exists': true
}
}
}
return this.db.find(query).then((documents) => {
if (documents.warning) {
console.warning(documents.warning)
}
return documents.docs
}).catch((error) => {
console.error(error)
})
}
}
export default AppsStore

@ -1,126 +0,0 @@
import PouchdbHandler from './pouchdbHandler.js'
/**
* apps
*
* @author Björn Hase
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/tellme-bot.git
*
*/
class TagsDatabase extends PouchdbHandler {
constructor() {
super()
// add index for apps
this.createIndex([
'name'
])
}
/**
*
*
* @param {object} data
*
*/
async create(tags) {
const query = {
'selector': {
'name': {
'$in': tags
}
}, 'fields': [
'name'
]
}
// check for existings tags and remove
// adding tags that are not removed
this.db.find(query).then((documents) => {
if (documents.docs && documents.docs.length > 0) {
documents.docs.forEach(data, () => {
const index = tags.indexOf(data.name)
if (index >= 0) {
tags.splice(index, 1)
}
})
}
if (tags.length > 0) {
tags.forEach((tag) => {
await this.db.post({
'name': tag
})
})
}
})
}
/**
*
*
* @param {object} data
*
*/
async remove(tags) {
const query = {
'selector': {
'name': {
'$in': tags
}
}, 'fields': [
'name'
]
}
// check for existings tags and remove
// adding tags that are not removed
this.db.find(query).then((documents) => {
if (documents.docs && documents.docs.length > 0) {
documents.docs.forEach(data, () => {
const index = tags.indexOf(data.name)
if (index >= 0) {
tags.splice(index, 1)
}
})
}
if (tags.length > 0) {
tags.forEach((tag) => {
await this.db.post({
'name': tag
})
})
}
})
}
/**
* find apps
*
* @return {mixed}
*
*/
find() {
const query = {
'fields': [
'name'
]
}
return this.db.find(query).then((documents) => {
return documents.docs
})
}
}
export default AppsDatabase

@ -0,0 +1,77 @@
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
// add var for window
let appWindow = null
// add events
app.on('ready', handleReady)
app.on('activate', handleActivate)
app.on('window-all-closed', handleAllClosed)
/**
* create window for app
*
*
*/
function handleReady() {
// create window
appWindow = new BrowserWindow({
frame: false,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: false,
contextIsolation: false,
webSecurity: true,
enableRemoteModule: true
}
})
// max size of window
appWindow.maximize()
appWindow.setMenu(null)
// loading
appWindow.loadURL('file://' + __dirname + '/dist/index.html')
// add event if window closed
appWindow.on('closed', handleClose)
if (process.env.debug) {
appWindow.webContents.openDevTools()
}
// adding events for ipcMain
ipcMain.on('getPathUserData', (event) => {
event.returnValue = app.getPath('userData')
})
}
/**
* handle close window
*
*/
function handleClose() {
appWindow = null
}
/**
*
*
*/
function handleAllClosed() {
if (process.platform !== 'darwin') {
app.quit()
}
}
/**
*
*
*/
function handleActivate() {
if (appWindow === null) {
createAppWindow()
}
}

@ -1,18 +1 @@
{
"/build/js/app/spritemap.js": "/build/js/app/spritemap.js",
"/public/symbol-defs.svg": "/public/symbol-defs.svg",
"/build/js/app.js": "/build/js/app.js",
"/build/css/styles.css": "/build/css/styles.css",
"/build/js/neutralino.js": "/build/js/neutralino.js",
"/build/css/demo.html": "/build/css/demo.html",
"/build/css/IBMPlexMono-Bold.eot": "/build/css/IBMPlexMono-Bold.eot",
"/build/css/IBMPlexMono-Bold.ttf": "/build/css/IBMPlexMono-Bold.ttf",
"/build/css/IBMPlexMono-Bold.woff": "/build/css/IBMPlexMono-Bold.woff",
"/build/css/IBMPlexMono-Bold.woff2": "/build/css/IBMPlexMono-Bold.woff2",
"/build/css/IBMPlexMono.eot": "/build/css/IBMPlexMono.eot",
"/build/css/IBMPlexMono.ttf": "/build/css/IBMPlexMono.ttf",
"/build/css/IBMPlexMono.woff": "/build/css/IBMPlexMono.woff",
"/build/css/IBMPlexMono.woff2": "/build/css/IBMPlexMono.woff2",
"/build/css/OFL.txt": "/build/css/OFL.txt",
"/build/css/stylesheet.css": "/build/css/stylesheet.css"
}
{}

@ -1,53 +0,0 @@
{
"applicationId": "js.neutralino.potato-launcher",
"version": "1.0.0",
"defaultMode": "window",
"port": 0,
"documentRoot": "/build/",
"url": "/",
"enableServer": true,
"enableNativeAPI": true,
"enableExtensions": true,
"tokenSecurity": "one-time",
"logging": {
"enabled": true,
"writeToLogFile": true
},
"nativeAllowList": [
"app.*",
"filesystem.*",
"os.*",
"extensions.*",
"computer.*",
"storage.*",
"debug.log"
],
"modes": {
"window": {
"title": "Potato Launcher",
"fullScreen": false,
"alwaysOnTop": false,
"icon": "/resources/icons/appIcon.png",
"enableInspector": false,
"borderless": true,
"maximize": true,
"hidden": false,
"resizable": true,
"exitProcessOnClose": true
}
},
"cli": {
"binaryName": "potato-launcher",
"resourcesPath": "/build/",
"extensionsPath": "/extensions/",
"clientLibrary": "/resources/js/neutralino.js",
"binaryVersion": "4.7.0",
"clientVersion": "3.6.0"
},
"extensions": [
{
"id": "js.neutralino.pouchdb",
"command": "node ${NL_PATH}/extensions/pouchdb/index.js"
}
]
}

15845
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,21 +1,54 @@
{
"name": "shiny-dashboard",
"version": "0.1.0",
"name": "potato-launcher",
"version": "0.2.0",
"author": "Björn Hase <me@herr-hase.wtf>",
"homepage": "https://herr-hase.wtf",
"main": "index.js",
"description": "Launcher for Apps on Desktop",
"build": {
"files": [
"dist/**/*",
"package.json",
"index.js"
],
"linux": {
"target": [
"AppImage",
"deb"
],
"icon": "resources/logo.png",
"category": "Utility"
},
"nodeGypRebuild": false,
"npmRebuild": false,
"asar": true
},
"scripts": {
"build": "npx mix"
"development": "export debug=true && npx mix && electron .",
"production": "npx mix --production && electron .",
"build-linux": "npx mix --production && npm exec --package=electron-builder -- electron-builder build --linux"
},
"dependencies": {
"@riotjs/observable": "^4.1.1",
"@tiny-components/confirm": "^0.1.1",
"@tiny-components/notification": "^0.1.0",
"@tiny-components/plain-ui": "^0.6.0",
"@tiny-components/sidebar-form": "^0.1.0",
"@tiny-components/validator": "^0.2.0",
"dayjs": "^1.11.5",
"identicon": "^3.1.1",
"md5": "^2.3.0",
"mime-types": "^2.1.35",
"mkdirp": "^1.0.4",
"pouchdb": "^7.3.0",
"pouchdb-find": "^7.3.0",
"riot": "^7.0.3"
"riot": "^7.0.3",
"uuid": "^9.0.0"
},
"devDependencies": {
"@riotjs/webpack-loader": "^6.0.0",
"electron": "^21.2.0",
"electron-builder": "^23.6.0",
"laravel-mix": "^6.0.49",
"sass": "^1.54.5",
"sass-loader": "^12.6.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 965 B

@ -4,18 +4,16 @@
<meta charset="UTF-8">
<title>Potato Launcher</title>
<link rel="stylesheet" href="css/styles.css">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; img-src data: 'self';">
</head>
<body>
<div class="potato-main">
<div class="container-full p-top-4">
<potato-apps-view></potato-apps-view>
<tiny-notification></tiny-notification>
<tiny-confirm></tiny-confirm>
</div>
</div>
<script src="js/neutralino.js"></script>
<!-- -->
<script src="js/app.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

@ -0,0 +1,16 @@
<svg width="72px" height="72px" viewBox="0 0 72 72" id="emoji" xmlns="http://www.w3.org/2000/svg">
<g id="color">
<path fill="#A57939" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M59.0417,27.8733c0,18.2898-7.5955,27.2079-14.6746,31.5411c-6.1848,3.8041-11.9917,4.1064-11.9917,4.1064 c-10.8076,0-19.5746-8.767-19.5746-19.5746c0-6.6509,3.7337-11.4301,8.3765-16.0729C33.2467,15.804,31.9388,9.5079,42.8303,9.5079 c8.956,0,16.2114,7.2681,16.2114,16.224C59.0417,26.5381,59.0417,27.8733,59.0417,27.8733z"/>
<path fill="#a57939" stroke="none" d="M59.0449,25.73c0,0.81,0,2.14,0,2.14c0,18.29-7.6,27.21-14.68,31.54 c-6.1801,3.81-11.9901,4.11-11.9901,4.11c-1.67,0-3.28-0.21-4.8199-0.6c31.56-8.62,25.35-49.89,25.35-49.89 C56.6449,16,59.0449,20.59,59.0449,25.73z"/>
<circle cx="38.2737" cy="51.6496" r="3.2408" fill="#A57939" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
</g>
<g id="hair"/>
<g id="skin"/>
<g id="skin-shadow"/>
<g id="line">
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M59.0417,27.8733c0,18.2898-7.5955,27.2079-14.6746,31.5411c-6.1848,3.8041-11.9917,4.1064-11.9917,4.1064 c-10.8076,0-19.5746-8.767-19.5746-19.5746c0-6.6509,3.7337-11.4301,8.3765-16.0729C33.2467,15.804,31.9388,9.5079,42.8303,9.5079 c8.956,0,16.2114,7.2681,16.2114,16.224C59.0417,26.5381,59.0417,27.8733,59.0417,27.8733z"/>
<circle cx="39.534" cy="16.2145" r="1.2603" fill="#000000" stroke="none"/>
<circle cx="38.2737" cy="51.6496" r="3.2408" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M59.0417,27.8733c0,18.2898-7.5955,27.2079-14.6746,31.5411c-6.1848,3.8041-11.9917,4.1064-11.9917,4.1064 c-10.8076,0-19.5746-8.767-19.5746-19.5746c0-6.6509,3.7337-11.4301,8.3765-16.0729C33.2467,15.804,31.9388,9.5079,42.8303,9.5079 c8.956,0,16.2114,7.2681,16.2114,16.224C59.0417,26.5381,59.0417,27.8733,59.0417,27.8733z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -4,6 +4,7 @@ import TinyLoading from '@tiny-components/loading/src/loading.riot'
import TinySidebarFormHeader from './forms/header.riot'
import TinySidebarFormFooter from './forms/footer.riot'
import TinyNotification from '@tiny-components/notification/src/notification.riot'
import TinyConfirm from '@tiny-components/confirm/src/confirm.riot'
import FieldError from '@tiny-components/validator/src/fieldError.riot'
@ -18,54 +19,12 @@ riot.register('tiny-loading', TinyLoading)
riot.register('tiny-sidebar-form-header', TinySidebarFormHeader)
riot.register('tiny-sidebar-form-footer', TinySidebarFormFooter)
riot.register('tiny-notification', TinyNotification)
riot.register('tiny-confirm', TinyConfirm)
riot.register('potato-field-tags', potatoFieldTags)
riot.register('potato-apps-view', potatoAppsView)
riot.register('potato-close-button', potatoCloseButton)
// adding events for Neutralino
Neutralino.events.on('ready', async () => {
// @TODO thats mad, it is only a workaround, don't judge me, i will do better
// its no possible to create a hole path, solving this with array and a recursive function
try {
const result = await Neutralino.filesystem.getStats('./.storage')
if (result) {
try {
await Neutralino.filesystem.createDirectory('./.storage/pouchdb')
} catch(error) {
}
}
} catch(error) {
if (error.code === 'NE_FS_NOPATHE') {
try {
await Neutralino.filesystem.createDirectory('./.storage')
await Neutralino.filesystem.createDirectory('./.storage/pouchdb')
} catch(error) {
}
}
}
riot.mount('potato-apps-view')
riot.mount('tiny-notification')
})
// let it rain
Neutralino.init()
// workaround to prevent a stack overflow
window._arrayBufferToBase64 = function _arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
riot.mount('potato-apps-view')
riot.mount('tiny-notification')
riot.mount('tiny-confirm')

@ -18,7 +18,6 @@
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
*/
export default {
/**

@ -1,6 +1,6 @@
<potato-close-button>
<button class="button button--icon button--danger button--hover-icon-contrast m-left-4 m-bottom-0" onclick={ (event) => { handleClose(event) } }>
<svg class="icon icon--big" aria-hidden="true">
<button class="button button--full button--icon button--danger button--hover-icon-text m-bottom-0" onclick={ (event) => { handleClose(event) } }>
<svg class="icon icon--big fill-text-contrast" aria-hidden="true">
<use xlink:href="symbol-defs.svg#icon-log-out"></use>
</svg>
</button>
@ -18,7 +18,7 @@
export default {
handleClose(event) {
Neutralino.app.exit()
window.close()
}
}

@ -2,7 +2,7 @@
<div class="field-tags">
<select name="{ state.options.name }[]" multiple style="display: none;">
<option each={ tag in state.tags } value={ tag }></option>
<option each={ tag in state.tags } value={ tag } selected></option>
</select>
<label class="field-label">
@ -30,6 +30,7 @@
</li>
</ul>
</div>
</div>
<script>
@ -74,7 +75,7 @@
onMounted() {
tagsStore.on('update', (data) => {
// if no data is send, reset array
// if no data is send, reset array
if (!data) {
data = []
}
@ -141,10 +142,8 @@
for (let i = 0; i < this.state.tags.length; i++) {
if (value === this.state.tags[i]) {
inArray = true
this.state.errors.push('Already added')
this.update()
break
}
}
@ -152,16 +151,13 @@
// if value is not already in data and longer than zero chars
if (inArray === false && value.length > 0) {
this.state.tags.push(value)
this.update()
// select new option
this.$('select option[value="' + value + '"]').selected = true
this.$('input').value = ''
} else if (value.length === 0) {
this.state.errors.push('Required!')
this.update()
}
this.update()
// adding focus to input
this.$('input').focus();
}

@ -0,0 +1,152 @@
<potato-filter>
<div class="{ getFilterCssClasses('filter') }" if={ this.state.tags.length > 0 }>
<div class="display-flex">
<button class="button button--open button--icon button--hover-icon-contrast m-bottom-0" onclick={ (event) => { handleOpen(event) }}>
<svg class="icon icon--big">
<use xlink:href="symbol-defs.svg#icon-filter" />
</svg>
</button>
<button class="button button--close button--icon button--hover-icon-contrast m-bottom-0" onclick={ (event) => { handleOpen(event) }}>
<svg class="icon icon--big">
<use xlink:href="symbol-defs.svg#icon-close" />
</svg>
</button>
<div class="filter__selected m-left-3" if={ state.tags.length > 0 }>
<button if={ tag.selected === true } class="button m-bottom-0 m-right-3" each={ (tag, index) in state.tags } onclick={ (event) => { handleTagToggle(event, index) }}>
{ tag.name }
<svg class="icon">
<use xlink:href="symbol-defs.svg#icon-close" />
</svg>
</button>
</div>
</div>
<div class="filter__tags">
<button class="button" each={ (tag, index) in state.tags } onclick={ (event) => { handleTagToggle(event, index) }}>
{ tag.name }
<svg class="{ getTagCssClasses('icon', tag, true) }">
<use xlink:href="symbol-defs.svg#icon-checkbox" />
</svg>
<svg class="{ getTagCssClasses('icon', tag) }">
<use xlink:href="symbol-defs.svg#icon-checkbox-checked" />
</svg>
</button>
</div>
</div>
<script>
import tagsStore from './../stores/tags.js'
import appsStore from './../stores/apps.js'
/**
* filtering apps by tags, showing all available tags as list of buttons
* whith a checkbox icon, after select or deselect update apps
*
* @author Björn Hase
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
*/
export default {
state: {
tags: [],
selected: [],
isOpen: false
},
/**
*
*
*/
onMounted() {
tagsStore.on('ready', (data) => {
this.state.tags = data
this.update()
})
tagsStore.get()
},
/**
* get css for filter
*
* @param {string} defaultCssClass
* @return {string}
*/
getFilterCssClasses(defaultCssClass) {
const classes = [
defaultCssClass
]
if (this.state.isOpen === true) {
classes.push('filter--open')
}
return classes.join(' ')
},
/**
* get css for tag
*
* @param {string} defaultCssClass
* @param {object} tag
* @param {mixed} parameter
* @return {string}
*/
getTagCssClasses(defaultCssClass, tag, parameter = undefined) {
const classes = [
defaultCssClass
]
if (tag.selected === parameter) {
classes.push('hidden')
}
return classes.join(' ')
},
/**
* toggle tag, if selected or not
*
* @param {object} event
* @param {integer} index
*
*/
handleTagToggle(event, index) {
// if selected is undefined on tags with index
if (this.state.tags[index].selected === undefined) {
this.state.tags[index].selected = true
this.state.selected.push(this.state.tags[index].name)
} else {
const selectedIndex = this.state.selected.indexOf(this.state.tags[index].name)
// delete selected from tags
delete this.state.tags[index].selected
// if selctedIndex has value, remove from array
if (selectedIndex !== -1) {
this.state.selected.splice(selectedIndex, 1)
}
}
// update filter for store
appsStore.filter(this.state.selected)
this.update()
},
handleOpen(event) {
if (this.state.isOpen === true) {
this.state.isOpen = false
} else {
this.state.isOpen = true
}
this.update()
}
}
</script>
</potato-filter>

@ -0,0 +1,57 @@
<potato-sorting>
<div class="filter">
<button class="button button--icon button--hover-icon-contrast m-bottom-0" if={ state.column === 'name' } onclick={ (event) => { handleToggle(event) }}>
<svg class="icon icon--big">
<use xlink:href="symbol-defs.svg#icon-text" />
</svg>
</button>
<button class="button button--icon button--hover-icon-contrast m-bottom-0" if={ state.column === 'date_started' } onclick={ (event) => { handleToggle(event) }}>
<svg class="icon icon--big">
<use xlink:href="symbol-defs.svg#icon-clock" />
</svg>
</button>
</div>
<script>
import appsStore from './../stores/apps.js'
/**
* sorting of apps
*
* @author Björn Hase <me@herr-hase.wtf>
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
*/
export default {
state: {
column: 'name'
},
/**
* toggle sorting
*
*
* @param {[type]} event
*
*/
handleToggle(event) {
event.preventDefault()
if (this.state.column === 'name') {
this.state.column = 'date_started'
} else if (this.state.column === 'date_started') {
this.state.column = 'name'
}
appsStore.sort(this.state.column)
this.update()
}
}
</script>
</potato-sorting>

@ -0,0 +1,274 @@
import DatabaseHandler from './databaseHandler.js'
import FileHandler from './fileHandler.js'
import TagsDatabase from './tags.js'
const { ipcRenderer } = require('electron')
/**
* apps
*
* @author Björn Hase
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/tellme-bot.git
*
*/
class AppsDatabase extends DatabaseHandler {
constructor() {
super('apps')
// add index for apps to sort by name
this.createIndex([
'name'
], 'name-sort')
// add index for apps to sort by date_started
this.createIndex([
'date_started'
], 'date-started-sort')
}
/**
* create app
*
* @param {object} data
* @return {object}
*
*/
async create(data) {
// default for date_started
data.date_started = 0
if (data.thumbnail) {
data.thumbnail = await this.createOrUpdateFile(data.thumbnail)
}
return this.db.post(data)
.then((response) => {
// if tags are in data update tags also in database
if (data.tags) {
const tagsDatabase = new TagsDatabase()
tagsDatabase.update(data.tags)
}
return this.findOneById(response._id)
}).catch((error) => {
// @TODO handle errors from pouchdb
console.error(error)
})
}
/**
* create or update file with fileHandler
*
*
* @param {[type]} file
* @param {[type]} entry
* @param {[type]} key
* @return {mixed}
*
*/
async createOrUpdateFile(file, entry, key) {
// fileHandler
const fileHandler = new FileHandler(ipcRenderer.sendSync('getPathUserData') + '/attachments')
const reader = new FileReader()
// if file is blob use readFileFromBlob to get data
if (file instanceof Blob) {
file.data = await fileHandler.readFileFromBlob(file)
if (entry && entry[key]) {
fileHandler.remove(entry[key])
}
file = fileHandler.put(file.name, file.type, file.data)
}
return file
}
/**
* update app
*
* @param {object} data
* @return {object}
*
*/
async update(data) {
const entry = await this.findOneById(data._id)
if (data.thumbnail) {
data.thumbnail = await this.createOrUpdateFile(data.thumbnail, entry, 'thumbnail')
}
return this.db.put(data)
.then(async (response) => {
const tagsDatabase = new TagsDatabase()
// if tags are in data update tags also in database
if (data.tags && data.tags.length > 0) {
tagsDatabase.update(data.tags)
} else {
await tagsDatabase.removeNotNeeded()
}
return this.findOneById(response._id)
}).catch((error) => {
// @TODO handle errors from pouchdb
console.error(error)
})
}
/**
* remove app
*
* @param {object} document
* @return {object}
*
*/
async remove(document) {
const entry = await this.findOneById(document._id)
if (entry.thumbnail) {
const fileHandler = new FileHandler(ipcRenderer.sendSync('getPathUserData') + '/attachments')
fileHandler.remove(entry.thumbnail)
}
return this.db.remove(document)
.then(async (response) => {
// successfull remove document, check for tags that
// not longer used
if (response.ok === true) {
const tagsDatabase = new TagsDatabase()
await tagsDatabase.removeNotNeeded()
}
return response
}).catch((error) => {
// @TODO handle errors from pouchdb
console.error(error)
})
}
/**
* find one app by id
*
* @param {string} id
* @return {mixed}
*
*/
findOneById(id) {
const query = {
'fields': [
'_id',
'_rev',
'name',
'command',
'description',
'thumbnail',
'tags',
'date_started'
],
'selector': {
'_id' : id
}
}
return this.db.find(query).then((documents) => {
if (documents.docs.length === 0) {
return null
} else {
return documents.docs[0]
}
}).catch((error) => {
// @TODO handle errors from pouchdb
console.error(error)
})
}
/**
* find apps
*
* @return {mixed}
*
*/
async find(parameters) {
const query = {
'selector': {
},
'fields': [
'_id',
'_rev',
'name',
'command',
'description',
'thumbnail',
'tags',
'date_started'
]
}
// adding sort from parameters
if (parameters.sort) {
query.sort = []
query.sort.push(parameters.sort)
// if sort is date_stared change 'date-started-sort'
if (parameters.sort === 'date_started') {
query.use_index = '_design/date-started-sort'
query.selector = {
'date_started': {
'$exists': true
}
}
// @TODO refactor this, change logic in filter
query.sort = [{
'date_started': 'desc'
}]
// if sort is name change 'name-sort'
} else {
query.use_index = '_design/name-sort'
query.selector = {
'name': {
'$regex': ''
}
}
}
}
// adding tags from parameters
if (parameters.tags.length > 0) {
query.selector.tags = {
$in: parameters.tags
}
}
return this.db.find(query).then(async (documents) => {
return documents.docs
}).catch((error) => {
// @TODO handle errors from pouchdb
console.error(error)
})
}
}
export default AppsDatabase

@ -10,16 +10,16 @@ import PouchDBfind from 'pouchdb-find'
*
*/
class PouchdbHandler {
class DatabaseHandler {
/**
*
*
*/
constructor() {
constructor(name) {
PouchDB.plugin(PouchDBfind)
this.db = new PouchDB('.storage/pouchdb/apps', {
this.db = new PouchDB(name, {
revs_limit: 0
})
}
@ -30,17 +30,22 @@ class PouchdbHandler {
* @param {array} fields
*
*/
createIndex(fields) {
createIndex(fields, name = null) {
// adding index
try {
this.db.createIndex({
const query = {
index: {
fields: fields
}
}).then(() => {
}
if (name) {
query.index.ddoc = name
}
})
this.db.createIndex(query)
} catch (error) {
console.log(error)
}
@ -48,4 +53,4 @@ class PouchdbHandler {
}
}
export default PouchdbHandler
export default DatabaseHandler

@ -0,0 +1,127 @@
import { v4 as uuidv4 } from 'uuid'
import md5 from 'md5'
import mkdirp from 'mkdirp'
import path from 'path'
import fs from 'fs'
/**
* handling files and create files in a hash-directory system
*
* @author Björn Hase
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
*/
class FileHandler {
/**
*
*
* @param {string} storagePath
*
*/
constructor(storagePath) {
this.storagePath = storagePath
}
/**
* add file to storage
*
* @param {string} filename
* @param {string} type
* @param {mixed} data
* @return {object}
*
*/
put(filename, type, data) {
const uuid = uuidv4()
const relativePath = this._createDirPath(uuid) + '/' + uuid
try {
fs.writeFileSync(this.storagePath + '/' + relativePath, data, { encoding: 'binary' })
} catch(error) {
throw new Error(error)
}
return {
name: filename,
type: type,
path: relativePath
}
}
/**
* remove
*
* @param {object} file
*
*/
remove(file) {
fs.unlinkSync(this.storagePath + '/' + file.path)
}
/**
* read file from Blob as binary string
*
* @param {Object} file
* @return {mixed}
*
*/
readFileFromBlob(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (response) => {
resolve(response.target.result)
}
reader.onerror = (error) => reject(error)
reader.readAsBinaryString(file)
})
}
/**
* creating directory for file,
* using filename and uniqid to get a hash and split
* him into chunks and creating directories
*
* @param {string} filename
* @return {string}
*/
_createDirPath(uuid) {
// getting hash
const hashPath = md5(uuid)
// creating string of relative path, and create missing directories
const relativePath = this._chunks(hashPath).join('/')
const absolutePath = this.storagePath + '/' + relativePath
mkdirp.sync(absolutePath)
return relativePath
}
/**
* split string into chunks
*
* @param {string} value
* @param {integer} size
* @return {array}
*/
_chunks(value, size = 8) {
let result = [];
if (value !== null) {
value = String(value);
size = ~~size;
result = size > 0 ? value.match(new RegExp('.{1,' + size + '}', 'g')) : [value];
}
return result;
}
}
export default FileHandler

@ -0,0 +1,169 @@
import DatabaseHandler from './databaseHandler.js'
import AppsStore from './apps.js'
import tagsStore from './../stores/tags.js'
/**
* tags
*
* @author Björn Hase
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/tellme-bot.git
*
*/
class TagsDatabase extends DatabaseHandler {
constructor() {
super('tags')
// add index for apps
this.createIndex([
'name'
], 'tags')
}
/**
* adding new tags, if tags are already
* exists skip tag
*
* @param {object} data
*
*/
update(tags) {
const query = {
'selector': {
'name': {
'$in': tags
}
}, 'fields': [
'name'
]
}
// check for existings tags and remove
// adding tags that are not removed
this.db.find(query).then((documents) => {
return this.filterTags(documents, tags)
}).then((tags) => {
if (tags.length > 0) {
// adding tags to entries
for (let i = 0; i < tags.length; i++) {
this.db.post({
'name': tags[i]
}).then(() => {
// last tag, then check for remove
if (i === (tags.length - 1)) {
this.removeNotNeeded()
}
})
}
}
})
}
/**
* search for tags in apps and remove
* tags that not longer exists in apps
*
*
*/
removeNotNeeded() {
const query = {
'selector': {
'name': {
'$exists': true
}
}, 'fields': [
'_id',
'_rev',
'name'
]
}
this.db.find(query).then((tags) => {
if (tags.docs.length > 0) {
const appsStore = new AppsStore()
for (let i = 0; i < tags.docs.length; i++) {
// create parameters
const parameters = {
tags: [],
sort: 'name'
}
// add tag name to parameters
parameters.tags.push(tags.docs[i].name)
// find app by tag, if no app is found, remove tag and update filter
appsStore.find(parameters).then((apps) => {
if (apps.length === 0) {
this.db.remove(tags.docs[i]).then(() => {
tagsStore.get()
})
}
})
}
}
})
}
/**
* filter tags, if they are already exists
*
* @param {array} documents
* @param {array} tags
* @return {array}
*
*/
filterTags(documents, tags) {
if (documents.docs.length > 0) {
for (let i = 0; i < documents.docs.length; i++) {
// check for name in tags
const index = tags.indexOf(documents.docs[i].name)
// if found remove from tags
if (index >= 0) {
tags.splice(index, 1)
}
}
}
return tags
}
/**
* find apps
*
* @return {array}
*
*/
find() {
const query = {
'fields': [
'name'
],
'selector': {
'name': {
'$exists': true
}
},
'sort': [
'name'
]
}
return this.db.find(query).then((documents) => {
return documents.docs
})
}
}
export default TagsDatabase

@ -3,7 +3,7 @@
<div class="sidebar__inner">
<!-- header -->
<tiny-sidebar-form-header title="New" close={ (event) => { handleClose(event) }}></tiny-sidebar-form-header>
<tiny-sidebar-form-header title="{ state.current._id ? 'Edit ' + state.current.name : 'New' }" close={ (event) => { handleClose(event) }}></tiny-sidebar-form-header>
<div class="sidebar__form">
@ -18,8 +18,9 @@
<div class="field-group">
<label class="field-label">
name*
<input type="text" class="field-text" name="name" value="{ state.current.name ? state.current.name : '' }" />
<input type="text" class="field-text" name="name" value={ state.current.name } />
</label>
<field-error name="name"></field-error>
</div>
<!-- command -->
@ -27,8 +28,8 @@
<label class="field-label">
command*
<div class="field-input-group">
<input class="field-text" type="text" name="command" value="{ state.current.command ? state.current.command : '' }" />
<button class="button button--hover-icon-contrast m-bottom-0" type="button" onclick={ handleSelectProgram }>
<input class="field-text" type="text" name="command" value="{ state.current.command }" />
<button class="button button--hover-icon-contrast m-bottom-0" type="button" onclick={ (event) => { handleOpenCommand(event) }}>
<svg class="icon">
<use xlink:href="symbol-defs.svg#icon-folder" />
</svg>
@ -52,15 +53,16 @@
<div class="field-group">
<label class="field-label">
description
<textarea class="field-text" name="description" value="{ state.current.description ? state.current.description : '' }"></textarea>
<textarea class="field-text" name="description" value="{ state.current.description }"></textarea>
</label>
</div>
<!-- media -->
<div class="field-group">
<label class="field-label">
Thumbnail<br />
<button class="button button--hover-icon-contrast m-top-3" type="button" onclick={ (event) => { handleSelectThumbnail(event) }}>
thumbnail<br />
<input id="apps-form-select-thumbnail" class="hidden-xxs hidden-xs-up" type="file" accept="image/png, image/jpeg, image/gif, image/webm, image/webp" onchange={ (event) => { handleSelectThumbnail(event) }} />
<button class="button button--hover-icon-contrast m-top-3" type="button" onclick={ (event) => { handleOpenThumbnail(event) }}>
<svg class="icon">
<use xlink:href="symbol-defs.svg#icon-image" />
</svg>
@ -73,7 +75,7 @@
<use xlink:href="symbol-defs.svg#icon-delete" />
</svg>
</button>
<img class="w-100" src={ toImage(state.current.thumbnail) } />
<img class="w-100" src="{ resolveMediaPath(state.current.thumbnail) }" />
</div>
</div>
<field-error name="thumbnail"></field-error>
@ -83,7 +85,11 @@
</div>
<!-- footer -->
<tiny-sidebar-form-footer id={ state.current.id ? state.current.id : false } loading={ state.loading }></tiny-sidebar-form-footer>
<tiny-sidebar-form-footer
id={ state.current._id ? state.current._id : false }
loading={ state.loading }
handle-delete={ handleDelete }>
</tiny-sidebar-form-footer>
</form>
</div>
@ -109,6 +115,11 @@
// validator
import FormValidator from '@tiny-components/validator/src/formValidator.js'
// confirm
import confirmStore from '@tiny-components/confirm/src/store.js'
const { ipcRenderer } = require('electron')
/**
* handler for apps
*
@ -129,57 +140,55 @@
*/
onMounted() {
this.state.storagePath = ipcRenderer.sendSync('getPathUserData') + '/attachments'
// adding event for open sidebar
formStore.on('open', (id) => {
this.reset()
this.state.current = false
this.state.open = true
// reset tags
tagsStore.trigger('update', [])
// if id is send, load apps from pouchdb
if (id) {
Neutralino.extensions.dispatch('js.neutralino.pouchdb', 'pouchdb.apps.findOne', {
'id': id
})
// if id is not undefined, get data from pouchdb
if (id !== undefined) {
appsStore.getOne(id)
}
this.state.open = true
this.update()
})
// creating formValidator
this.state.validator = new FormValidator(this.$('.form'), {
'name': {
'presence': true
},
'command': {
'presence': true
},
'tags': {
this.createValidator()
}
})
// receive from extensions database
appsStore.on('success', (data) => {
appsStore.trigger('update')
// adding on success
this.state.validator.onSuccess((event, data) => {
this.handleSuccess(event, data)
this.state.current._id = data._id
this.state.current._rev = data._rev
// stop loading
this.state.loading = false
this.update()
})
// receive from extensions database
Neutralino.events.on('pouchdb.apps.success', (event) => {
appsStore.on('close', (data) => {
appsStore.trigger('updated')
appsStore.trigger('update')
this.state.current._id = event.detail._id
this.state.current._rev = event.detail._rev
this.state.open = false
// stop loading
this.state.loading = false
this.update()
})
// receive from extensions database
Neutralino.events.on('pouchdb.apps.readyOne', (event) => {
appsStore.on('readyOne', (data) => {
this.state.current = event.detail
this.state.current = data
if (!this.state.current.tags) {
this.state.current.tags = []
@ -192,7 +201,31 @@
this.update()
})
},
},
/**
* creating Validator for form
*
*
*/
createValidator() {
this.state.validator = new FormValidator(this.$('.form'), {
'name': {
'presence': true
},
'command': {
'presence': true
},
'tags': {
}
})
// adding on success
this.state.validator.onSuccess((event, data) => {
this.handleSuccess(event, data)
})
},
/**
* choose a file for command
@ -201,18 +234,30 @@
* @param {object} event
*
*/
async handleSelectProgram(event) {
handleOpenCommand(event) {
event.preventDefault()
this.$('#apps-form-select-program').click()
},
// open dialog to choose a file that starts a programm
const entry = await Neutralino.os.showOpenDialog('Choose a File', {
'multiSelections': false,
'defaultPath': NL_CWD
})
/**
*
*
*
*/
handleSelectCommand(event) {
event.preventDefault()
if (event.target.files[0]) {
if (!this.state.current) {
this.state.current = {
// adding path to command in current
this.state.current.command = entry
this.update()
}
}
this.state.current.command = event.target.files[0].path
this.update()
}
},
/**
@ -224,55 +269,57 @@
handleSuccess(event, data) {
event.preventDefault()
data = Object.assign(this.state.current, data)
this.state.current = data
this.state.loading = true
this.update()
// adding thumbnail
data.thumbnail = this.state.current.thumbnail
this.update()
// send to extensions database
if (data._id && data._rev) {
Neutralino.extensions.dispatch('js.neutralino.pouchdb', 'pouchdb.apps.update', data)
appsStore.put(data)
} else {
Neutralino.extensions.dispatch('js.neutralino.pouchdb', 'pouchdb.apps.create', data)
appsStore.post(data)
}
},
/**
*
* open dialog for choosing thumbnail
*
*
* @param object event
* @param {object} event
*
*/
async handleSelectThumbnail(event)
handleOpenThumbnail(event)
{
const entry = await Neutralino.os.showOpenDialog('Choose a Thumbnail for App', {
'multiSelections': false,
'defaultPath': NL_CWD,
'filters': [{
'name': 'Images',
'extensions': [
'jpg',
'png',
'jpeg',
'webp',
'gif'
]
}]
})
event.preventDefault()
this.$('#apps-form-select-thumbnail').click()
},
// getting data
const data = await Neutralino.filesystem.readBinaryFile(entry[0])
/**
* if select thumbnail
*
*
* @param {object} event
*
*/
handleSelectThumbnail(event)
{
if (event.target.files[0]) {
if (!this.state.current) {
this.state.current = {
// adding as blob
this.state.current.thumbnail = _arrayBufferToBase64(data)
}
}
this.update()
this.state.current.thumbnail = event.target.files[0]
this.update()
}
},
/**
* remove current thumbnail for App
* remove thumbnail
*
*
* @param {object} event
@ -281,30 +328,42 @@
handleRemoveThumbnail(event) {
event.preventDefault()
this.state.current.thumbnail = null
delete this.state.current.thumbnail
// remove value, make sure that onChange trigger also on same file
this.$('#apps-form-select-thumbnail').value = ''
this.update()
},
/**
* delete current
*
*
* @param {object} media
*
*/
toImage(media) {
return 'data:image/png;base64,' + media
handleDelete() {
confirmStore.trigger('open', {
'content': 'Delete "' + this.state.current.name + '" ?'
})
confirmStore.confirm(() => {
appsStore.remove(this.state.current)
})
},
/**
* reset data of current form
*
* @param {[type]} file
* @return {[type]}
*/
reset() {
this.state.current = {
resolveMediaPath(file) {
let result = this.state.storagePath + '/' + file.path
if (file instanceof Blob) {
result = file.path
}
tagsStore.trigger('update', [])
return result
}
}
}

@ -1,12 +1,12 @@
<tiny-sidebar-form-footer>
<div class="sidebar__footer">
<button class="button button--danger m-bottom-0" disabled={ props.loading } if={ props.id }>
<button class="button button--danger button--hover-icon-contrast m-bottom-0" disabled={ props.loading } type="button" if={ props.id } onclick={ () => { props.handleDelete() }}>
Delete
<svg class="icon fill-success p-left-3" aria-hidden="true">
<svg class="icon fill-danger p-left-3" aria-hidden="true">
<use xlink:href="symbol-defs.svg#icon-delete"></use>
</svg>
</button>
<button class="button m-bottom-0" type="submit" disabled={ props.loading } close>
<button class="button button--hover-icon-contrast m-bottom-0" type="submit" disabled={ props.loading }>
Save
<svg class="icon fill-success p-left-3" aria-hidden="true">
<use xlink:href="symbol-defs.svg#icon-check"></use>

@ -6,7 +6,7 @@
<div class="bar__end">
<button class="button button--icon button--hover-icon-contrast m-top-3 m-bottom-3" type="button" onclick={ props.close }>
<svg class="icon fill-danger" aria-hidden="true">
<use xlink:href="/symbol-defs.svg#icon-close"></use>
<use xlink:href="symbol-defs.svg#icon-close"></use>
</svg>
</button>
</div>

@ -9,21 +9,23 @@
export default {
state: {
open: false, // if sidebar is open
loading: false, // if loading is shown
current: { // current data of form
}
/**
* adding default values
*
*
*/
onBeforeMount() {
this.state.open = false
this.state.loading = false
this.state.current = false
},
/**
* close current sidebar
*
*
*/
handleClose() {
this.reset()
this.state.open = false
this.update()
},
@ -32,7 +34,8 @@ export default {
* getting css classes for sidebar
*
*
* @return {String}
* @return {string}
*
*/
getCssClasses() {
const classes = [
@ -44,17 +47,5 @@ export default {
}
return classes.join(' ')
},
/**
*
*
*/
onBeforeMount() {
if (!this.hasOwnProperty('reset')) {
throw new Error('reset-Function in Form is missing')
}
this.reset()
}
}

@ -7,8 +7,107 @@
*
*/
import observable from '@riotjs/observable'
import AppsDatabase from './../database/apps.js'
import tagsStore from './../stores/tags.js'
import observable from '@riotjs/observable'
export default observable({
export default observable({
})
parameters: {
sort: 'name',
tags: []
},
/**
* getting all apps
*
*/
get() {
const appsDatabase = new AppsDatabase()
appsDatabase.find(this.parameters).then((data) => {
this.trigger('ready', data)
})
},
/**
* getting single apps
*
* @param {uuid} id
*
*/
getOne(id) {
const appsDatabase = new AppsDatabase()
appsDatabase.findOneById(id).then((data) => {
this.trigger('readyOne', data)
})
},
/**
* create new app
*
* @param {object} data
*
*/
post(data) {
const appsDatabase = new AppsDatabase()
appsDatabase.create(data).then((data) => {
this.trigger('success', data)
tagsStore.get()
})
},
/**
* update app
*
* @param {object} data
*
*/
put(data) {
const appsDatabase = new AppsDatabase()
appsDatabase.update(data).then((data) => {
this.trigger('success', data)
tagsStore.get()
})
},
/**
* update app
*
* @param {object} data
*
*/
remove(data) {
const appsDatabase = new AppsDatabase()
appsDatabase.remove(data).then(() => {
this.trigger('close')
})
},
/**
* orderBy and run get
*
* @param {string} orderBy
*
*/
sort(column) {
this.parameters.sort = column
this.get()
},
/**
* filter and run get
*
* @param {array} tags
*
*/
filter(tags) {
this.parameters.tags = tags
this.get()
}
})

@ -1,5 +1,5 @@
/**
* Store for apps
* Store for tags
*
* @author Björn Hase
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
@ -7,8 +7,20 @@
*
*/
import observable from '@riotjs/observable'
import TagsDatabase from './../database/tags.js'
import observable from '@riotjs/observable'
export default observable({
export default observable({
})
/**
* get all tags
*
*/
get() {
const tagsDatabase = new TagsDatabase()
tagsDatabase.find().then((data) => {
this.trigger('ready', data)
})
}
})

@ -3,15 +3,12 @@
<header class="header m-bottom-3">
<div class="display-flex justify-content-space-between m-top-3">
<div>
<button class="button button--icon button--hover-icon-contrast m-bottom-0">
<svg class="icon icon--big">
<use xlink:href="symbol-defs.svg#icon-filter" />
</svg>
</button>
<div class="display-flex">
<potato-sorting class="m-right-3"></potato-sorting>
<potato-filter></potata-filter>
</div>
<div>
<potato-apps-form-button></potato-apps-form-button>
<potato-apps-form-button class="m-right-3"></potato-apps-form-button>
<potato-close-button></potato-close-button>
</div>
</div>
@ -20,23 +17,26 @@
<div class="apps">
<div class="grid">
<div class="col-12 col-md-3 col-lg-2" each={ app in state.apps }>
<div class="apps__item" onclick={ (event) => { handleClick(event, app) }} style="{ addBackgroundStyles(app) }">
<div class="apps__header p-4 right">
<button class="button button--icon button--hover-icon-contrast" onclick={ (event) => { handleEdit(event, app) }}>
<svg class="icon">
<use xlink:href="symbol-defs.svg#icon-settings" />
</svg>
</button>
</div>
<div class="apps__item-panel p-4">
<h3 class="m-bottom-0">
{ app.name }
</h3>
<div if={ app.description } class="content m-top-3 m-bottom-last-child-0">
{ app.description }
<div class="apps__item" onclick={ (event) => { handleClick(event, app) }}>
<img class="apps__item-background" if={ app.thumbnail } src="{ state.storagePath + '/' + app.thumbnail.path }" />
<div class="apps__item-inner">
<div class="apps__header p-4 right">
<button class="button button--icon button--hover-icon-contrast" onclick={ (event) => { handleEdit(event, app) }}>
<svg class="icon">
<use xlink:href="symbol-defs.svg#icon-settings" />
</svg>
</button>
</div>
<div if={ app.tags && app.tags.length > 0 } class="m-top-3">
<span class="badge m-right-3" each={ tag in app.tags }>{ tag }</span>
<div class="apps__item-panel p-4">
<h3 class="m-bottom-0">
{ app.name }
</h3>
<div if={ app.description } class="content m-top-3 m-bottom-last-child-0">
{ app.description }
</div>
<div if={ app.tags && app.tags.length > 0 } class="m-top-3">
<span class="badge m-right-3" each={ tag in app.tags }>{ tag }</span>
</div>
</div>
</div>
</div>
@ -47,16 +47,23 @@
<potato-apps-form></potato-apps-form>
</div>
<script>
import * as riot from 'riot'
import dayjs from 'dayjs'
import { spawn } from 'child_process'
import potatoAppsForm from './../forms/apps.riot'
import potatoAppsFormButton from './../components/appsFormButton.riot'
import potatoFilter from './../components/filter.riot'
import potatoSorting from './../components/sorting.riot'
import appsStore from './../stores/apps.js'
import formStore from './../stores/form.js'
import notificationStore from '@tiny-components/notification/src/notificationStore.js'
const { ipcRenderer } = require('electron')
// let it rain
riot.register('potato-apps-form', potatoAppsForm)
riot.mount('potato-apps-form')
@ -64,10 +71,16 @@
riot.register('potato-apps-form-button', potatoAppsFormButton)
riot.mount('potato-apps-form-buttons')
riot.register('potato-filter', potatoFilter)
riot.mount('potato-filter')
riot.register('potato-sorting', potatoSorting)
riot.mount('potato-sorting')
/**
*
*
* @author Björn Hase
* @author Björn Hase <me@herr-hase.wtf>
* @license hhttps://www.gnu.org/licenses/gpl-3.0.en.html GPL-3
* @link https://gitea.node001.net/HerrHase/potato-launcher.git
*
@ -80,7 +93,8 @@
*
*/
state: {
apps: []
apps: [],
storagePath: undefined
},
/**
@ -89,19 +103,21 @@
*/
onMounted() {
this.state.storagePath = ipcRenderer.sendSync('getPathUserData') + '/attachments'
// add event to receive data from pouchdb
Neutralino.events.on('pouchdb.apps.ready', (event) => {
this.state.apps = event.detail
appsStore.on('ready', (data) => {
this.state.apps = data
this.update()
})
// send event to get all after
appsStore.on('updated', () => {
Neutralino.extensions.dispatch('js.neutralino.pouchdb', 'pouchdb.apps.find')
appsStore.on('update', () => {
appsStore.get()
})
// trigger event to get all apps
appsStore.trigger('updated')
appsStore.trigger('update')
},
@ -113,20 +129,33 @@
*
*/
async handleClick(event, app) {
try {
const state = await Neutralino.os.execCommand(app.command)
// if there is a error message display send a notification
if (state.stdErr) {
notificationStore.danger(state.stdErr)
const running = spawn(app.command)
// send notification for starting
running.on('spawn', (data) => {
notificationStore.success(app.name + ' is starting')
// adding current datetime for started app
app.date_started = dayjs()
appsStore.put(app)
})
// if app has a error
running.on('error', (error) => {
notificationStore.danger(app.name + ' could be not started, check if command is correct')
})
// if app is closing
running.on('close', (code) => {
if (code === 0) {
notificationStore.success(app.name + ' is closed')
}
} catch(error) {
console.log(error)
}
})
},
/**
* start app
* edit app
*
* @param {object} event
* @param {object} app
@ -135,24 +164,7 @@
handleEdit(event, app) {
event.stopPropagation()
formStore.open(app._id)
},
/**
*
*
*/
addBackgroundStyles(app) {
let styles = false
if (app.thumbnail) {
styles = "background-image: url('data:image/jpg;base64," + app.thumbnail + "')"
}
return styles
}
}
</script>
</potato-apps-view>

@ -1,3 +1,7 @@
.justify-content-space-between {
justify-content: space-between;
}
.hidden {
display: none;
}

@ -7,20 +7,26 @@
.apps {
&__item {
display: flex;
flex-flow: column;
justify-content: space-between;
border: 1px solid var(--border);
position: relative;
&-background {
position: absolute;
object-fit: cover;
width: 100%;
height: 100%;
z-index: -1;
}
// background image default
background-repeat: no-repeat;
background-size: cover;
background-position: 50%;
&-inner {
display: flex;
flex-flow: column;
justify-content: space-between;
height: 450px;
border: 1px solid var(--border);
height: 450px;
transition: transform 0.25s, box-shadow 0.5s;
transition: transform 0.25s, box-shadow 0.5s;
}
&:hover {
cursor: pointer;

@ -15,4 +15,12 @@
}
}
}
&--hover-icon-text {
&:hover {
.icon {
fill: var(--text);
}
}
}
}

@ -0,0 +1,6 @@
.tiny-modal {
.modal__footer {
display: flex;
justify-content: space-between;
}
}

@ -0,0 +1,67 @@
.filter {
&__tags {
visibility: hidden;
position: absolute;
z-index: 100;
margin: 1em 0 0;
.button {
display: block;
margin-right: 1em;
.icon {
vertical-align: sub;
margin: 0 -0.25em 0 0.25em;
}
}
}
&:after {
content: '';
position: fixed;
left: 0;
top:0;
z-index: 99;
visibility: hidden;
width: 100%;
height: 100%;
background-color: #04040480;
}
.button--close {
display: none;
}
&__selected {
.button {
color: white;
padding: 0.8em 1.2em;
background: #878787;
.icon {
fill: white;
margin: 0 -0.25em 0 0.25em
}
}
}
&--open {
&:after {
visibility: visible;
}
.filter__tags {
visibility: visible;
}
.button--close {
display: inline-flex;
z-index: 101;
}
.button--open {
display: none;
}
}
}

@ -10,5 +10,7 @@
'components/field-tags',
'components/loading',
'components/buttons',
'components/filter',
'components/confirm',
'components/icons';

@ -16,8 +16,8 @@ const SvgSpritemapPlugin = require('svg-spritemap-webpack-plugin')
mix.webpackConfig({
plugins: [
new SvgSpritemapPlugin([
'node_modules/@tiny-components/plain-ui/src/icons/mono-icons/svg/*.svg'
], {
'node_modules/@tiny-components/plain-ui/src/icons/mono-icons/svg/*.svg'
], {
output: {
filename: 'symbol-defs.svg',
chunk: {
@ -27,7 +27,7 @@ mix.webpackConfig({
plugins: [{
name: 'convertStyleToAttrs',
active: true
},{
}, {
name: 'removeStyleElement',
active: true
}, {
@ -44,25 +44,32 @@ mix.webpackConfig({
})
],
module: {
rules: [{
test: /\.riot$/,
exclude: '/node_modules/',
use: [{
loader: '@riotjs/webpack-loader',
options: {
hot: false
}
}]
}]
}
rules: [{
test: /\.riot$/,
exclude: '/node_modules/',
use: [{
loader: '@riotjs/webpack-loader',
options: {
hot: false
}
}]
}]
},
externals: {
child_process: 'require("child_process")',
fs: 'require("fs")',
path: 'require("path")',
mkdirp: 'require("mkdirp")',
electron: 'require("electron")'
}
}).options({
processCssUrls: false,
target: 'node'
})
mix
.setPublicPath('./build')
.setPublicPath('./dist')
.js('src/js/app.js', 'js')
.copy('resources/js/neutralino.js', 'build/js')
.sass('src/scss/styles.scss', 'css')
.copyDirectory('node_modules/@tiny-components/plain-ui/src/fonts/**', 'build/css')
.copyDirectory('resources/index.html', 'build')
.copyDirectory('node_modules/@tiny-components/plain-ui/src/fonts/**', 'dist/css')
.copyDirectory('resources/index.html', 'dist')
Loading…
Cancel
Save