initial attempt to refactor frontend with lit

This commit is contained in:
Lars Berning 2021-11-27 17:17:58 +01:00
parent 66ec2070bc
commit 70a86d0914
No known key found for this signature in database
GPG Key ID: 028E73C9E1D8A0B3
14 changed files with 544 additions and 196 deletions

21
app/client/.eslintrc.json Normal file
View File

@ -0,0 +1,21 @@
{
"env": {
"browser": true,
"node": false,
"es2021": true
},
"extends": [
"standard",
"plugin:wc/recommended",
"plugin:lit/recommended"
],
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"ignorePatterns": ["**/*.min.js"],
"rules": {
"camelcase": 0
}
}

View File

@ -0,0 +1,44 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Main Component of the Open Rowing Monitor App
*/
import { AppElement, html, css } from './AppElement'
import { customElement } from 'lit/decorators.js'
import { createApp } from '../lib/network.js'
import './PerformanceDashboard'
@customElement('web-app')
export class App extends AppElement {
static get styles () {
return css`
`
}
constructor () {
super()
this.app = createApp()
window.app = this.app
this.app.setMetricsCallback(metrics => this.metricsUpdated(metrics))
}
static properties = {
metrics: { state: true }
};
render () {
return html`
<performance-dashboard .metrics=${this.metrics}></performance-dashboard>
`
}
metricsUpdated (metrics) {
this.metrics = Object.assign({}, metrics)
}
createRenderRoot () {
return this
}
}

View File

@ -0,0 +1,16 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Base Component for all other App Components
*/
import { LitElement } from 'lit'
export * from 'lit'
export class AppElement extends LitElement {
// todo: should use shadow root once the global style file is dissolved into the components
createRenderRoot () {
return this
}
}

View File

@ -0,0 +1,59 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Component that renders the action buttons of the dashboard
*/
import { AppElement, html, css } from './AppElement'
import { customElement, property } from 'lit/decorators.js'
import { icon_undo, icon_expand, icon_compress, icon_poweroff, icon_bluetooth } from '../lib/icons'
@customElement('dashboard-actions')
export class DashboardActions extends AppElement {
static get styles () {
return css`
`
}
@property({ type: String })
peripheralMode = ''
render () {
return html`
<button @click=${this.reset}>${icon_undo}</button>
<!-- todo: hide in standalone mode -->
<button @click=${this.toggleFullscreen}>
<div id="fullscreen-icon">${icon_expand}</div>
<div id="windowed-icon">${icon_compress}</div>
</button>
<button @click=${this.close} id="close-button">${icon_poweroff}</button>
<button @lick=${this.switchPeripheralMode}>${icon_bluetooth}</button>
<div class="metric-unit">${this.peripheralMode}</div>
`
}
toggleFullscreen () {
const fullscreenElement = document.getElementsByTagName('web-app')[0]
if (!document.fullscreenElement) {
fullscreenElement.requestFullscreen({ navigationUI: 'hide' })
} else {
if (document.exitFullscreen) {
document.exitFullscreen()
}
}
}
close () {
window.close()
}
// todo: should use events instead of communicating via a global app object
reset () {
window.app.reset()
}
switchPeripheralMode () {
window.app.switchPeripheralMode()
}
}

View File

@ -0,0 +1,55 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Component that renders a metric of the dashboard
*/
import { AppElement, html, svg, css } from './AppElement'
import { customElement, property } from 'lit/decorators.js'
@customElement('dashboard-metric')
export class DashboardMetric extends AppElement {
static get styles () {
return css`
`
}
@property({ type: Object })
icon
@property({ type: String })
unit = ''
@property({ type: String })
value = ''
@property({ type: String })
batteryLevel = ''
render () {
return html`
<div class="label">${this.icon}</div>
<div class="content">
<span class="metric-value">${this.value}</span>
<span class="metric-unit">${this.unit}</span>
</div>
${this.batteryLevel
? html`<div id="heartrate-battery-container">${this.batteryIcon}</div>`
: ''}
`
}
get batteryIcon () {
// 416 is the max width value of the battery bar in the SVG graphic
const batteryWidth = this.batteryLevel * 416 / 100
return svg`
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
<path fill="currentColor" d="M544 160v64h32v64h-32v64H64V160h480m16-64H48c-26.51 0-48 21.49-48 48v224c0 26.51 21.49 48 48 48h512c26.51 0 48-21.49 48-48v-16h8c13.255 0 24-10.745 24-24V184c0-13.255-10.745-24-24-24h-8v-16c0-26.51-21.49-48-48-48z"></path>
<rect fill="currentColor" id="battery-level" x="96" y="192" width=${batteryWidth} height="128"></rect>
</svg>
`
}
}

View File

@ -0,0 +1,40 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Component that renders the dashboard
*/
import { AppElement, html, css } from './AppElement'
import { customElement, property } from 'lit/decorators.js'
import './DashboardMetric'
import './DashboardActions'
import { icon_route, icon_stopwatch, icon_bolt, icon_paddle, icon_heartbeat, icon_fire, icon_clock } from '../lib/icons'
@customElement('performance-dashboard')
export class PerformanceDashboard extends AppElement {
static get styles () {
return css`
`
}
@property({ type: Object })
metrics
render () {
return html`
<dashboard-metric .icon=${icon_route} .unit=${this.metrics?.distanceTotal?.unit} .value=${this.metrics?.distanceTotal?.value}></dashboard-metric>
<dashboard-metric .icon=${icon_stopwatch} unit="/500m" .value=${this.metrics?.splitFormatted?.value}></dashboard-metric>
<dashboard-metric .icon=${icon_bolt} unit="watt" .value=${this.metrics?.power?.value}></dashboard-metric>
<dashboard-metric .icon=${icon_paddle} unit="/min" .value=${this.metrics?.strokesPerMinute?.value}></dashboard-metric>
${this.metrics?.heartrate?.value
? html`<dashboard-metric .icon=${icon_heartbeat} unit="bpm"
.value=${this.metrics?.heartrate?.value}
.batteryLevel=${this.metrics?.heartrateBatteryLevel?.value}></dashboard-metric>`
: html`<dashboard-metric .icon=${icon_paddle} unit="total" .value=${this.metrics?.strokesTotal?.value}></dashboard-metric>`}
<dashboard-metric .icon=${icon_fire} unit="kcal" .value=${this.metrics?.caloriesTotal?.value}></dashboard-metric>
<dashboard-metric .icon=${icon_clock} .value=${this.metrics?.durationTotalFormatted?.value}></dashboard-metric>
<dashboard-actions .peripheralMode=${this.metrics?.peripheralMode.value}></dashboard-actions>
`
}
}

View File

@ -15,132 +15,8 @@
<title>Open Rowing Monitor</title>
</head>
<body>
<script type="module" src="/index.js"></script>
<div id="dashboard" class="grid">
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M416 320h-96c-17.6 0-32-14.4-32-32s14.4-32 32-32h96s96-107 96-160-43-96-96-96-96 43-96 96c0 25.5 22.2 63.4 45.3 96H320c-52.9 0-96 43.1-96 96s43.1 96 96 96h96c17.6 0 32 14.4 32 32s-14.4 32-32 32H185.5c-16 24.8-33.8 47.7-47.3 64H416c52.9 0 96-43.1 96-96s-43.1-96-96-96zm0-256c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM96 256c-53 0-96 43-96 96s96 160 96 160 96-107 96-160-43-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"></path></svg>
</div>
<div class="content">
<span class="metric-value" id="distanceTotal"></span>
<span class="metric-unit" id="distanceTotalUnit">m</span>
</div>
</div>
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M432 304c0 114.9-93.1 208-208 208S16 418.9 16 304c0-104 76.3-190.2 176-205.5V64h-28c-6.6 0-12-5.4-12-12V12c0-6.6 5.4-12 12-12h120c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-28v34.5c37.5 5.8 71.7 21.6 99.7 44.6l27.5-27.5c4.7-4.7 12.3-4.7 17 0l28.3 28.3c4.7 4.7 4.7 12.3 0 17l-29.4 29.4-.6.6C419.7 223.3 432 262.2 432 304zm-176 36V188.5c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12V340c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path></svg>
</div>
<div class="content">
<span class="metric-value" id="splitFormatted"></span>
<span class="metric-unit">/500m</span>
</div>
</div>
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z"></path></svg>
</div>
<div class="content">
<span class="metric-value" id="power"></span>
<span class="metric-unit">watt</span>
</div>
</div>
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="m 420.20605,468.95387 33.11371,-33.1137 c 5.22683,-5.22684 8.77492,-17.24658 3.54807,-22.47345 l -2.40714,-2.40718 c -5.22685,-5.22685 -24.05986,3.18856 -30.46144,6.8845 L 208.43041,202.27519 C 197.48359,161.4211 171.36396,110.7189 139.6182,78.964771 L 104.97336,44.31993 C 84.074343,23.420923 78.962098,22.904081 62.197813,39.66838 L 24.014429,77.851751 C 9.0221901,92.843993 7.7868003,99.708469 28.685812,120.60748 l 34.653205,34.65321 c 30.664056,30.66402 82.456343,57.8654 123.310393,68.8122 l 215.56885,215.56886 c -3.69593,6.40155 -12.11135,25.23463 -6.88448,30.46145 l 2.40718,2.40715 c 5.2185,5.21846 17.23824,1.67039 22.46509,-3.55648 z"/>
<path fill="currentColor" d="M 93.111861,469.41843 59.998156,436.30471 c -5.226842,-5.22684 -8.774914,-17.24659 -3.548088,-22.47344 l 2.407166,-2.40717 c 5.226836,-5.22685 24.059868,3.18854 30.46142,6.88449 L 304.88751,202.73974 c 10.94682,-40.85409 37.06645,-91.55629 68.81223,-123.310429 L 408.34455,44.78448 c 20.89903,-20.899007 26.01127,-21.415839 42.77554,-4.65156 l 38.18338,38.183384 c 14.99224,14.992241 16.22764,21.856706 -4.67138,42.755726 l -34.65319,34.6532 c -30.66405,30.66404 -82.45634,57.8654 -123.31038,68.8122 L 111.09965,440.10631 c 3.69593,6.40153 12.11135,25.23461 6.88448,30.46143 l -2.40718,2.40716 c -5.21849,5.21846 -17.23824,1.67038 -22.465089,-3.55647 z"/>
</svg>
</div>
<div class="content">
<span class="metric-value" id="strokesPerMinute"></span>
<span class="metric-unit">/min</span>
</div>
</div>
<div class="col">
<div id="strokes-total-container">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="m 420.20605,468.95387 33.11371,-33.1137 c 5.22683,-5.22684 8.77492,-17.24658 3.54807,-22.47345 l -2.40714,-2.40718 c -5.22685,-5.22685 -24.05986,3.18856 -30.46144,6.8845 L 208.43041,202.27519 C 197.48359,161.4211 171.36396,110.7189 139.6182,78.964771 L 104.97336,44.31993 C 84.074343,23.420923 78.962098,22.904081 62.197813,39.66838 L 24.014429,77.851751 C 9.0221901,92.843993 7.7868003,99.708469 28.685812,120.60748 l 34.653205,34.65321 c 30.664056,30.66402 82.456343,57.8654 123.310393,68.8122 l 215.56885,215.56886 c -3.69593,6.40155 -12.11135,25.23463 -6.88448,30.46145 l 2.40718,2.40715 c 5.2185,5.21846 17.23824,1.67039 22.46509,-3.55648 z"/>
<path fill="currentColor" d="M 93.111861,469.41843 59.998156,436.30471 c -5.226842,-5.22684 -8.774914,-17.24659 -3.548088,-22.47344 l 2.407166,-2.40717 c 5.226836,-5.22685 24.059868,3.18854 30.46142,6.88449 L 304.88751,202.73974 c 10.94682,-40.85409 37.06645,-91.55629 68.81223,-123.310429 L 408.34455,44.78448 c 20.89903,-20.899007 26.01127,-21.415839 42.77554,-4.65156 l 38.18338,38.183384 c 14.99224,14.992241 16.22764,21.856706 -4.67138,42.755726 l -34.65319,34.6532 c -30.66405,30.66404 -82.45634,57.8654 -123.31038,68.8122 L 111.09965,440.10631 c 3.69593,6.40153 12.11135,25.23461 6.88448,30.46143 l -2.40718,2.40716 c -5.21849,5.21846 -17.23824,1.67038 -22.465089,-3.55647 z"/>
</svg>
</div>
<div class="content">
<span class="metric-value" id="strokesTotal"></span>
<span class="metric-unit">total</span>
</div>
</div>
<div id="heartrate-container">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M320.2 243.8l-49.7 99.4c-6 12.1-23.4 11.7-28.9-.6l-56.9-126.3-30 71.7H60.6l182.5 186.5c7.1 7.3 18.6 7.3 25.7 0L451.4 288H342.3l-22.1-44.2zM473.7 73.9l-2.4-2.5c-51.5-52.6-135.8-52.6-187.4 0L256 100l-27.9-28.5c-51.5-52.7-135.9-52.7-187.4 0l-2.4 2.4C-10.4 123.7-12.5 203 31 256h102.4l35.9-86.2c5.4-12.9 23.6-13.2 29.4-.4l58.2 129.3 49-97.9c5.9-11.8 22.7-11.8 28.6 0l27.6 55.2H481c43.5-53 41.4-132.3-7.3-182.1z"></path>
</svg>
</div>
<div class="content">
<span class="metric-value" id="heartrate"></span>
<span class="metric-unit">bpm</span>
</div>
<div id="heartrate-battery-container">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
<path fill="currentColor" d="M544 160v64h32v64h-32v64H64V160h480m16-64H48c-26.51 0-48 21.49-48 48v224c0 26.51 21.49 48 48 48h512c26.51 0 48-21.49 48-48v-16h8c13.255 0 24-10.745 24-24V184c0-13.255-10.745-24-24-24h-8v-16c0-26.51-21.49-48-48-48z"></path>
<rect fill="currentColor" id="battery-level" x="96" y="192" width="416" height="128"></rect>
</svg>
</div>
</div>
</div>
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M323.56 51.2c-20.8 19.3-39.58 39.59-56.22 59.97C240.08 73.62 206.28 35.53 168 0 69.74 91.17 0 209.96 0 281.6 0 408.85 100.29 512 224 512s224-103.15 224-230.4c0-53.27-51.98-163.14-124.44-230.4zm-19.47 340.65C282.43 407.01 255.72 416 226.86 416 154.71 416 96 368.26 96 290.75c0-38.61 24.31-72.63 72.79-130.75 6.93 7.98 98.83 125.34 98.83 125.34l58.63-66.88c4.14 6.85 7.91 13.55 11.27 19.97 27.35 52.19 15.81 118.97-33.43 153.42z"></path></svg>
</div>
<div class="content">
<span class="metric-value" id="caloriesTotal"></span>
<span class="metric-unit">kcal</span>
</div>
</div>
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>
</div>
<div class="content">
<span class="metric-value" id="durationTotalFormatted"></span>
<span class="metric-unit"></span>
</div>
</div>
<div class="col actions">
<div class="content">
<button onclick="app.reset()">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path>
</svg>
</button>
<button onclick="app.toggleFullscreen()" id="fullscreen-button">
<div id="fullscreen-icon">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M0 180V56c0-13.3 10.7-24 24-24h124c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H64v84c0 6.6-5.4 12-12 12H12c-6.6 0-12-5.4-12-12zM288 44v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12V56c0-13.3-10.7-24-24-24H300c-6.6 0-12 5.4-12 12zm148 276h-40c-6.6 0-12 5.4-12 12v84h-84c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24V332c0-6.6-5.4-12-12-12zM160 468v-40c0-6.6-5.4-12-12-12H64v-84c0-6.6-5.4-12-12-12H12c-6.6 0-12 5.4-12 12v124c0 13.3 10.7 24 24 24h124c6.6 0 12-5.4 12-12z"></path>
</svg>
</div>
<div id="windowed-icon">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path>
</svg>
</div>
</button>
<button onclick="app.close()" id="close-button">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M400 54.1c63 45 104 118.6 104 201.9 0 136.8-110.8 247.7-247.5 248C120 504.3 8.2 393 8 256.4 7.9 173.1 48.9 99.3 111.8 54.2c11.7-8.3 28-4.8 35 7.7L162.6 90c5.9 10.5 3.1 23.8-6.6 31-41.5 30.8-68 79.6-68 134.9-.1 92.3 74.5 168.1 168 168.1 91.6 0 168.6-74.2 168-169.1-.3-51.8-24.7-101.8-68.1-134-9.7-7.2-12.4-20.5-6.5-30.9l15.8-28.1c7-12.4 23.2-16.1 34.8-7.8zM296 264V24c0-13.3-10.7-24-24-24h-32c-13.3 0-24 10.7-24 24v240c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24z"></path>
</svg>
</button>
<button onclick="app.switchPeripheralMode()">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M292.6 171.1L249.7 214l-.3-86 43.2 43.1m-43.2 219.8l43.1-43.1-42.9-42.9-.2 86zM416 259.4C416 465 344.1 512 230.9 512S32 465 32 259.4 115.4 0 228.6 0 416 53.9 416 259.4zm-158.5 0l79.4-88.6L211.8 36.5v176.9L138 139.6l-27 26.9 92.7 93-92.7 93 26.9 26.9 73.8-73.8 2.3 170 127.4-127.5-83.9-88.7z"></path>
</svg>
</button>
<div class="metric-unit" id="peripheralMode"></div>
</div>
</div>
</div>
<noscript>You need to enable JavaScript to run this app.</noscript>
<web-app></web-app>
<script type="module" src="/index.js"></script>
</body>
</html>

View File

@ -2,8 +2,7 @@
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Init file for the web frontend
Initialization file for the web application
*/
import { createApp } from './app.js'
window.app = createApp()
import './components/App'

26
app/client/lib/icons.js Normal file
View File

@ -0,0 +1,26 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
SVG Icons that are used by the Application
*/
import { svg } from 'lit'
export const icon_route = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M416 320h-96c-17.6 0-32-14.4-32-32s14.4-32 32-32h96s96-107 96-160-43-96-96-96-96 43-96 96c0 25.5 22.2 63.4 45.3 96H320c-52.9 0-96 43.1-96 96s43.1 96 96 96h96c17.6 0 32 14.4 32 32s-14.4 32-32 32H185.5c-16 24.8-33.8 47.7-47.3 64H416c52.9 0 96-43.1 96-96s-43.1-96-96-96zm0-256c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM96 256c-53 0-96 43-96 96s96 160 96 160 96-107 96-160-43-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"></path></svg>`
export const icon_stopwatch = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M432 304c0 114.9-93.1 208-208 208S16 418.9 16 304c0-104 76.3-190.2 176-205.5V64h-28c-6.6 0-12-5.4-12-12V12c0-6.6 5.4-12 12-12h120c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-28v34.5c37.5 5.8 71.7 21.6 99.7 44.6l27.5-27.5c4.7-4.7 12.3-4.7 17 0l28.3 28.3c4.7 4.7 4.7 12.3 0 17l-29.4 29.4-.6.6C419.7 223.3 432 262.2 432 304zm-176 36V188.5c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12V340c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path></svg>`
export const icon_bolt = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z"></path></svg>`
export const icon_paddle = svg`
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="m 420.20605,468.95387 33.11371,-33.1137 c 5.22683,-5.22684 8.77492,-17.24658 3.54807,-22.47345 l -2.40714,-2.40718 c -5.22685,-5.22685 -24.05986,3.18856 -30.46144,6.8845 L 208.43041,202.27519 C 197.48359,161.4211 171.36396,110.7189 139.6182,78.964771 L 104.97336,44.31993 C 84.074343,23.420923 78.962098,22.904081 62.197813,39.66838 L 24.014429,77.851751 C 9.0221901,92.843993 7.7868003,99.708469 28.685812,120.60748 l 34.653205,34.65321 c 30.664056,30.66402 82.456343,57.8654 123.310393,68.8122 l 215.56885,215.56886 c -3.69593,6.40155 -12.11135,25.23463 -6.88448,30.46145 l 2.40718,2.40715 c 5.2185,5.21846 17.23824,1.67039 22.46509,-3.55648 z"/>
<path fill="currentColor" d="M 93.111861,469.41843 59.998156,436.30471 c -5.226842,-5.22684 -8.774914,-17.24659 -3.548088,-22.47344 l 2.407166,-2.40717 c 5.226836,-5.22685 24.059868,3.18854 30.46142,6.88449 L 304.88751,202.73974 c 10.94682,-40.85409 37.06645,-91.55629 68.81223,-123.310429 L 408.34455,44.78448 c 20.89903,-20.899007 26.01127,-21.415839 42.77554,-4.65156 l 38.18338,38.183384 c 14.99224,14.992241 16.22764,21.856706 -4.67138,42.755726 l -34.65319,34.6532 c -30.66405,30.66404 -82.45634,57.8654 -123.31038,68.8122 L 111.09965,440.10631 c 3.69593,6.40153 12.11135,25.23461 6.88448,30.46143 l -2.40718,2.40716 c -5.21849,5.21846 -17.23824,1.67038 -22.465089,-3.55647 z"/>
</svg>
`
export const icon_heartbeat = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M320.2 243.8l-49.7 99.4c-6 12.1-23.4 11.7-28.9-.6l-56.9-126.3-30 71.7H60.6l182.5 186.5c7.1 7.3 18.6 7.3 25.7 0L451.4 288H342.3l-22.1-44.2zM473.7 73.9l-2.4-2.5c-51.5-52.6-135.8-52.6-187.4 0L256 100l-27.9-28.5c-51.5-52.7-135.9-52.7-187.4 0l-2.4 2.4C-10.4 123.7-12.5 203 31 256h102.4l35.9-86.2c5.4-12.9 23.6-13.2 29.4-.4l58.2 129.3 49-97.9c5.9-11.8 22.7-11.8 28.6 0l27.6 55.2H481c43.5-53 41.4-132.3-7.3-182.1z"></path></svg>`
export const icon_fire = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M323.56 51.2c-20.8 19.3-39.58 39.59-56.22 59.97C240.08 73.62 206.28 35.53 168 0 69.74 91.17 0 209.96 0 281.6 0 408.85 100.29 512 224 512s224-103.15 224-230.4c0-53.27-51.98-163.14-124.44-230.4zm-19.47 340.65C282.43 407.01 255.72 416 226.86 416 154.71 416 96 368.26 96 290.75c0-38.61 24.31-72.63 72.79-130.75 6.93 7.98 98.83 125.34 98.83 125.34l58.63-66.88c4.14 6.85 7.91 13.55 11.27 19.97 27.35 52.19 15.81 118.97-33.43 153.42z"></path></svg>`
export const icon_clock = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>`
export const icon_undo = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path></svg>`
export const icon_poweroff = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M400 54.1c63 45 104 118.6 104 201.9 0 136.8-110.8 247.7-247.5 248C120 504.3 8.2 393 8 256.4 7.9 173.1 48.9 99.3 111.8 54.2c11.7-8.3 28-4.8 35 7.7L162.6 90c5.9 10.5 3.1 23.8-6.6 31-41.5 30.8-68 79.6-68 134.9-.1 92.3 74.5 168.1 168 168.1 91.6 0 168.6-74.2 168-169.1-.3-51.8-24.7-101.8-68.1-134-9.7-7.2-12.4-20.5-6.5-30.9l15.8-28.1c7-12.4 23.2-16.1 34.8-7.8zM296 264V24c0-13.3-10.7-24-24-24h-32c-13.3 0-24 10.7-24 24v240c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24z"></path></svg>`
export const icon_expand = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 180V56c0-13.3 10.7-24 24-24h124c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H64v84c0 6.6-5.4 12-12 12H12c-6.6 0-12-5.4-12-12zM288 44v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12V56c0-13.3-10.7-24-24-24H300c-6.6 0-12 5.4-12 12zm148 276h-40c-6.6 0-12 5.4-12 12v84h-84c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24V332c0-6.6-5.4-12-12-12zM160 468v-40c0-6.6-5.4-12-12-12H64v-84c0-6.6-5.4-12-12-12H12c-6.6 0-12 5.4-12 12v124c0 13.3 10.7 24 24 24h124c6.6 0 12-5.4 12-12z"></path></svg>`
export const icon_compress = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path></svg>`
export const icon_bluetooth = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M292.6 171.1L249.7 214l-.3-86 43.2 43.1m-43.2 219.8l43.1-43.1-42.9-42.9-.2 86zM416 259.4C416 465 344.1 512 230.9 512S32 465 32 259.4 115.4 0 228.6 0 416 53.9 416 259.4zm-158.5 0l79.4-88.6L211.8 36.5v176.9L138 139.6l-27 26.9 92.7 93-92.7 93 26.9 26.9 73.8-73.8 2.3 170 127.4-127.5-83.9-88.7z"></path></svg>`

View File

@ -26,15 +26,18 @@ export function createApp () {
power: (value) => Math.round(value),
strokesPerMinute: (value) => Math.round(value)
}
const standalone = (window.location.hash === '#:standalone:')
// const standalone = (window.location.hash === '#:standalone:')
let metricsCallback
const metrics = {
}
/*
if (standalone) {
document.getElementById('close-button').style.display = 'inline-block'
document.getElementById('fullscreen-button').style.display = 'none'
} else {
document.getElementById('fullscreen-button').style.display = 'inline-block'
document.getElementById('close-button').style.display = 'none'
}
} */
let socket
@ -67,23 +70,6 @@ export function createApp () {
try {
const data = JSON.parse(event.data)
// show heart rate, if present
if (data.heartrate !== undefined) {
if (data.heartrate !== 0) {
document.getElementById('heartrate-container').style.display = 'inline-block'
document.getElementById('strokes-total-container').style.display = 'none'
if (data.heartrateBatteryLevel !== undefined) {
document.getElementById('heartrate-battery-container').style.display = 'inline-block'
setHeartrateMonitorBatteryLevel(data.heartrateBatteryLevel)
} else {
document.getElementById('heartrate-battery-container').style.display = 'none'
}
} else {
document.getElementById('strokes-total-container').style.display = 'inline-block'
document.getElementById('heartrate-container').style.display = 'none'
}
}
let activeFields = fields
// if we are in reset state only update heart rate and peripheral mode
if (data.strokesTotal === 0) {
@ -94,13 +80,21 @@ export function createApp () {
if (activeFields.includes(key)) {
const valueFormatted = fieldFormatter[key] ? fieldFormatter[key](value) : value
if (valueFormatted.value !== undefined && valueFormatted.unit !== undefined) {
if (document.getElementById(key)) document.getElementById(key).innerHTML = valueFormatted.value
if (document.getElementById(`${key}Unit`)) document.getElementById(`${key}Unit`).innerHTML = valueFormatted.unit
metrics[key] = {
value: valueFormatted.value,
unit: valueFormatted.unit
}
} else {
if (document.getElementById(key)) document.getElementById(key).innerHTML = valueFormatted
metrics[key] = {
value: valueFormatted
}
}
}
}
if (metricsCallback) {
metricsCallback(metrics)
}
} catch (err) {
console.log(err)
}
@ -125,25 +119,13 @@ export function createApp () {
function resetFields () {
for (const key of fields.filter((elem) => elem !== 'peripheralMode')) {
if (document.getElementById(key)) document.getElementById(key).innerHTML = '--'
}
}
function toggleFullscreen () {
const fullscreenElement = document.getElementById('dashboard')
if (!document.fullscreenElement) {
fullscreenElement.requestFullscreen({ navigationUI: 'hide' })
} else {
if (document.exitFullscreen) {
document.exitFullscreen()
if (metrics[key])metrics[key].value = '--'
if (metricsCallback) {
metricsCallback(metrics)
}
}
}
function close () {
window.close()
}
function reset () {
resetFields()
if (socket)socket.send(JSON.stringify({ command: 'reset' }))
@ -153,17 +135,14 @@ export function createApp () {
if (socket)socket.send(JSON.stringify({ command: 'switchPeripheralMode' }))
}
function setHeartrateMonitorBatteryLevel (batteryLevel) {
if (document.getElementById('battery-level') !== null) {
// 416 is the max width value of the battery bar in the SVG graphic
document.getElementById('battery-level').setAttribute('width', `${batteryLevel * 416 / 100}px`)
}
function setMetricsCallback (callback) {
metricsCallback = callback
}
return {
toggleFullscreen,
reset,
close,
switchPeripheralMode
switchPeripheralMode,
metrics,
setMetricsCallback
}
}

View File

@ -11,7 +11,7 @@ body {
overscroll-behavior: contain;
}
.grid {
performance-dashboard {
display: grid;
height: calc(100vh - 2vw);
padding: 1vw;
@ -20,18 +20,19 @@ body {
grid-template-rows: repeat(2, minmax(0, 1fr));
}
.col {
dashboard-metric, dashboard-actions {
background: #002b57;
text-align: center;
position: relative;
padding: 0.5em 0.2em 0 0.2em;
}
.col.actions {
dashboard-actions {
padding: 0.5em 0 0 0;
}
@media (orientation: portrait) {
.grid {
performance-dashboard {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: repeat(4, minmax(0, 1fr));
}
@ -70,7 +71,6 @@ div.label, div.content {
right: 0.2em;
bottom: 0;
position: absolute;
display: none;
}
.metric-value {
font-size: 150%;
@ -93,13 +93,3 @@ button {
display: inline-block;
width: 3.5em;
}
#close-button, #heartrate-container {
display: none;
}
#heartrate-container, #strokes-total-container {
width: 100%;
height: 100%;
position: relative;
}

232
package-lock.json generated
View File

@ -13,6 +13,7 @@
"@abandonware/noble": "^1.9.2-14",
"ant-plus": "^0.1.24",
"finalhandler": "^1.1.2",
"lit": "^2.0.2",
"loglevel": "^1.8.0",
"nosleep.js": "^0.12.0",
"onoff": "^6.0.3",
@ -21,14 +22,17 @@
"xml2js": "^0.4.23"
},
"devDependencies": {
"@babel/eslint-parser": "^7.16.3",
"@babel/plugin-proposal-class-properties": "^7.16.0",
"@babel/plugin-proposal-decorators": "^7.16.4",
"@snowpack/plugin-babel": "^2.1.7",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-lit": "^1.6.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-wc": "^1.3.2",
"http2-proxy": "^5.0.53",
"markdownlint-cli": "^0.30.0",
"npm-run-all": "^4.1.5",
@ -202,6 +206,33 @@
"node": ">=0.10.0"
}
},
"node_modules/@babel/eslint-parser": {
"version": "7.16.3",
"resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.3.tgz",
"integrity": "sha512-iB4ElZT0jAt7PKVaeVulOECdGe6UnmA/O0P9jlF5g5GBOwDVbna8AXhHRu4s27xQf6OkveyA8iTDv1jHdDejgQ==",
"dev": true,
"dependencies": {
"eslint-scope": "^5.1.1",
"eslint-visitor-keys": "^2.1.0",
"semver": "^6.3.0"
},
"engines": {
"node": "^10.13.0 || ^12.13.0 || >=14.0.0"
},
"peerDependencies": {
"@babel/core": ">=7.11.0",
"eslint": "^7.5.0 || ^8.0.0"
}
},
"node_modules/@babel/eslint-parser/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/generator": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz",
@ -734,6 +765,11 @@
"integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==",
"dev": true
},
"node_modules/@lit/reactive-element": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.0.2.tgz",
"integrity": "sha512-oz3d3MKjQ2tXynQgyaQaMpGTDNyNDeBdo6dXf1AbjTwhA1IRINHmA7kSaVYv9ttKweNkEoNqp9DqteDdgWzPEg=="
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.6.tgz",
@ -1138,6 +1174,11 @@
"@types/node": "*"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -2791,6 +2832,23 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"node_modules/eslint-plugin-lit": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.6.1.tgz",
"integrity": "sha512-BpPoWVhf8dQ/Sz5Pi9NlqbGoH5BcMcVyXhi2XTx2XGMAO9U2lS+GTSsqJjI5hL3OuxCicNiUEWXazAwi9cAGxQ==",
"dev": true,
"dependencies": {
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",
"requireindex": "^1.2.0"
},
"engines": {
"node": ">= 12"
},
"peerDependencies": {
"eslint": ">= 5"
}
},
"node_modules/eslint-plugin-node": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
@ -2860,6 +2918,19 @@
"eslint": "^7.0.0"
}
},
"node_modules/eslint-plugin-wc": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-1.3.2.tgz",
"integrity": "sha512-/Tt3kIXBp1jh06xYtRqPwAvpNxVVk9YtbcFCKEgLa5l3GY+urZyn376pISaaZxkm9HVD3AIPOF5i9/uFwyF0Zw==",
"dev": true,
"dependencies": {
"is-valid-element-name": "^1.0.0",
"js-levenshtein-esm": "^1.2.0"
},
"peerDependencies": {
"eslint": ">=5"
}
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -4127,6 +4198,12 @@
"node": ">=0.10.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true
},
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
@ -4209,6 +4286,15 @@
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"devOptional": true
},
"node_modules/is-valid-element-name": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz",
"integrity": "sha1-Ju8/12zfHxItEFQG4y01sN4AWYE=",
"dev": true,
"dependencies": {
"is-potential-custom-element-name": "^1.0.0"
}
},
"node_modules/is-valid-identifier": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-valid-identifier/-/is-valid-identifier-2.0.2.tgz",
@ -4272,6 +4358,12 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"devOptional": true
},
"node_modules/js-levenshtein-esm": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
"integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
"dev": true
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -4469,6 +4561,33 @@
"uc.micro": "^1.0.1"
}
},
"node_modules/lit": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.0.2.tgz",
"integrity": "sha512-hKA/1YaSB+P+DvKWuR2q1Xzy/iayhNrJ3aveD0OQ9CKn6wUjsdnF/7LavDOJsKP/K5jzW/kXsuduPgRvTFrFJw==",
"dependencies": {
"@lit/reactive-element": "^1.0.0",
"lit-element": "^3.0.0",
"lit-html": "^2.0.0"
}
},
"node_modules/lit-element": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.0.2.tgz",
"integrity": "sha512-9vTJ47D2DSE4Jwhle7aMzEwO2ZcOPRikqfT3CVG7Qol2c9/I4KZwinZNW5Xv8hNm+G/enSSfIwqQhIXi6ioAUg==",
"dependencies": {
"@lit/reactive-element": "^1.0.0",
"lit-html": "^2.0.0"
}
},
"node_modules/lit-html": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.2.tgz",
"integrity": "sha512-dON7Zg8btb14/fWohQLQBdSgkoiQA4mIUy87evmyJHtxRq7zS6LlC32bT5EPWiof5PUQaDpF45v2OlrxHA5Clg==",
"dependencies": {
"@types/trusted-types": "^2.0.2"
}
},
"node_modules/load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
@ -6401,6 +6520,15 @@
"node": ">=0.10.0"
}
},
"node_modules/requireindex": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
"integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
"dev": true,
"engines": {
"node": ">=0.10.5"
}
},
"node_modules/resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@ -8222,6 +8350,25 @@
}
}
},
"@babel/eslint-parser": {
"version": "7.16.3",
"resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.3.tgz",
"integrity": "sha512-iB4ElZT0jAt7PKVaeVulOECdGe6UnmA/O0P9jlF5g5GBOwDVbna8AXhHRu4s27xQf6OkveyA8iTDv1jHdDejgQ==",
"dev": true,
"requires": {
"eslint-scope": "^5.1.1",
"eslint-visitor-keys": "^2.1.0",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"@babel/generator": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz",
@ -8632,6 +8779,11 @@
"integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==",
"dev": true
},
"@lit/reactive-element": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.0.2.tgz",
"integrity": "sha512-oz3d3MKjQ2tXynQgyaQaMpGTDNyNDeBdo6dXf1AbjTwhA1IRINHmA7kSaVYv9ttKweNkEoNqp9DqteDdgWzPEg=="
},
"@mapbox/node-pre-gyp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.6.tgz",
@ -8978,6 +9130,11 @@
"@types/node": "*"
}
},
"@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -10256,6 +10413,17 @@
}
}
},
"eslint-plugin-lit": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.6.1.tgz",
"integrity": "sha512-BpPoWVhf8dQ/Sz5Pi9NlqbGoH5BcMcVyXhi2XTx2XGMAO9U2lS+GTSsqJjI5hL3OuxCicNiUEWXazAwi9cAGxQ==",
"dev": true,
"requires": {
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",
"requireindex": "^1.2.0"
}
},
"eslint-plugin-node": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
@ -10301,6 +10469,16 @@
"dev": true,
"requires": {}
},
"eslint-plugin-wc": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-1.3.2.tgz",
"integrity": "sha512-/Tt3kIXBp1jh06xYtRqPwAvpNxVVk9YtbcFCKEgLa5l3GY+urZyn376pISaaZxkm9HVD3AIPOF5i9/uFwyF0Zw==",
"dev": true,
"requires": {
"is-valid-element-name": "^1.0.0",
"js-levenshtein-esm": "^1.2.0"
}
},
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -11250,6 +11428,12 @@
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"dev": true
},
"is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true
},
"is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
@ -11305,6 +11489,15 @@
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"devOptional": true
},
"is-valid-element-name": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz",
"integrity": "sha1-Ju8/12zfHxItEFQG4y01sN4AWYE=",
"dev": true,
"requires": {
"is-potential-custom-element-name": "^1.0.0"
}
},
"is-valid-identifier": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-valid-identifier/-/is-valid-identifier-2.0.2.tgz",
@ -11356,6 +11549,12 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"devOptional": true
},
"js-levenshtein-esm": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
"integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -11523,6 +11722,33 @@
"uc.micro": "^1.0.1"
}
},
"lit": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.0.2.tgz",
"integrity": "sha512-hKA/1YaSB+P+DvKWuR2q1Xzy/iayhNrJ3aveD0OQ9CKn6wUjsdnF/7LavDOJsKP/K5jzW/kXsuduPgRvTFrFJw==",
"requires": {
"@lit/reactive-element": "^1.0.0",
"lit-element": "^3.0.0",
"lit-html": "^2.0.0"
}
},
"lit-element": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.0.2.tgz",
"integrity": "sha512-9vTJ47D2DSE4Jwhle7aMzEwO2ZcOPRikqfT3CVG7Qol2c9/I4KZwinZNW5Xv8hNm+G/enSSfIwqQhIXi6ioAUg==",
"requires": {
"@lit/reactive-element": "^1.0.0",
"lit-html": "^2.0.0"
}
},
"lit-html": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.2.tgz",
"integrity": "sha512-dON7Zg8btb14/fWohQLQBdSgkoiQA4mIUy87evmyJHtxRq7zS6LlC32bT5EPWiof5PUQaDpF45v2OlrxHA5Clg==",
"requires": {
"@types/trusted-types": "^2.0.2"
}
},
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
@ -13017,6 +13243,12 @@
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
"requireindex": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
"integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
"dev": true
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",

View File

@ -16,7 +16,7 @@
"scripts": {
"lint": "eslint ./app ./config && markdownlint '**/*.md' --ignore node_modules",
"start": "node app/server.js",
"dev": "npm-run-all --parallel start build:watch",
"dev": "npm-run-all --parallel start dev:frontend",
"dev:frontend": "snowpack dev",
"build": "snowpack build",
"build:watch": "snowpack build --watch",
@ -30,6 +30,7 @@
"@abandonware/noble": "^1.9.2-14",
"ant-plus": "^0.1.24",
"finalhandler": "^1.1.2",
"lit": "^2.0.2",
"loglevel": "^1.8.0",
"nosleep.js": "^0.12.0",
"onoff": "^6.0.3",
@ -38,14 +39,17 @@
"xml2js": "^0.4.23"
},
"devDependencies": {
"@babel/eslint-parser": "^7.16.3",
"@babel/plugin-proposal-class-properties": "^7.16.0",
"@babel/plugin-proposal-decorators": "^7.16.4",
"@snowpack/plugin-babel": "^2.1.7",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-lit": "^1.6.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-wc": "^1.3.2",
"http2-proxy": "^5.0.53",
"markdownlint-cli": "^0.30.0",
"npm-run-all": "^4.1.5",

View File

@ -1,18 +1,25 @@
// Snowpack Configuration File
// See all supported options: https://www.snowpack.dev/reference/configuration
import proxy from 'http2-proxy'
import { nodeResolve } from '@rollup/plugin-node-resolve'
// todo: might add a proxy for websockets here so we can use snowpack dev server with HMR
export default {
mount: {
// the web frontend is located in this directory
'./app/client': { url: '/' }
// mount "public" to the root URL path ("/*") and serve files with zero transformations:
// './public': { url: '/', static: true, resolve: false }
},
plugins: ['@snowpack/plugin-babel'],
packageOptions: {
/* ... */
rollup: {
plugins: [
// todo: related to the lit documentation this should enable development mode
// unfortunately this currently does not seem to work
nodeResolve({
exportConditions: ['development'],
dedupe: true
})
]
}
},
devOptions: {
open: 'none',