ui: Begin GUI
The GUI shows a screenshot of each monitor, as well as the title and URL of the page being displayed. There's also a button to trigger a page reload on the remote machine.master
parent
fa1c9cb42a
commit
0536574072
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "ui",
|
||||
"path": "ui"
|
||||
},
|
||||
{
|
||||
"name": "svc",
|
||||
"path": "svc"
|
||||
},
|
||||
{
|
||||
"name": "aiomarionette",
|
||||
"path": "../aiomarionette"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
|
@ -0,0 +1,19 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended",
|
||||
"@vue/typescript/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
},
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
},
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"printWidth": 80
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
# hudctrl
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "hudctrl",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"register-service-worker": "^1.7.2",
|
||||
"vue": "^2.6.14",
|
||||
"vue-class-component": "^7.2.3",
|
||||
"vue-property-decorator": "^9.1.2",
|
||||
"vuetify": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-pwa": "~5.0.0",
|
||||
"@vue/cli-plugin-typescript": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@vue/eslint-config-typescript": "^9.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"prettier": "^2.4.1",
|
||||
"sass": "~1.32.0",
|
||||
"sass-loader": "^10.0.0",
|
||||
"typescript": "~4.5.5",
|
||||
"vue-cli-plugin-vuetify": "~2.4.8",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"vuetify-loader": "^1.7.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Basement HUD</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,118 @@
|
|||
<template>
|
||||
<v-app>
|
||||
<v-app-bar color="grey darken-2" app>
|
||||
<v-toolbar-title>Basement HUD</v-toolbar-title>
|
||||
</v-app-bar>
|
||||
<v-main>
|
||||
<v-container>
|
||||
<v-row justify="space-around">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
xl="4"
|
||||
v-for="(screen, name) in screens"
|
||||
:key="name"
|
||||
>
|
||||
<v-card>
|
||||
<v-hover v-slot="{ hover }">
|
||||
<v-img
|
||||
:src="screen.screenshot"
|
||||
class="ss white--text align-end"
|
||||
>
|
||||
<v-fade-transition>
|
||||
<div
|
||||
v-if="hover"
|
||||
class="d-flex justify-space-around align-center ss-overlay"
|
||||
>
|
||||
<v-btn x-large icon dark @click="refresh_ss(name)">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
<v-card-title>{{ screen.title }}</v-card-title>
|
||||
</v-img>
|
||||
</v-hover>
|
||||
<v-card-subtitle class="pb-0">{{ name }}</v-card-subtitle>
|
||||
<v-card-text>
|
||||
<div class="screen-url">
|
||||
<a :href="screen.url">{{ screen.url }}</a>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn @click="refresh(name)" text>
|
||||
<span>Reload Page</span>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import Component from "vue-class-component";
|
||||
|
||||
interface Screen {
|
||||
title: string;
|
||||
url: string;
|
||||
screenshot: string;
|
||||
}
|
||||
|
||||
interface Screens {
|
||||
[keys: string]: Screen;
|
||||
}
|
||||
|
||||
@Component
|
||||
export default class App extends Vue {
|
||||
screens: Screens = {};
|
||||
|
||||
async mounted() {
|
||||
const res = await fetch("/api/screen/");
|
||||
if (res.ok) {
|
||||
const screens: Screens = await res.json();
|
||||
for (const [name, screen] of Object.entries(screens)) {
|
||||
const ts = new Date().getTime();
|
||||
screen.screenshot = `/api/screen/${name}/screenshot?${ts}`;
|
||||
}
|
||||
this.screens = screens;
|
||||
}
|
||||
}
|
||||
|
||||
async refresh_ss(name: string) {
|
||||
const screen = this.screens[name];
|
||||
const base = screen.screenshot.split("?")[0];
|
||||
const ts = new Date().getTime();
|
||||
screen.screenshot = `${base}?${ts}`;
|
||||
}
|
||||
|
||||
async refresh(name: string) {
|
||||
const res = await fetch(`/api/screen/${name}/refresh`, { method: "POST" });
|
||||
if (res.ok) {
|
||||
await this.refresh_ss(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.ss {
|
||||
position: relative;
|
||||
}
|
||||
.ss-overlay {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.screen-url {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,11 @@
|
|||
import Vue from "vue";
|
||||
import App from "./App.vue";
|
||||
import "./registerServiceWorker";
|
||||
import vuetify from "./plugins/vuetify";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
vuetify,
|
||||
render: (h) => h(App),
|
||||
}).$mount("#app");
|
|
@ -0,0 +1,10 @@
|
|||
import Vue from "vue";
|
||||
import Vuetify from "vuetify/lib";
|
||||
|
||||
Vue.use(Vuetify);
|
||||
|
||||
export default new Vuetify({
|
||||
theme: {
|
||||
dark: true,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
import { register } from "register-service-worker";
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||
ready() {
|
||||
console.log(
|
||||
"App is being served from cache by a service worker.\n" +
|
||||
"For more details, visit https://goo.gl/AFskqB"
|
||||
);
|
||||
},
|
||||
registered() {
|
||||
console.log("Service worker has been registered.");
|
||||
},
|
||||
cached() {
|
||||
console.log("Content has been cached for offline use.");
|
||||
},
|
||||
updatefound() {
|
||||
console.log("New content is downloading.");
|
||||
},
|
||||
updated() {
|
||||
console.log("New content is available; please refresh.");
|
||||
},
|
||||
offline() {
|
||||
console.log(
|
||||
"No internet connection found. App is running in offline mode."
|
||||
);
|
||||
},
|
||||
error(error) {
|
||||
console.error("Error during service worker registration:", error);
|
||||
},
|
||||
});
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import Vue, { VNode } from "vue";
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface Element extends VNode {}
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
declare module "*.vue" {
|
||||
import Vue from "vue";
|
||||
export default Vue;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"vuetify",
|
||||
"webpack-env"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
const { defineConfig } = require("@vue/cli-service");
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: ["vuetify"],
|
||||
devServer: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://[::1]:8000/',
|
||||
pathRewrite: {
|
||||
'^/api': ''
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue