forked from btclock/webui
Initial commit
This commit is contained in:
commit
96d609a89e
34 changed files with 4978 additions and 0 deletions
13
.eslintignore
Normal file
13
.eslintignore
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Ignore files for PNPM, NPM and YARN
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
30
.eslintrc.cjs
Normal file
30
.eslintrc.cjs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:svelte/recommended',
|
||||||
|
'prettier'
|
||||||
|
],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
extraFileExtensions: ['.svelte']
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2017: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.svelte'],
|
||||||
|
parser: 'svelte-eslint-parser',
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
build
|
||||||
|
build_gz
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
engine-strict=true
|
13
.prettierignore
Normal file
13
.prettierignore
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Ignore files for PNPM, NPM and YARN
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
8
.prettierrc
Normal file
8
.prettierrc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 100,
|
||||||
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||||
|
}
|
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"i18n-ally.localesPaths": [
|
||||||
|
"src/lib/locales"
|
||||||
|
],
|
||||||
|
"i18n-ally.keystyle": "nested"
|
||||||
|
}
|
38
README.md
Normal file
38
README.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# create-svelte
|
||||||
|
|
||||||
|
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||||
|
|
||||||
|
## Creating a project
|
||||||
|
|
||||||
|
If you're seeing this, you've probably already done this step. Congrats!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a new project in the current directory
|
||||||
|
npm create svelte@latest
|
||||||
|
|
||||||
|
# create a new project in my-app
|
||||||
|
npm create svelte@latest my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
npm run dev -- --open
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To create a production version of your app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
|
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
29
gzip_build.py
Normal file
29
gzip_build.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import os
|
||||||
|
import gzip
|
||||||
|
from shutil import copyfileobj
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def gzip_file(input_file, output_file):
|
||||||
|
with open(input_file, 'rb') as f_in:
|
||||||
|
with gzip.open(output_file, 'wb') as f_out:
|
||||||
|
copyfileobj(f_in, f_out)
|
||||||
|
|
||||||
|
def process_directory(input_dir, output_dir):
|
||||||
|
for root, dirs, files in os.walk(input_dir):
|
||||||
|
relative_path = os.path.relpath(root, input_dir)
|
||||||
|
output_root = os.path.join(output_dir, relative_path)
|
||||||
|
|
||||||
|
Path(output_root).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
# if file.endswith(('.html', '.css', '.js')):
|
||||||
|
input_file_path = os.path.join(root, file)
|
||||||
|
output_file_path = os.path.join(output_root, file + '.gz')
|
||||||
|
gzip_file(input_file_path, output_file_path)
|
||||||
|
print(f'Compressed: {input_file_path} -> {output_file_path}')
|
||||||
|
|
||||||
|
# Replace 'input_directory' and 'output_directory' with your actual input and output directories
|
||||||
|
input_directory = 'build'
|
||||||
|
output_directory = 'build_gz'
|
||||||
|
|
||||||
|
process_directory(input_directory, output_directory)
|
44
package.json
Normal file
44
package.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "btclock",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"lint": "prettier --check . && eslint .",
|
||||||
|
"format": "prettier --write ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-json": "^6.0.1",
|
||||||
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
|
"@sveltejs/kit": "^1.27.4",
|
||||||
|
"@types/swagger-ui": "^3.52.4",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
|
"eslint": "^8.28.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-svelte": "^2.30.0",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
|
"prettier-plugin-svelte": "^3.0.0",
|
||||||
|
"sass": "^1.69.5",
|
||||||
|
"svelte": "^4.0.5",
|
||||||
|
"svelte-check": "^3.6.0",
|
||||||
|
"tslib": "^2.4.1",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vite": "^4.4.2"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"@fontsource/antonio": "^5.0.17",
|
||||||
|
"@fontsource/oswald": "^5.0.17",
|
||||||
|
"@fontsource/ubuntu": "^5.0.8",
|
||||||
|
"bootstrap": "^5.3.2",
|
||||||
|
"svelte-i18n": "^4.0.0",
|
||||||
|
"sveltestrap": "^5.11.2",
|
||||||
|
"swagger-ui": "^5.10.0"
|
||||||
|
}
|
||||||
|
}
|
12
src/app.d.ts
vendored
Normal file
12
src/app.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
|
// for information about these interfaces
|
||||||
|
declare global {
|
||||||
|
namespace App {
|
||||||
|
// interface Error {}
|
||||||
|
// interface Locals {}
|
||||||
|
// interface PageData {}
|
||||||
|
// interface Platform {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
12
src/app.html
Normal file
12
src/app.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
src/hooks.server.ts
Normal file
10
src/hooks.server.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Handle } from '@sveltejs/kit'
|
||||||
|
import { locale } from 'svelte-i18n'
|
||||||
|
|
||||||
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
const lang = event.request.headers.get('accept-language')?.split(',')[0]
|
||||||
|
if (lang) {
|
||||||
|
locale.set(lang)
|
||||||
|
}
|
||||||
|
return resolve(event)
|
||||||
|
}
|
13
src/lib/i18n/index.ts
Normal file
13
src/lib/i18n/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { browser } from '$app/environment'
|
||||||
|
import { init, register } from 'svelte-i18n'
|
||||||
|
|
||||||
|
const defaultLocale = 'en'
|
||||||
|
|
||||||
|
register('en', () => import('../locales/en.json'))
|
||||||
|
register('nl', () => import('../locales/nl.json'))
|
||||||
|
register('es', () => import('../locales/es.json'))
|
||||||
|
|
||||||
|
init({
|
||||||
|
fallbackLocale: defaultLocale,
|
||||||
|
initialLocale: browser ? window.navigator.language : defaultLocale,
|
||||||
|
})
|
1
src/lib/index.ts
Normal file
1
src/lib/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// place files you want to import through the `$lib` alias in this folder.
|
12
src/lib/locales/en.json
Normal file
12
src/lib/locales/en.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"my": {
|
||||||
|
"translation": {
|
||||||
|
"key": "Test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"section": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/lib/locales/es.json
Normal file
7
src/lib/locales/es.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"section": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Configuración"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/lib/locales/nl.json
Normal file
7
src/lib/locales/nl.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"section": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Instellingen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
200
src/lib/style/app.scss
Normal file
200
src/lib/style/app.scss
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
// @use "../node_modules/@fontsource/antonio/scss/mixins" as Antonio;
|
||||||
|
// $directory: "../node_modules/@fontsource/ubuntu";
|
||||||
|
// @use "../node_modules/@fontsource/ubuntu/scss/mixins" as Ubuntu;
|
||||||
|
|
||||||
|
|
||||||
|
// @import "../node_modules/bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
|
@import "../node_modules/bootstrap/scss/functions";
|
||||||
|
@import "../node_modules/bootstrap/scss/variables";
|
||||||
|
@import "../node_modules/bootstrap/scss/variables-dark";
|
||||||
|
|
||||||
|
@import "@fontsource/antonio/latin-400.css";
|
||||||
|
@import "@fontsource/ubuntu/latin-400.css";
|
||||||
|
@import "@fontsource/oswald/latin-400.css";
|
||||||
|
|
||||||
|
$form-range-track-bg: #fff;
|
||||||
|
$color-mode-type: media-query;
|
||||||
|
$font-family-base: "Ubuntu";
|
||||||
|
|
||||||
|
// $border-radius: .675rem;
|
||||||
|
|
||||||
|
@import "../node_modules/bootstrap/scss/mixins";
|
||||||
|
@import "../node_modules/bootstrap/scss/maps";
|
||||||
|
@import "../node_modules/bootstrap/scss/utilities";
|
||||||
|
|
||||||
|
@import "../node_modules/bootstrap/scss/root";
|
||||||
|
@import "../node_modules/bootstrap/scss/reboot";
|
||||||
|
@import "../node_modules/bootstrap/scss/type";
|
||||||
|
@import "../node_modules/bootstrap/scss/containers";
|
||||||
|
@import "../node_modules/bootstrap/scss/grid";
|
||||||
|
@import "../node_modules/bootstrap/scss/forms";
|
||||||
|
@import "../node_modules/bootstrap/scss/buttons";
|
||||||
|
@import "../node_modules/bootstrap/scss/button-group";
|
||||||
|
@import "../node_modules/bootstrap/scss/dropdown";
|
||||||
|
|
||||||
|
@import "../node_modules/bootstrap/scss/navbar";
|
||||||
|
@import "../node_modules/bootstrap/scss/nav";
|
||||||
|
@import "../node_modules/bootstrap/scss/card";
|
||||||
|
@import "../node_modules/bootstrap/scss/progress";
|
||||||
|
|
||||||
|
@import "../node_modules/bootstrap/scss/helpers";
|
||||||
|
@import "../node_modules/bootstrap/scss/utilities/api";
|
||||||
|
|
||||||
|
@include media-breakpoint-down(xl) {
|
||||||
|
html {
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.btn,
|
||||||
|
input[type="button"].btn,
|
||||||
|
input[type="submit"].btn,
|
||||||
|
input[type="reset"].btn {
|
||||||
|
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius-sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-down(lg) {
|
||||||
|
html {
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nav {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splitText div:first-child::after {
|
||||||
|
display: block;
|
||||||
|
content: '';
|
||||||
|
margin-top: 0px;
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#btcclock-wrapper {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btclock {
|
||||||
|
border: 1px solid darkgray;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.digit,
|
||||||
|
.splitText,
|
||||||
|
.mediumText {
|
||||||
|
border: 2px solid gold;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
min-width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(xxl) {
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btclock {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
align-content: stretch;
|
||||||
|
font-family: 'Oswald', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btclock>div {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fg-ffff .btclock>div {
|
||||||
|
color: #fff;
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-ffff .btclock>div {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fg-f800 .btclock>div {
|
||||||
|
color: #f00;
|
||||||
|
border-color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-f800 .btclock>div {
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fg-0 .btclock>div {
|
||||||
|
color: #000;
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-0 .btclock>div {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splitText {
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 1.0rem;
|
||||||
|
padding-top: 8px !important;
|
||||||
|
padding-bottom: 9px !important;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-up(xxl) {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
padding-top: 19px !important;
|
||||||
|
padding-bottom: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediumText {
|
||||||
|
font-size: 3rem;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-top: 20px !important;
|
||||||
|
padding-bottom: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.digit {
|
||||||
|
font-size: 5rem;
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-up(xxl) {
|
||||||
|
font-size: 5rem;
|
||||||
|
}
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.digit-blank {
|
||||||
|
content: "abc";
|
||||||
|
}
|
||||||
|
|
||||||
|
#customText {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggleTimerArea {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system_info {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
}
|
47
src/routes/+layout.svelte
Normal file
47
src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
import {
|
||||||
|
Navbar,
|
||||||
|
NavbarBrand,
|
||||||
|
Nav,
|
||||||
|
NavItem,
|
||||||
|
NavLink,
|
||||||
|
Collapse,
|
||||||
|
Dropdown,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownItem,
|
||||||
|
DropdownToggle
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import { locale, locales } from 'svelte-i18n';
|
||||||
|
|
||||||
|
export const setLocale = (lang: string) => () => {
|
||||||
|
locale.set(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Navbar expand="md">
|
||||||
|
<NavbarBrand>₿TClock</NavbarBrand>
|
||||||
|
<Collapse navbar expand="md">
|
||||||
|
<Nav class="me-auto" navbar>
|
||||||
|
<NavItem>
|
||||||
|
<NavLink href="/">Home</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
<NavItem>
|
||||||
|
<NavLink href="/api">API</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
</Nav>
|
||||||
|
<Dropdown inNavbar>
|
||||||
|
<DropdownToggle nav caret>{$locale}</DropdownToggle>
|
||||||
|
<DropdownMenu end>
|
||||||
|
{#each $locales as locale}
|
||||||
|
<DropdownItem on:click={setLocale(locale)}>{locale}</DropdownItem>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
</Collapse>
|
||||||
|
</Navbar>
|
||||||
|
|
||||||
|
<!-- +layout.svelte -->
|
||||||
|
<slot />
|
16
src/routes/+layout.ts
Normal file
16
src/routes/+layout.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import "$lib/style/app.scss";
|
||||||
|
|
||||||
|
|
||||||
|
import { browser } from '$app/environment'
|
||||||
|
import '$lib/i18n' // Import to initialize. Important :)
|
||||||
|
import { locale, waitLocale } from 'svelte-i18n'
|
||||||
|
import type { LayoutLoad } from './$types'
|
||||||
|
|
||||||
|
export const load: LayoutLoad = async () => {
|
||||||
|
if (browser) {
|
||||||
|
locale.set(window.navigator.language)
|
||||||
|
}
|
||||||
|
await waitLocale()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const prerender = true;
|
32
src/routes/+page.svelte
Normal file
32
src/routes/+page.svelte
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { PUBLIC_BASE_URL } from '$env/static/public';
|
||||||
|
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { Col, Container, Row } from 'sveltestrap';
|
||||||
|
|
||||||
|
import Control from './Control.svelte';
|
||||||
|
import Status from './Status.svelte';
|
||||||
|
import Settings from './Settings.svelte';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let settings = writable({});
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
fetch( PUBLIC_BASE_URL + `/api/settings`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
data.fgColor = String(data.fgColor);
|
||||||
|
data.bgColor = String(data.bgColor);
|
||||||
|
settings.set(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<Control bind:settings></Control>
|
||||||
|
<Status bind:settings></Status>
|
||||||
|
<Settings bind:settings></Settings>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
71
src/routes/Control.svelte
Normal file
71
src/routes/Control.svelte
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { PUBLIC_BASE_URL } from '$env/static/public';
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
Col,
|
||||||
|
Container,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Row
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
export let settings = {};
|
||||||
|
export let customText:String;
|
||||||
|
|
||||||
|
const setCustomText = () => {
|
||||||
|
fetch(`${PUBLIC_BASE_URL}/api/show/text/${customText}`).catch(err => { });
|
||||||
|
};
|
||||||
|
|
||||||
|
const setLEDcolor = () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<h2>{$_('section.control.title', { default: 'Control' })}</h2>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<Form>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="customText" size="sm">Text</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input type="text" id="customText"bind:value={customText} bsSize="sm" maxLength="{$settings.numScreens}"/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Button color="primary" on:click={setCustomText}>Show text</Button>
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
<hr />
|
||||||
|
<h3>LEDs</h3>
|
||||||
|
<Form>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="ledColorPicker" size="sm">LEDs color</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input type="color" id="ledColorPicker" />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Button color="secondary" id="turnOffLedsBtn">Turn off</Button>
|
||||||
|
<Button color="primary">Set color</Button>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<h3>System info</h3>
|
||||||
|
<ul class="small system_info">
|
||||||
|
<li>Version: {$settings.gitRev}</li>
|
||||||
|
<li>Build time: {new Date(($settings.lastBuildTime * 1000)).toLocaleString()}</li>
|
||||||
|
<li>IP: {$settings.ip}</li>
|
||||||
|
<li>Hostname: {$settings.hostname}</li>
|
||||||
|
</ul>
|
||||||
|
<Button color="danger" id="restartBtn">Restart</Button>
|
||||||
|
<Button color="warning" id="forceFullRefresh">Force full refresh</Button>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
0
src/routes/Control.ts
Normal file
0
src/routes/Control.ts
Normal file
25
src/routes/Rendered.svelte
Normal file
25
src/routes/Rendered.svelte
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let status = {};
|
||||||
|
|
||||||
|
const isSplitText = (str:String) => {
|
||||||
|
return str.includes("/");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="btcclock-wrapper" id="btcclock-wrapper">
|
||||||
|
<div class="btclock">
|
||||||
|
{#each status.data as char}
|
||||||
|
{#if isSplitText(char)}
|
||||||
|
<div class="splitText">
|
||||||
|
{#each char.split("/") as part}
|
||||||
|
<div class="flex-items">{part}</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else if char.length === 0 || char === " "}
|
||||||
|
<div class="digit"> </div>
|
||||||
|
{:else}
|
||||||
|
<div class="digit">{char}</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
155
src/routes/Settings.svelte
Normal file
155
src/routes/Settings.svelte
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { readonly, writable } from 'svelte/store';
|
||||||
|
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import {
|
||||||
|
Col,
|
||||||
|
Container,
|
||||||
|
Row,
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardBody,
|
||||||
|
Form,
|
||||||
|
FormGroup,
|
||||||
|
FormText,
|
||||||
|
Label,
|
||||||
|
Input,
|
||||||
|
InputGroup,
|
||||||
|
InputGroupText,
|
||||||
|
Button
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
export let settings;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<h2>{$_('section.settings.title', { default: 'Settings' })}</h2>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<Form>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="fgColor" size="sm">Text color</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input
|
||||||
|
type="select"
|
||||||
|
value={$settings.fgColor}
|
||||||
|
name="select"
|
||||||
|
id="fgColor"
|
||||||
|
bsSize="sm"
|
||||||
|
class="form-select-sm"
|
||||||
|
>
|
||||||
|
<option value="0">Black</option>
|
||||||
|
<option value="65535">White</option>
|
||||||
|
</Input>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="bgColor" size="sm">Background color</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input
|
||||||
|
type="select"
|
||||||
|
bind:value={$settings.bgColor}
|
||||||
|
name="select"
|
||||||
|
id="bgColor"
|
||||||
|
bsSize="sm"
|
||||||
|
class="form-select-sm"
|
||||||
|
>
|
||||||
|
<option value="0">Black</option>
|
||||||
|
<option value="65535">White</option>
|
||||||
|
</Input>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="timePerScreen" size="sm">Time per screen</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<InputGroup size="sm">
|
||||||
|
<Input type="number" min={1} step="1" bind:value={$settings.timerSeconds} />
|
||||||
|
<InputGroupText>minutes</InputGroupText>
|
||||||
|
</InputGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="fullRefreshMin" size="sm">Full refresh every</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<InputGroup size="sm">
|
||||||
|
<Input type="number" min={1} step="1" bind:value={$settings.fullRefreshMin} />
|
||||||
|
<InputGroupText>minutes</InputGroupText>
|
||||||
|
</InputGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="minSecPriceUpd" size="sm">Time between price updates</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<InputGroup size="sm">
|
||||||
|
<Input type="number" min={1} step="1" bind:value={$settings.minSecPriceUpd} />
|
||||||
|
<InputGroupText>seconds</InputGroupText>
|
||||||
|
</InputGroup>
|
||||||
|
<FormText>Short amounts might shorten lifespan.</FormText>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="tzOffset" size="sm">Timezone offset</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<InputGroup size="sm">
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
step="1"
|
||||||
|
name="tzOffset"
|
||||||
|
id="tzOffset"
|
||||||
|
bind:value={$settings.tzOffset}
|
||||||
|
/>
|
||||||
|
<InputGroupText>minutes</InputGroupText>
|
||||||
|
</InputGroup>
|
||||||
|
<FormText>A restart is required to apply TZ offset.</FormText>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="ledBrightness" size="sm">LED brightness</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input
|
||||||
|
type="range"
|
||||||
|
name="ledBrightness"
|
||||||
|
id="ledBrightness"
|
||||||
|
bind:value={$settings.ledBrightness}
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="mempoolInstance" size="sm">Mempool Instance</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
bind:value={$settings.mempoolInstance}
|
||||||
|
name="mempoolInstance"
|
||||||
|
id="mempoolInstance"
|
||||||
|
bsSize="sm"
|
||||||
|
>
|
||||||
|
</Input>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Label md={6} for="hostnamePrefix" size="sm">Hostname prefix</Label>
|
||||||
|
<Col md="6">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
bind:value={$settings.hostnamePrefix}
|
||||||
|
name="hostnamePrefix"
|
||||||
|
id="hostnamePrefix"
|
||||||
|
bsSize="sm"
|
||||||
|
>
|
||||||
|
</Input>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Button color="secondary">Reset</Button>
|
||||||
|
<Button color="primary">Save</Button>
|
||||||
|
</Form>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
131
src/routes/Status.svelte
Normal file
131
src/routes/Status.svelte
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { PUBLIC_BASE_URL } from '$env/static/public';
|
||||||
|
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import { Button, ButtonGroup, Card, CardBody, CardHeader, Col, Progress } from 'sveltestrap';
|
||||||
|
import Rendered from './Rendered.svelte';
|
||||||
|
|
||||||
|
|
||||||
|
const status = writable({
|
||||||
|
data: ["L", "O", "A", "D", "I", "N", "G"],
|
||||||
|
espFreeHeap: 0,
|
||||||
|
espHeapSize: 0,
|
||||||
|
connectionStatus: {
|
||||||
|
"price": false,
|
||||||
|
"blocks": false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
fetch(`${PUBLIC_BASE_URL}/api/status`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
status.set(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
|
||||||
|
|
||||||
|
evtSource.addEventListener('status', (e) => {
|
||||||
|
let dataObj = (JSON.parse(e.data));
|
||||||
|
status.set(dataObj);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const toTime = (secs:Number) => {
|
||||||
|
var hours = Math.floor(secs / (60 * 60));
|
||||||
|
|
||||||
|
var divisor_for_minutes = secs % (60 * 60);
|
||||||
|
var minutes = Math.floor(divisor_for_minutes / 60);
|
||||||
|
|
||||||
|
var divisor_for_seconds = divisor_for_minutes % 60;
|
||||||
|
var seconds = Math.ceil(divisor_for_seconds);
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
"h": hours,
|
||||||
|
"m": minutes,
|
||||||
|
"s": seconds
|
||||||
|
};
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toUptimeString = (secs:Number):String => {
|
||||||
|
let time = toTime(secs);
|
||||||
|
|
||||||
|
return `${time.h}h ${time.m}m ${time.s}s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let memoryFreePercent:number = 50;
|
||||||
|
|
||||||
|
status.subscribe((value) => {
|
||||||
|
memoryFreePercent = Math.round(value.espFreeHeap / value.espHeapSize * 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
const setScreen = (id:number) => () => {
|
||||||
|
fetch(`${PUBLIC_BASE_URL}/api/show/screen/${id}`).catch(err => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleTimer = (currentStatus:boolean) => () => {
|
||||||
|
if (currentStatus) {
|
||||||
|
fetch(`${PUBLIC_BASE_URL}/api/action/pause`);
|
||||||
|
} else {
|
||||||
|
fetch(`${PUBLIC_BASE_URL}/api/action/timer_restart`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let settings;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<h2>{$_('section.status.title', { default: 'Status' })}</h2>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
{#if $settings.screens}
|
||||||
|
<ButtonGroup size="sm">
|
||||||
|
{#each $settings.screens as s}
|
||||||
|
<Button color="outline-primary" active={$status.currentScreen == s.id} on:click={setScreen(s.id)}>{s.name}</Button>
|
||||||
|
{/each}
|
||||||
|
</ButtonGroup>
|
||||||
|
<hr>
|
||||||
|
{#if $status.data}
|
||||||
|
<Rendered status="{$status}"></Rendered>
|
||||||
|
Screen cycle: {#if status.timerRunning}⏵ running{:else}⏸ stopped{/if}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
<hr>
|
||||||
|
<Progress striped value={memoryFreePercent}>{ memoryFreePercent }%</Progress>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div>Memory free </div>
|
||||||
|
<div>{ Math.round($status.espFreeHeap / 1024) } / { Math.round($status.espHeapSize / 1024) } KiB</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
Uptime: {toUptimeString($status.espUptime)}
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
WS Price connection:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.price}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
-
|
||||||
|
WS Mempool.space connection:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span><br>
|
||||||
|
<small>If you use "Fetch € price" the WS Price connection will show ❌ since it uses another data source.</small>
|
||||||
|
</p>
|
||||||
|
</CardBody>
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
</Col>
|
24
src/routes/api/+page.svelte
Normal file
24
src/routes/api/+page.svelte
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { Col, Container, Row } from 'sveltestrap';
|
||||||
|
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import * as swaggerJson from './swagger.json';
|
||||||
|
//import SwaggerUI from 'swagger-ui';
|
||||||
|
//import 'swagger-ui/dist/swagger-ui.css';
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
// SwaggerUI({
|
||||||
|
// spec: swaggerJson,
|
||||||
|
// dom_id: '#swagger-ui-container'
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>API playground</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<Container fluid>
|
||||||
|
<div id="swagger-ui-container" />
|
||||||
|
</Container>
|
345
src/routes/api/swagger.json
Normal file
345
src/routes/api/swagger.json
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {
|
||||||
|
"title": "BTClock API",
|
||||||
|
"version": "3.0",
|
||||||
|
"description": "BTClock V3 API"
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "/api/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/status": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"summary": "Get current status",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/system_status": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"summary": "Get system status",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/settings": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"summary": "Get current settings",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"summary": "Save current settings",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/action/pause": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"timer"
|
||||||
|
],
|
||||||
|
"summary": "Pause screen rotation",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/action/timer_restart": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"timer"
|
||||||
|
],
|
||||||
|
"summary": "Restart screen rotation",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/show/screen/{id}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"screens"
|
||||||
|
],
|
||||||
|
"summary": "Set screen to show",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "id",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"description": "ID of screen to show"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/show/text/{text}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"screens"
|
||||||
|
],
|
||||||
|
"summary": "Set text to show",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "text",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "text"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"description": "Text to show"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/show/custom": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"screens"
|
||||||
|
],
|
||||||
|
"summary": "Set text to show (advanced)",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/CustomText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/full_refresh": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"summary": "Force full refresh of all displays",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/lights/{color}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"lights"
|
||||||
|
],
|
||||||
|
"summary": "Turn on LEDs with specific color",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "color",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "FFCC00"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"description": "Color in RGB hex"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/lights/off": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"lights"
|
||||||
|
],
|
||||||
|
"summary": "Turn LEDs off",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/restart": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"summary": "Restart BTClock",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Settings": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fetchEurPrice": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Fetch EUR price instead of USD"
|
||||||
|
},
|
||||||
|
"fgColor": {
|
||||||
|
"type": "string",
|
||||||
|
"default": 16777215,
|
||||||
|
"description": "ePaper foreground (text) color"
|
||||||
|
},
|
||||||
|
"bgColor": {
|
||||||
|
"type": "string",
|
||||||
|
"default": 0,
|
||||||
|
"description": "ePaper background color"
|
||||||
|
},
|
||||||
|
"ledTestOnPower": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Do LED test on power-on"
|
||||||
|
},
|
||||||
|
"ledFlashOnUpd": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Flash LEDs on new block"
|
||||||
|
},
|
||||||
|
"mdnsEnabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Enable mDNS"
|
||||||
|
},
|
||||||
|
"otaEnabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Enable over-the-air updates"
|
||||||
|
},
|
||||||
|
"stealFocusOnBlock": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Steal focus on new block"
|
||||||
|
},
|
||||||
|
"mcapBigChar": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use big characters for market cap screen"
|
||||||
|
},
|
||||||
|
"mempoolInstance": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "mempool.space",
|
||||||
|
"description": "Mempool.space instance to connect to"
|
||||||
|
},
|
||||||
|
"ledBrightness": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 128,
|
||||||
|
"description": "Brightness of LEDs"
|
||||||
|
},
|
||||||
|
"fullRefreshMin": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 60,
|
||||||
|
"description": "Full refresh time of ePaper displays in minutes"
|
||||||
|
},
|
||||||
|
"screen[0]": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"screen[1]": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"screen[2]": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"screen[3]": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"screen[4]": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"screen[5]": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"tzOffset": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 60,
|
||||||
|
"description": "Timezone offset in minutes"
|
||||||
|
},
|
||||||
|
"minSecPriceUpd": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 30,
|
||||||
|
"description": "Minimum time between price updates in seconds"
|
||||||
|
},
|
||||||
|
"timePerScreen": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 30,
|
||||||
|
"description": "Time between screens when rotating in minutes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CustomText": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"minItems": 7,
|
||||||
|
"maxItems": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
static/favicon.png
Normal file
BIN
static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
28
svelte.config.js
Normal file
28
svelte.config.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import adapter from '@sveltejs/adapter-static';
|
||||||
|
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: vitePreprocess({
|
||||||
|
|
||||||
|
}),
|
||||||
|
|
||||||
|
kit: {
|
||||||
|
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||||
|
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||||
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
|
adapter: adapter({
|
||||||
|
// default options are shown. On some platforms
|
||||||
|
// these options are set automatically — see below
|
||||||
|
pages: 'build',
|
||||||
|
assets: 'build',
|
||||||
|
fallback: undefined,
|
||||||
|
precompress: false,
|
||||||
|
strict: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
}
|
||||||
|
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||||
|
//
|
||||||
|
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||||
|
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||||
|
}
|
9
vite.config.ts
Normal file
9
vite.config.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [sveltekit()],
|
||||||
|
build: {
|
||||||
|
minify: true
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in a new issue