Compare commits
No commits in common. "main" and "main" have entirely different histories.
52 changed files with 1759 additions and 2908 deletions
|
@ -1,9 +1,4 @@
|
||||||
on:
|
on: [push]
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-changes:
|
check-changes:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
|
@ -47,8 +42,7 @@ jobs:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/pip
|
~/.cache/pip
|
||||||
~/node_modules
|
~/node_modules
|
||||||
~/.cache/ms-playwright
|
key: ${{ runner.os }}-pio
|
||||||
key: ${{ runner.os }}-pio-playwright-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '>=3.10'
|
python-version: '>=3.10'
|
||||||
|
@ -68,19 +62,14 @@ jobs:
|
||||||
- name: Run vitest tests
|
- name: Run vitest tests
|
||||||
run: yarn vitest run
|
run: yarn vitest run
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
run: npx playwright install --with-deps
|
run: npx playwright install --with-deps
|
||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
run: npx playwright test
|
run: npx playwright test
|
||||||
- name: Build WebUI
|
- name: Build WebUI
|
||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
# The following steps only run on push to main
|
|
||||||
- name: Get current block
|
- name: Get current block
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
||||||
id: getBlockHeight
|
id: getBlockHeight
|
||||||
run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT
|
run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Write block height to file
|
- name: Write block height to file
|
||||||
env:
|
env:
|
||||||
BLOCK_HEIGHT: ${{ steps.getBlockHeight.outputs.blockHeight }}
|
BLOCK_HEIGHT: ${{ steps.getBlockHeight.outputs.blockHeight }}
|
||||||
|
@ -105,14 +94,12 @@ jobs:
|
||||||
echo "Directory size is within the threshold $DIRECTORY_SIZE"
|
echo "Directory size is within the threshold $DIRECTORY_SIZE"
|
||||||
fi
|
fi
|
||||||
- name: Create tarball
|
- name: Create tarball
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
||||||
run: tar czf webui.tgz --strip-components=1 dist
|
run: tar czf webui.tgz --strip-components=1 dist
|
||||||
- name: Build LittleFS
|
- name: Build LittleFS
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
/tmp/mklittlefs/mklittlefs -c build_gz -s 410000 output/littlefs.bin
|
/tmp/mklittlefs/mklittlefs -c build_gz -s 410000 output/littlefs.bin
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
||||||
uses: https://code.forgejo.org/forgejo/upload-artifact@v4
|
uses: https://code.forgejo.org/forgejo/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
|
@ -120,7 +107,7 @@ jobs:
|
||||||
output/littlefs.bin
|
output/littlefs.bin
|
||||||
- name: Create release
|
- name: Create release
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
uses: https://code.forgejo.org/actions/forgejo-release@v2.5.1
|
uses: https://code.forgejo.org/actions/forgejo-release@v2.4.0
|
||||||
with:
|
with:
|
||||||
url: 'https://git.btclock.dev/'
|
url: 'https://git.btclock.dev/'
|
||||||
repo: '${{ github.repository }}'
|
repo: '${{ github.repository }}'
|
||||||
|
|
11
README.md
11
README.md
|
@ -1,11 +1,10 @@
|
||||||
# BTClock WebUI
|
# BTClock WebUI
|
||||||
|
|
||||||
[![Latest release](https://git.btclock.dev/btclock/webui/badges/release.svg)](https://git.btclock.dev/btclock/webui/releases/latest)
|
[![BTClock CI](https://github.com/btclock/webui/actions/workflows/workflow.yml/badge.svg)](https://github.com/btclock/webui2/actions/workflows/workflow.yml)
|
||||||
[![BTClock CI](https://git.btclock.dev/btclock/webui/badges/workflows/build.yaml/badge.svg)](https://git.btclock.dev/btclock/webui/actions?workflow=build.yaml&actor=0&status=0)
|
|
||||||
|
|
||||||
The web user-interface for the BTClock, based on Svelte-kit. It uses Bootstrap for the lay-out.
|
The web user-interface for the BTClock, based on Svelte-kit. It uses Bootstrap for the lay-out.
|
||||||
|
|
||||||
![Screenshot](doc/screenshot-light.webp)
|
![Screenshot](doc/screenshot.webp)
|
||||||
![Screenshot Dark](doc/screenshot-dark.webp)
|
![Screenshot Dark](doc/screenshot-dark.webp)
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
@ -31,11 +30,7 @@ Make sure the postinstall script is ran, because otherwise the filenames are to
|
||||||
|
|
||||||
## Deploying
|
## Deploying
|
||||||
|
|
||||||
To upload the firmware to the BTClock, you need to GZIP all the files. You can use the python script `gzip_build.py` for that:
|
To upload the firmware to the BTClock, you need to GZIP all the files. You can use the python script `gzip_build.py` for that.
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 gzip_build.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Then you can make a `LittleFS.bin` with mklittlefs:
|
Then you can make a `LittleFS.bin` with mklittlefs:
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 51 KiB |
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
BIN
doc/screenshot.webp
Normal file
BIN
doc/screenshot.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
12
package.json
12
package.json
|
@ -5,17 +5,14 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:test": "vite build --config vite.config.test.ts",
|
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . && eslint .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
"test": "prettier --write . && eslint . && npm run test:integration && npm run test:unit",
|
"test": "npm run test:integration && npm run test:unit",
|
||||||
"test:integration": "playwright test",
|
"test:integration": "playwright test",
|
||||||
"test:screenshots": "playwright test -c playwright.screenshot.config.ts",
|
|
||||||
"doc:update-screenshots": "playwright test -c playwright.doc-screenshot.config.ts",
|
|
||||||
"test:unit": "vitest"
|
"test:unit": "vitest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -35,9 +32,7 @@
|
||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
|
||||||
"sass": "^1.79.3",
|
"sass": "^1.79.3",
|
||||||
"sharp": "^0.33.5",
|
|
||||||
"svelte": "^4.2.19",
|
"svelte": "^4.2.19",
|
||||||
"svelte-check": "^4.0.2",
|
"svelte-check": "^4.0.2",
|
||||||
"svelte-preprocess": "^6.0.2",
|
"svelte-preprocess": "^6.0.2",
|
||||||
|
@ -45,7 +40,8 @@
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"typescript-eslint": "^8.7.0",
|
"typescript-eslint": "^8.7.0",
|
||||||
"vite": "^5.4.7",
|
"vite": "^5.4.7",
|
||||||
"vitest": "^2.1.1"
|
"vitest": "^2.1.1",
|
||||||
|
"vitest-github-actions-reporter": "^0.11.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -67,7 +63,9 @@
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"es5-ext": ">=0.10.64",
|
"es5-ext": ">=0.10.64",
|
||||||
|
"undici": ">=5.28.4",
|
||||||
"ws": ">=8.18.0",
|
"ws": ">=8.18.0",
|
||||||
|
"axios": ">=1.7.7",
|
||||||
"micromatch": ">=4.0.8"
|
"micromatch": ">=4.0.8"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
diff --git a/node_modules/@sveltejs/kit/src/exports/vite/index.js b/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
|
||||||
index 21bc3d4..eef2db3 100644
|
|
||||||
--- a/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
|
||||||
+++ b/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
|
||||||
@@ -648,9 +648,9 @@ async function kit({ svelte_config }) {
|
|
||||||
output: {
|
|
||||||
format: inline ? 'iife' : 'esm',
|
|
||||||
name: `__sveltekit_${version_hash}.app`,
|
|
||||||
- entryFileNames: ssr ? '[name].js' : `${prefix}/[name].[hash].${ext}`,
|
|
||||||
- chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[name].[hash].${ext}`,
|
|
||||||
- assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
|
|
||||||
+ entryFileNames: ssr ? '[name].js' : `${prefix}/[hash].${ext}`,
|
|
||||||
+ chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[hash].${ext}`,
|
|
||||||
+ assetFileNames: `${prefix}/assets/[hash][extname]`,
|
|
||||||
hoistTransitiveImports: false,
|
|
||||||
sourcemapIgnoreList,
|
|
||||||
manualChunks: split ? undefined : () => 'bundle',
|
|
||||||
@@ -665,9 +665,9 @@ async function kit({ svelte_config }) {
|
|
||||||
worker: {
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
- entryFileNames: `${prefix}/workers/[name]-[hash].js`,
|
|
||||||
- chunkFileNames: `${prefix}/workers/chunks/[name]-[hash].js`,
|
|
||||||
- assetFileNames: `${prefix}/workers/assets/[name]-[hash][extname]`,
|
|
||||||
+ entryFileNames: `${prefix}/workers/[hash].js`,
|
|
||||||
+ chunkFileNames: `${prefix}/workers/chunks/[hash].js`,
|
|
||||||
+ assetFileNames: `${prefix}/workers/assets/[hash][extname]`,
|
|
||||||
hoistTransitiveImports: false
|
|
||||||
}
|
|
||||||
}
|
|
17
patches/@sveltejs+kit+2.7.5.patch
Normal file
17
patches/@sveltejs+kit+2.7.5.patch
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
diff --git a/node_modules/@sveltejs/kit/src/exports/vite/index.js b/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
||||||
|
index 40fa4c6..738cabf 100644
|
||||||
|
--- a/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
||||||
|
+++ b/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
||||||
|
@@ -655,9 +655,9 @@ async function kit({ svelte_config }) {
|
||||||
|
input,
|
||||||
|
output: {
|
||||||
|
format: 'esm',
|
||||||
|
- entryFileNames: ssr ? '[name].js' : `${prefix}/[name].[hash].${ext}`,
|
||||||
|
- chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[name].[hash].${ext}`,
|
||||||
|
- assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
|
||||||
|
+ entryFileNames: ssr ? '[name].js' : `${prefix}/[hash].${ext}`,
|
||||||
|
+ chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[hash].${ext}`,
|
||||||
|
+ assetFileNames: `${prefix}/assets/[hash][extname]`,
|
||||||
|
hoistTransitiveImports: false,
|
||||||
|
sourcemapIgnoreList
|
||||||
|
},
|
17
patches/@sveltejs+kit+2.8.5.patch
Normal file
17
patches/@sveltejs+kit+2.8.5.patch
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
diff --git a/node_modules/@sveltejs/kit/src/exports/vite/index.js b/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
||||||
|
index e6521e9..f31c28b 100644
|
||||||
|
--- a/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
||||||
|
+++ b/node_modules/@sveltejs/kit/src/exports/vite/index.js
|
||||||
|
@@ -639,9 +639,9 @@ async function kit({ svelte_config }) {
|
||||||
|
input,
|
||||||
|
output: {
|
||||||
|
format: 'esm',
|
||||||
|
- entryFileNames: ssr ? '[name].js' : `${prefix}/[name].[hash].${ext}`,
|
||||||
|
- chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[name].[hash].${ext}`,
|
||||||
|
- assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
|
||||||
|
+ entryFileNames: ssr ? '[name].js' : `${prefix}/[hash].${ext}`,
|
||||||
|
+ chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[hash].${ext}`,
|
||||||
|
+ assetFileNames: `${prefix}/assets/[hash][extname]`,
|
||||||
|
hoistTransitiveImports: false,
|
||||||
|
sourcemapIgnoreList
|
||||||
|
},
|
|
@ -6,11 +6,11 @@ const config: PlaywrightTestConfig = {
|
||||||
timezoneId: 'Europe/Amsterdam'
|
timezoneId: 'Europe/Amsterdam'
|
||||||
},
|
},
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'npm run build:test && npm run preview',
|
command: 'npm run build && npm run preview',
|
||||||
port: 4173
|
port: 4173
|
||||||
},
|
},
|
||||||
reporter: process.env.CI ? 'github' : 'list',
|
reporter: process.env.CI ? 'github' : 'list',
|
||||||
testDir: 'tests/playwright',
|
testDir: 'tests',
|
||||||
testMatch: /(.+\.)?(test|spec)\.[jt]s/
|
testMatch: /(.+\.)?(test|spec)\.[jt]s/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import { defineConfig } from '@playwright/test';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
use: {
|
|
||||||
locale: 'en-GB',
|
|
||||||
timezoneId: 'Europe/Amsterdam'
|
|
||||||
},
|
|
||||||
webServer: {
|
|
||||||
command: 'yarn build && yarn preview',
|
|
||||||
port: 4173
|
|
||||||
},
|
|
||||||
testDir: './tests/doc-screenshots',
|
|
||||||
outputDir: './test-results/screenshots',
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'Light Mode',
|
|
||||||
use: {
|
|
||||||
viewport: { width: 1440, height: 900 },
|
|
||||||
colorScheme: 'light'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Dark Mode',
|
|
||||||
use: { viewport: { width: 1440, height: 900 }, colorScheme: 'dark' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { defineConfig, devices } from '@playwright/test';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
reporter: 'html',
|
|
||||||
use: {
|
|
||||||
locale: 'en-GB',
|
|
||||||
timezoneId: 'Europe/Amsterdam'
|
|
||||||
},
|
|
||||||
webServer: {
|
|
||||||
command: 'npm run build && npm run preview',
|
|
||||||
port: 4173
|
|
||||||
},
|
|
||||||
testDir: './tests/screenshots',
|
|
||||||
outputDir: './test-results/screenshots',
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'MacBook Air 13 inch',
|
|
||||||
use: {
|
|
||||||
viewport: { width: 1440, height: 900 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'iPhone 14 Pro',
|
|
||||||
use: { ...devices['iPhone 14 Pro'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'iPhone 15 Pro Landscape',
|
|
||||||
use: { ...devices['iPhone 15 Pro Landscape'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MacBook Pro 14 inch',
|
|
||||||
use: {
|
|
||||||
viewport: { width: 1512, height: 982 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MacBook Pro 14 inch NL locale',
|
|
||||||
use: {
|
|
||||||
viewport: { width: 1512, height: 982 },
|
|
||||||
locale: 'nl'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MacBook Pro 14 inch nl-NL locale',
|
|
||||||
use: {
|
|
||||||
viewport: { width: 1512, height: 982 },
|
|
||||||
locale: 'nl-NL'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MacBook Pro 14 inch Firefox HiDPI',
|
|
||||||
use: { ...devices['Desktop Firefox HiDPI'], viewport: { width: 1512, height: 982 } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MacBook Pro 14 inch Safari',
|
|
||||||
use: { ...devices['Desktop Safari'], viewport: { width: 1512, height: 982 } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MacBook Pro 14 inch Safari Dark Mode',
|
|
||||||
use: {
|
|
||||||
...devices['Desktop Safari'],
|
|
||||||
viewport: { width: 1512, height: 982 },
|
|
||||||
colorScheme: 'dark'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": ["config:recommended"],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": ["major"],
|
|
||||||
"enabled": false,
|
|
||||||
"matchPackageNames": ["*"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"npm": {
|
|
||||||
"rangeStrategy": "update-lockfile"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
export let value: unknown;
|
|
||||||
export let checkValue: unknown = null;
|
|
||||||
export let width: number = 25;
|
|
||||||
|
|
||||||
$: valueToCheck = checkValue === null ? value : checkValue;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<span class:placeholder={!valueToCheck} class={!valueToCheck ? `w-${width}` : ''}>
|
|
||||||
{valueToCheck ? value : ''}
|
|
||||||
</span>
|
|
|
@ -1,65 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import {
|
|
||||||
Input,
|
|
||||||
InputGroup,
|
|
||||||
InputGroupText,
|
|
||||||
Label,
|
|
||||||
FormText,
|
|
||||||
Col,
|
|
||||||
Row
|
|
||||||
} from '@sveltestrap/sveltestrap';
|
|
||||||
|
|
||||||
export let id: string;
|
|
||||||
export let label: string;
|
|
||||||
export let value: string | number;
|
|
||||||
export let type: string = 'text';
|
|
||||||
export let size: string = 'sm';
|
|
||||||
export let required: boolean = false;
|
|
||||||
export let min: number | undefined = undefined;
|
|
||||||
export let max: number | undefined = undefined;
|
|
||||||
export let step: number | string | undefined = undefined;
|
|
||||||
export let suffix: string | undefined = undefined;
|
|
||||||
export let helpText: string | undefined = undefined;
|
|
||||||
export let disabled: boolean = false;
|
|
||||||
export let valid: boolean | undefined = undefined;
|
|
||||||
export let invalid: boolean | undefined = undefined;
|
|
||||||
export let minlength: string | undefined = undefined;
|
|
||||||
export let onChange: (() => void) | undefined = undefined;
|
|
||||||
export let onInput: (() => void) | undefined = undefined;
|
|
||||||
|
|
||||||
const onInputHandler = () => {
|
|
||||||
onInput?.();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<Label md={6} for={id} {size}>{label}</Label>
|
|
||||||
<Col md="6">
|
|
||||||
<InputGroup {size}>
|
|
||||||
<Input
|
|
||||||
{id}
|
|
||||||
{type}
|
|
||||||
bind:value
|
|
||||||
{required}
|
|
||||||
{min}
|
|
||||||
{max}
|
|
||||||
{step}
|
|
||||||
{disabled}
|
|
||||||
{valid}
|
|
||||||
{invalid}
|
|
||||||
{minlength}
|
|
||||||
bsSize={size}
|
|
||||||
on:change={onChange}
|
|
||||||
on:input={onInputHandler}
|
|
||||||
spellcheck={type === 'text' ? 'false' : undefined}
|
|
||||||
/>
|
|
||||||
{#if suffix}
|
|
||||||
<InputGroupText>{suffix}</InputGroupText>
|
|
||||||
{/if}
|
|
||||||
<slot />
|
|
||||||
</InputGroup>
|
|
||||||
{#if helpText}
|
|
||||||
<FormText>{helpText}</FormText>
|
|
||||||
{/if}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { Input, Label, FormText, Col, Row } from '@sveltestrap/sveltestrap';
|
|
||||||
|
|
||||||
export let id: string;
|
|
||||||
export let label: string;
|
|
||||||
export let value: string | number;
|
|
||||||
export let options: Array<[string, string | number]>;
|
|
||||||
export let size: string = 'sm';
|
|
||||||
export let helpText: string | undefined = undefined;
|
|
||||||
export let selectClass: string | undefined = undefined;
|
|
||||||
export let onChange: (() => void) | undefined = undefined;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<Label md={6} for={id} {size}>{label}</Label>
|
|
||||||
<Col md="6">
|
|
||||||
<Input
|
|
||||||
{id}
|
|
||||||
type="select"
|
|
||||||
bind:value
|
|
||||||
name="select"
|
|
||||||
bsSize={size}
|
|
||||||
class={selectClass}
|
|
||||||
on:change={onChange}
|
|
||||||
>
|
|
||||||
{#each options as [key, val]}
|
|
||||||
<option value={val}>{key}</option>
|
|
||||||
{/each}
|
|
||||||
</Input>
|
|
||||||
{#if helpText}
|
|
||||||
<FormText>{helpText}</FormText>
|
|
||||||
{/if}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { Input, Col } from '@sveltestrap/sveltestrap';
|
|
||||||
|
|
||||||
// Props
|
|
||||||
export let id: string;
|
|
||||||
export let checked: boolean;
|
|
||||||
export let label: string;
|
|
||||||
export let size: string = 'sm';
|
|
||||||
export let disabled: boolean = false;
|
|
||||||
export let col: { [key: string]: string } = { md: '6', xl: '12', xxl: '6' };
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Col {...col}>
|
|
||||||
<Input {id} bind:checked type="switch" bsSize={size} {label} {disabled} />
|
|
||||||
</Col>
|
|
|
@ -1,6 +0,0 @@
|
||||||
export { default as SettingsSwitch } from './SettingsSwitch.svelte';
|
|
||||||
export { default as SettingsInput } from './SettingsInput.svelte';
|
|
||||||
export { default as SettingsSelect } from './SettingsSelect.svelte';
|
|
||||||
export { default as ToggleHeader } from './ToggleHeader.svelte';
|
|
||||||
export { default as ColorSchemeSwitcher } from './ColorSchemeSwitcher.svelte';
|
|
||||||
export { default as Placeholder } from './Placeholder.svelte';
|
|
|
@ -1,142 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { SettingsInput, SettingsSwitch } from '$lib/components';
|
|
||||||
import { _ } from 'svelte-i18n';
|
|
||||||
import { Row, Col, FormGroup, Input, InputGroupText } from '@sveltestrap/sveltestrap';
|
|
||||||
import ToggleHeader from '../ToggleHeader.svelte';
|
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
|
||||||
import { isValidHexPubKey, getPubKey, isValidNpub } from '$lib';
|
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
import { DataSourceType } from '$lib/types/dataSource';
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
export let settings;
|
|
||||||
export let isOpen = false;
|
|
||||||
|
|
||||||
const checkValidNostrPubkey = (key: string) => {
|
|
||||||
$settings[key] = $settings[key].trim();
|
|
||||||
if (isValidNpub($settings[key])) {
|
|
||||||
dispatch('showToast', {
|
|
||||||
color: 'info',
|
|
||||||
text: $_('section.settings.convertingValidNpub')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = getPubKey($settings[key]);
|
|
||||||
if (ret) $settings[key] = ret;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<ToggleHeader header={$_('section.settings.section.dataSource')} bind:isOpen defaultOpen={false}>
|
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
<h5>Data Source</h5>
|
|
||||||
<FormGroup>
|
|
||||||
<Row>
|
|
||||||
<Col xs="12" xl="6" class="mb-2">
|
|
||||||
<Input
|
|
||||||
type="radio"
|
|
||||||
id="btclock_source"
|
|
||||||
name="dataSource"
|
|
||||||
bind:group={$settings.dataSource}
|
|
||||||
value={DataSourceType.BTCLOCK_SOURCE}
|
|
||||||
label={$_('section.settings.dataSource.btclock')}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col xs="12" xl="6" class="mb-2">
|
|
||||||
<Input
|
|
||||||
type="radio"
|
|
||||||
id="third_party_source"
|
|
||||||
name="dataSource"
|
|
||||||
bind:group={$settings.dataSource}
|
|
||||||
value={DataSourceType.THIRD_PARTY_SOURCE}
|
|
||||||
label={$_('section.settings.dataSource.thirdParty')}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
{#if $settings.nostrRelay}
|
|
||||||
<Col xs="12" xl="6" class="mb-2">
|
|
||||||
<Input
|
|
||||||
type="radio"
|
|
||||||
id="nostr_source"
|
|
||||||
name="dataSource"
|
|
||||||
bind:group={$settings.dataSource}
|
|
||||||
value={DataSourceType.NOSTR_SOURCE}
|
|
||||||
label={$_('section.settings.dataSource.nostr')}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
{/if}
|
|
||||||
<Col xs="12" xl="6" class="mb-2">
|
|
||||||
<Input
|
|
||||||
type="radio"
|
|
||||||
id="custom_source"
|
|
||||||
name="dataSource"
|
|
||||||
bind:group={$settings.dataSource}
|
|
||||||
value={DataSourceType.CUSTOM_SOURCE}
|
|
||||||
label={$_('section.settings.dataSource.custom')}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
{#if $settings.dataSource === DataSourceType.THIRD_PARTY_SOURCE}
|
|
||||||
<SettingsInput
|
|
||||||
id="mempoolInstance"
|
|
||||||
label={$_('section.settings.mempoolnstance')}
|
|
||||||
bind:value={$settings.mempoolInstance}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
>
|
|
||||||
<InputGroupText>
|
|
||||||
<Input
|
|
||||||
type="checkbox"
|
|
||||||
bind:checked={$settings.mempoolSecure}
|
|
||||||
bsSize={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
HTTPS
|
|
||||||
</InputGroupText>
|
|
||||||
</SettingsInput>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $settings.dataSource === DataSourceType.NOSTR_SOURCE}
|
|
||||||
<SettingsInput
|
|
||||||
id="nostrRelay"
|
|
||||||
label={$_('section.settings.nostrRelay')}
|
|
||||||
bind:value={$settings.nostrRelay}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsInput
|
|
||||||
id="nostrPubKey"
|
|
||||||
label={$_('section.settings.nostrPubKey')}
|
|
||||||
bind:value={$settings.nostrPubKey}
|
|
||||||
required={true}
|
|
||||||
minlength="64"
|
|
||||||
invalid={!isValidHexPubKey($settings.nostrPubKey)}
|
|
||||||
helpText={!isValidHexPubKey($settings.nostrPubKey)
|
|
||||||
? $_('section.settings.invalidNostrPubkey')
|
|
||||||
: undefined}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
onChange={() => checkValidNostrPubkey('nostrPubKey')}
|
|
||||||
onInput={() => checkValidNostrPubkey('nostrPubKey')}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $settings.dataSource === DataSourceType.CUSTOM_SOURCE}
|
|
||||||
<SettingsInput
|
|
||||||
id="ceEndpoint"
|
|
||||||
label={$_('section.settings.ceEndpoint')}
|
|
||||||
bind:value={$settings.ceEndpoint}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="ceDisableSSL"
|
|
||||||
bind:checked={$settings.ceDisableSSL}
|
|
||||||
label={$_('section.settings.ceDisableSSL')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</ToggleHeader>
|
|
||||||
</Row>
|
|
|
@ -1,202 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { SettingsInput, SettingsSwitch, SettingsSelect } from '$lib/components';
|
|
||||||
import { _ } from 'svelte-i18n';
|
|
||||||
import { Row } from '@sveltestrap/sveltestrap';
|
|
||||||
import ToggleHeader from '../ToggleHeader.svelte';
|
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
|
||||||
import { PUBLIC_BASE_URL } from '$lib/config';
|
|
||||||
|
|
||||||
export let settings;
|
|
||||||
export let isOpen = false;
|
|
||||||
|
|
||||||
const onFlBrightnessChange = async () => {
|
|
||||||
await fetch(`${PUBLIC_BASE_URL}/api/frontlight/brightness/${$settings.flMaxBrightness}`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setTextColor = () => {
|
|
||||||
$settings.invertedColor = !$settings.invertedColor;
|
|
||||||
};
|
|
||||||
|
|
||||||
const textColorOptions: [string, boolean][] = [
|
|
||||||
[$_('colors.black') + ' on ' + $_('colors.white'), false],
|
|
||||||
[$_('colors.white') + ' on ' + $_('colors.black'), true]
|
|
||||||
];
|
|
||||||
|
|
||||||
const fontPreferenceOptions: [string, string][] = $settings.availableFonts?.map((font) => [
|
|
||||||
$_(`fonts.${font}`) !== `fonts.${font}`
|
|
||||||
? $_(`fonts.${font}`)
|
|
||||||
: font.charAt(0).toUpperCase() + font.slice(1),
|
|
||||||
font
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<ToggleHeader
|
|
||||||
header={$_('section.settings.section.displaysAndLed')}
|
|
||||||
bind:isOpen
|
|
||||||
defaultOpen={false}
|
|
||||||
>
|
|
||||||
<SettingsSelect
|
|
||||||
id="textColor"
|
|
||||||
label={$_('section.settings.textColor')}
|
|
||||||
bind:value={$settings.invertedColor}
|
|
||||||
options={textColorOptions}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
on:change={setTextColor}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsSelect
|
|
||||||
id="fontName"
|
|
||||||
label={$_('section.settings.fontName')}
|
|
||||||
bind:value={$settings.fontName}
|
|
||||||
options={fontPreferenceOptions}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="timePerScreen"
|
|
||||||
label={$_('section.settings.timePerScreen')}
|
|
||||||
bind:value={$settings.timePerScreen}
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
step={1}
|
|
||||||
required={true}
|
|
||||||
suffix={$_('time.minutes')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="fullRefreshMin"
|
|
||||||
label={$_('section.settings.fullRefreshEvery')}
|
|
||||||
bind:value={$settings.fullRefreshMin}
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
step={1}
|
|
||||||
required={true}
|
|
||||||
suffix={$_('time.minutes')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="minSecPriceUpd"
|
|
||||||
label={$_('section.settings.timeBetweenPriceUpdates')}
|
|
||||||
bind:value={$settings.minSecPriceUpd}
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
step={1}
|
|
||||||
suffix={$_('time.seconds')}
|
|
||||||
helpText={$_('section.settings.shortAmountsWarning')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="ledBrightness"
|
|
||||||
label={$_('section.settings.ledBrightness')}
|
|
||||||
bind:value={$settings.ledBrightness}
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={255}
|
|
||||||
step={1}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
|
||||||
<SettingsInput
|
|
||||||
id="flMaxBrightness"
|
|
||||||
label={$_('section.settings.flMaxBrightness')}
|
|
||||||
bind:value={$settings.flMaxBrightness}
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={4095}
|
|
||||||
step={1}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
on:change={onFlBrightnessChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="flEffectDelay"
|
|
||||||
label={$_('section.settings.flEffectDelay')}
|
|
||||||
bind:value={$settings.flEffectDelay}
|
|
||||||
type="range"
|
|
||||||
min={5}
|
|
||||||
max={300}
|
|
||||||
step={1}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if !$settings.flDisable && $settings.hasLightLevel}
|
|
||||||
<SettingsInput
|
|
||||||
id="luxLightToggle"
|
|
||||||
label={`${$_('section.settings.luxLightToggle')} (${$settings.luxLightToggle})`}
|
|
||||||
bind:value={$settings.luxLightToggle}
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={1000}
|
|
||||||
step={1}
|
|
||||||
helpText={$_('section.settings.luxLightToggleText')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="ledTestOnPower"
|
|
||||||
bind:checked={$settings.ledTestOnPower}
|
|
||||||
label={$_('section.settings.ledPowerOnTest')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsSwitch
|
|
||||||
id="ledFlashOnUpd"
|
|
||||||
bind:checked={$settings.ledFlashOnUpd}
|
|
||||||
label={$_('section.settings.ledFlashOnBlock')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsSwitch
|
|
||||||
id="disableLeds"
|
|
||||||
bind:checked={$settings.disableLeds}
|
|
||||||
label={$_('section.settings.disableLeds')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if $settings.hasFrontlight}
|
|
||||||
<SettingsSwitch
|
|
||||||
id="flDisable"
|
|
||||||
bind:checked={$settings.flDisable}
|
|
||||||
label={$_('section.settings.flDisable')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
|
||||||
<SettingsSwitch
|
|
||||||
id="flAlwaysOn"
|
|
||||||
bind:checked={$settings.flAlwaysOn}
|
|
||||||
label={$_('section.settings.flAlwaysOn')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsSwitch
|
|
||||||
id="flFlashOnUpd"
|
|
||||||
bind:checked={$settings.flFlashOnUpd}
|
|
||||||
label={$_('section.settings.flFlashOnUpd')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsSwitch
|
|
||||||
id="flOffWhenDark"
|
|
||||||
bind:checked={$settings.flOffWhenDark}
|
|
||||||
label={$_('section.settings.flOffWhenDark')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</Row>
|
|
||||||
</ToggleHeader>
|
|
||||||
</Row>
|
|
|
@ -1,248 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { SettingsInput, SettingsSwitch, SettingsSelect } from '$lib/components';
|
|
||||||
import { _ } from 'svelte-i18n';
|
|
||||||
import { Row, Button, Col } from '@sveltestrap/sveltestrap';
|
|
||||||
import ToggleHeader from '../ToggleHeader.svelte';
|
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
|
||||||
import { isValidHexPubKey, getPubKey, isValidNpub } from '$lib';
|
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
export let settings;
|
|
||||||
export let isOpen = false;
|
|
||||||
export let miningPoolMap: Map<string, string>;
|
|
||||||
|
|
||||||
let validBitaxe = false;
|
|
||||||
const testBitaxe = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`http://${$settings.bitaxeHostname}/api/system/info`);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
dispatch('showToast', {
|
|
||||||
color: 'danger',
|
|
||||||
text: `Failed to connect to BitAxe HTTP error! status: ${response.status}`
|
|
||||||
});
|
|
||||||
validBitaxe = false;
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
const systemInfo = await response.json();
|
|
||||||
dispatch('showToast', {
|
|
||||||
color: 'success',
|
|
||||||
text: `Connected to BitAxe ${systemInfo.ASICModel} (Board version ${systemInfo.boardVersion}) running firmware ${systemInfo.version}.\r\nCurrent hashrate ${Math.round(systemInfo.hashRate)} GH/s`
|
|
||||||
});
|
|
||||||
validBitaxe = true;
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof TypeError && error.message.includes('Failed to fetch')) {
|
|
||||||
dispatch('showToast', {
|
|
||||||
color: 'danger',
|
|
||||||
text: `Failed to connect to BitAxe, make sure you are connected to the same network.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.error('Failed to fetch Bitaxe system info:', error);
|
|
||||||
validBitaxe = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkValidNostrPubkey = (key: string) => {
|
|
||||||
$settings[key] = $settings[key].trim();
|
|
||||||
if (isValidNpub($settings[key])) {
|
|
||||||
dispatch('showToast', {
|
|
||||||
color: 'info',
|
|
||||||
text: $_('section.settings.convertingValidNpub')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = getPubKey($settings[key]);
|
|
||||||
if (ret) $settings[key] = ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
$: poolOptions = ($settings.availablePools || []).map((pool: string): [string, string] => [
|
|
||||||
miningPoolMap.get(pool) || pool,
|
|
||||||
pool
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<ToggleHeader
|
|
||||||
header={$_('section.settings.section.extraFeatures')}
|
|
||||||
bind:isOpen
|
|
||||||
defaultOpen={false}
|
|
||||||
>
|
|
||||||
<!--- Time based do not disturb settings -->
|
|
||||||
<SettingsSwitch
|
|
||||||
id="timeBasedDnd"
|
|
||||||
label={$_('section.settings.timeBasedDnd')}
|
|
||||||
bind:checked={$settings.dnd.timeBasedEnabled}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{#if $settings.dnd.timeBasedEnabled}
|
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
<SettingsInput
|
|
||||||
id="dndStartHour"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max="23"
|
|
||||||
label={$_('section.settings.dndStartHour')}
|
|
||||||
bind:value={$settings.dnd.startHour}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<SettingsInput
|
|
||||||
id="dndStartMinute"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max="59"
|
|
||||||
label={$_('section.settings.dndStartMinute')}
|
|
||||||
bind:value={$settings.dnd.startMinute}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
<SettingsInput
|
|
||||||
id="dndEndHour"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max="23"
|
|
||||||
label={$_('section.settings.dndEndHour')}
|
|
||||||
bind:value={$settings.dnd.endHour}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<SettingsInput
|
|
||||||
id="dndEndMinute"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max="59"
|
|
||||||
label={$_('section.settings.dndEndMinute')}
|
|
||||||
bind:value={$settings.dnd.endMinute}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- BitAxe Settings -->
|
|
||||||
{#if 'bitaxeEnabled' in $settings}
|
|
||||||
<Row class="mb-3">
|
|
||||||
<Col>
|
|
||||||
<h5>BitAxe</h5>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="bitaxeEnabled"
|
|
||||||
bind:checked={$settings.bitaxeEnabled}
|
|
||||||
label="{$_('section.settings.bitaxeEnabled')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '12', xl: '12', xxl: '12' }}
|
|
||||||
/>
|
|
||||||
{#if $settings.bitaxeEnabled}
|
|
||||||
<SettingsInput
|
|
||||||
id="bitaxeHostname"
|
|
||||||
label={$_('section.settings.bitaxeHostname')}
|
|
||||||
bind:value={$settings.bitaxeHostname}
|
|
||||||
required={true}
|
|
||||||
valid={validBitaxe}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
>
|
|
||||||
<Button type="button" color="success" on:click={testBitaxe}>
|
|
||||||
{$_('test', { default: 'Test' })}
|
|
||||||
</Button>
|
|
||||||
</SettingsInput>
|
|
||||||
{/if}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- Mining Pool Settings -->
|
|
||||||
{#if 'miningPoolStats' in $settings}
|
|
||||||
<Row class="mb-3">
|
|
||||||
<Col>
|
|
||||||
<h5>Mining Pool stats</h5>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="miningPoolStats"
|
|
||||||
bind:checked={$settings.miningPoolStats}
|
|
||||||
label="{$_('section.settings.miningPoolStats')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '12', xl: '12', xxl: '12' }}
|
|
||||||
/>
|
|
||||||
{#if $settings.miningPoolStats}
|
|
||||||
<SettingsSelect
|
|
||||||
id="miningPoolName"
|
|
||||||
label={$_('section.settings.miningPoolName')}
|
|
||||||
bind:value={$settings.miningPoolName}
|
|
||||||
options={poolOptions}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
selectClass={$uiSettings.selectClass}
|
|
||||||
/>
|
|
||||||
<SettingsInput
|
|
||||||
id="miningPoolUser"
|
|
||||||
label={$_('section.settings.miningPoolUser')}
|
|
||||||
bind:value={$settings.miningPoolUser}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- Nostr Settings -->
|
|
||||||
{#if 'nostrZapNotify' in $settings}
|
|
||||||
<Row class="mb-3">
|
|
||||||
<Col>
|
|
||||||
<h5>Nostr</h5>
|
|
||||||
<SettingsInput
|
|
||||||
id="nostrRelay"
|
|
||||||
label={$_('section.settings.nostrRelay')}
|
|
||||||
bind:value={$settings.nostrRelay}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="nostrZapNotify"
|
|
||||||
bind:checked={$settings.nostrZapNotify}
|
|
||||||
label="{$_('section.settings.nostrZapNotify')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '12', xl: '12', xxl: '12' }}
|
|
||||||
/>
|
|
||||||
{#if $settings.nostrZapNotify}
|
|
||||||
<Row>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="ledFlashOnZap"
|
|
||||||
bind:checked={$settings.ledFlashOnZap}
|
|
||||||
label={$_('section.settings.ledFlashOnZap')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
|
||||||
<SettingsSwitch
|
|
||||||
id="flFlashOnZap"
|
|
||||||
bind:checked={$settings.flFlashOnZap}
|
|
||||||
label={$_('section.settings.flFlashOnZap')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</Row>
|
|
||||||
<SettingsInput
|
|
||||||
id="nostrZapPubkey"
|
|
||||||
label={$_('section.settings.nostrZapPubkey')}
|
|
||||||
bind:value={$settings.nostrZapPubkey}
|
|
||||||
required={true}
|
|
||||||
minlength="64"
|
|
||||||
invalid={!isValidHexPubKey($settings.nostrZapPubkey)}
|
|
||||||
helpText={!isValidHexPubKey($settings.nostrZapPubkey)
|
|
||||||
? $_('section.settings.invalidNostrPubkey')
|
|
||||||
: undefined}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
onChange={() => checkValidNostrPubkey('nostrZapPubkey')}
|
|
||||||
onInput={() => checkValidNostrPubkey('nostrZapPubkey')}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{/if}
|
|
||||||
</ToggleHeader>
|
|
||||||
</Row>
|
|
|
@ -1,125 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { SettingsSwitch } from '$lib/components';
|
|
||||||
import { _ } from 'svelte-i18n';
|
|
||||||
import { Row, Col } from '@sveltestrap/sveltestrap';
|
|
||||||
import ToggleHeader from '../ToggleHeader.svelte';
|
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
|
||||||
|
|
||||||
export let settings;
|
|
||||||
export let isOpen = false;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<ToggleHeader
|
|
||||||
header={$_('section.settings.section.screenSettings')}
|
|
||||||
bind:isOpen
|
|
||||||
defaultOpen={true}
|
|
||||||
>
|
|
||||||
<Row>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="stealFocus"
|
|
||||||
bind:checked={$settings.stealFocus}
|
|
||||||
label={$_('section.settings.StealFocusOnNewBlock')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="mcapBigChar"
|
|
||||||
bind:checked={$settings.mcapBigChar}
|
|
||||||
label={$_('section.settings.useBigCharsMcap')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="useBlkCountdown"
|
|
||||||
bind:checked={$settings.useBlkCountdown}
|
|
||||||
label={$_('section.settings.useBlkCountdown')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="useSatsSymbol"
|
|
||||||
bind:checked={$settings.useSatsSymbol}
|
|
||||||
label={$_('section.settings.useSatsSymbol')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="suffixPrice"
|
|
||||||
bind:checked={$settings.suffixPrice}
|
|
||||||
label={$_('section.settings.suffixPrice')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="mowMode"
|
|
||||||
bind:checked={$settings.mowMode}
|
|
||||||
label={$_('section.settings.mowMode')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
disabled={!$settings.suffixPrice}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="suffixShareDot"
|
|
||||||
bind:checked={$settings.suffixShareDot}
|
|
||||||
label={$_('section.settings.suffixShareDot')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
disabled={!$settings.suffixPrice}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="verticalDesc"
|
|
||||||
bind:checked={$settings.verticalDesc}
|
|
||||||
label={$_('section.settings.verticalDesc')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if !$settings.actCurrencies}
|
|
||||||
<SettingsSwitch
|
|
||||||
id="fetchEurPrice"
|
|
||||||
bind:checked={$settings.fetchEurPrice}
|
|
||||||
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<h5>{$_('section.settings.screens')}</h5>
|
|
||||||
{#if $settings.screens}
|
|
||||||
{#each $settings.screens as s}
|
|
||||||
<SettingsSwitch
|
|
||||||
id="screens_{s.id}"
|
|
||||||
bind:checked={s.enabled}
|
|
||||||
label={s.name}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</Row>
|
|
||||||
{#if $settings.actCurrencies && $settings.useNostr !== true}
|
|
||||||
<Row>
|
|
||||||
<h5>{$_('section.settings.currencies')}</h5>
|
|
||||||
<small>{$_('restartRequired')}</small>
|
|
||||||
{#if $settings.availableCurrencies}
|
|
||||||
{#each $settings.availableCurrencies as c}
|
|
||||||
<Col md="6" xl="12" xxl="6">
|
|
||||||
<div class="form-check form-control-{$uiSettings.inputSize}">
|
|
||||||
<input
|
|
||||||
id="currency_{c}"
|
|
||||||
bind:group={$settings.actCurrencies}
|
|
||||||
value={c}
|
|
||||||
type="checkbox"
|
|
||||||
class="form-check-input"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="currency_{c}">{c}</label>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</Row>
|
|
||||||
{/if}
|
|
||||||
</ToggleHeader>
|
|
||||||
</Row>
|
|
|
@ -1,113 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { SettingsInput, SettingsSwitch } from '$lib/components';
|
|
||||||
import { _ } from 'svelte-i18n';
|
|
||||||
import { Row, Button } from '@sveltestrap/sveltestrap';
|
|
||||||
import ToggleHeader from '../ToggleHeader.svelte';
|
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
|
||||||
import EyeIcon from 'svelte-bootstrap-icons/lib/Eye.svelte';
|
|
||||||
import EyeSlashIcon from 'svelte-bootstrap-icons/lib/EyeSlash.svelte';
|
|
||||||
|
|
||||||
export let settings;
|
|
||||||
export let isOpen = false;
|
|
||||||
|
|
||||||
let showPassword = false;
|
|
||||||
|
|
||||||
const getTzOffsetFromSystem = () => {
|
|
||||||
const dt = new Date();
|
|
||||||
let diffTZ = dt.getTimezoneOffset();
|
|
||||||
$settings.tzOffset = diffTZ * -1;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<ToggleHeader header={$_('section.settings.section.system')} bind:isOpen defaultOpen={false}>
|
|
||||||
<SettingsInput
|
|
||||||
id="tzOffset"
|
|
||||||
label={$_('section.settings.timezoneOffset')}
|
|
||||||
bind:value={$settings.tzOffset}
|
|
||||||
type="number"
|
|
||||||
step={1}
|
|
||||||
required={true}
|
|
||||||
suffix={$_('time.minutes')}
|
|
||||||
helpText={$_('section.settings.tzOffsetHelpText')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
>
|
|
||||||
<Button type="button" color="info" on:click={getTzOffsetFromSystem}>
|
|
||||||
{$_('auto-detect')}
|
|
||||||
</Button>
|
|
||||||
</SettingsInput>
|
|
||||||
|
|
||||||
{#if $settings.httpAuthEnabled}
|
|
||||||
<SettingsInput
|
|
||||||
id="httpAuthUser"
|
|
||||||
label={$_('section.settings.httpAuthUser')}
|
|
||||||
bind:value={$settings.httpAuthUser}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsInput
|
|
||||||
id="httpAuthPass"
|
|
||||||
label={$_('section.settings.httpAuthPass')}
|
|
||||||
bind:value={$settings.httpAuthPass}
|
|
||||||
type={showPassword ? 'text' : 'password'}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
on:click={() => (showPassword = !showPassword)}
|
|
||||||
color={showPassword ? 'success' : 'danger'}
|
|
||||||
>
|
|
||||||
{#if !showPassword}<EyeIcon />{:else}<EyeSlashIcon />{/if}
|
|
||||||
</Button>
|
|
||||||
</SettingsInput>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="hostnamePrefix"
|
|
||||||
label={$_('section.settings.hostnamePrefix')}
|
|
||||||
bind:value={$settings.hostnamePrefix}
|
|
||||||
required={true}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsInput
|
|
||||||
id="wpTimeout"
|
|
||||||
label={$_('section.settings.wpTimeout')}
|
|
||||||
bind:value={$settings.wpTimeout}
|
|
||||||
type="number"
|
|
||||||
min={1}
|
|
||||||
step={1}
|
|
||||||
required={true}
|
|
||||||
suffix={$_('time.seconds')}
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Row>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="otaEnabled"
|
|
||||||
bind:checked={$settings.otaEnabled}
|
|
||||||
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="mdnsEnabled"
|
|
||||||
bind:checked={$settings.mdnsEnabled}
|
|
||||||
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="httpAuthEnabled"
|
|
||||||
bind:checked={$settings.httpAuthEnabled}
|
|
||||||
label="{$_('section.settings.httpAuthEnabled')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
<SettingsSwitch
|
|
||||||
id="enableDebugLog"
|
|
||||||
bind:checked={$settings.enableDebugLog}
|
|
||||||
label="{$_('section.settings.enableDebugLog')} ({$_('restartRequired')})"
|
|
||||||
size={$uiSettings.inputSize}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
</ToggleHeader>
|
|
||||||
</Row>
|
|
|
@ -1,5 +0,0 @@
|
||||||
export { default as ScreenSpecificSettings } from './ScreenSpecificSettings.svelte';
|
|
||||||
export { default as DisplaySettings } from './DisplaySettings.svelte';
|
|
||||||
export { default as DataSourceSettings } from './DataSourceSettings.svelte';
|
|
||||||
export { default as ExtraFeaturesSettings } from './ExtraFeaturesSettings.svelte';
|
|
||||||
export { default as SystemSettings } from './SystemSettings.svelte';
|
|
|
@ -8,23 +8,11 @@ register('nl', () => import('../locales/nl.json'));
|
||||||
register('es', () => import('../locales/es.json'));
|
register('es', () => import('../locales/es.json'));
|
||||||
register('de', () => import('../locales/de.json'));
|
register('de', () => import('../locales/de.json'));
|
||||||
|
|
||||||
const getInitialLocale = () => {
|
|
||||||
if (!browser) return defaultLocale;
|
|
||||||
|
|
||||||
// Check localStorage first
|
|
||||||
const storedLocale = localStorage.getItem('locale');
|
|
||||||
if (storedLocale) return storedLocale;
|
|
||||||
|
|
||||||
// Get browser locale and normalize it
|
|
||||||
const browserLocale = window.navigator.language;
|
|
||||||
const normalizedLocale = browserLocale.split('-')[0].toLowerCase();
|
|
||||||
|
|
||||||
// Check if we support this locale
|
|
||||||
const supportedLocales = ['en', 'nl', 'es', 'de'];
|
|
||||||
return supportedLocales.includes(normalizedLocale) ? normalizedLocale : defaultLocale;
|
|
||||||
};
|
|
||||||
|
|
||||||
init({
|
init({
|
||||||
fallbackLocale: defaultLocale,
|
fallbackLocale: defaultLocale,
|
||||||
initialLocale: getInitialLocale()
|
initialLocale: browser
|
||||||
|
? browser && localStorage.getItem('locale')
|
||||||
|
? localStorage.getItem('locale')
|
||||||
|
: window.navigator.language.slice(0, 2)
|
||||||
|
: defaultLocale
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"wifiTxPower": "WiFi-TX-Leistung",
|
"wifiTxPower": "WiFi-TX-Leistung",
|
||||||
"settingsSaved": "Einstellungen gespeichert",
|
"settingsSaved": "Einstellungen gespeichert",
|
||||||
"errorSavingSettings": "Fehler beim Speichern der Einstellungen",
|
"errorSavingSettings": "Fehler beim Speichern der Einstellungen",
|
||||||
"ownDataSource": "BTClock-Datenquelle",
|
"ownDataSource": "BTClock-Datenquelle verwenden",
|
||||||
"flAlwaysOn": "Displaybeleuchtung immer an",
|
"flAlwaysOn": "Displaybeleuchtung immer an",
|
||||||
"flEffectDelay": "Displaybeleuchtungeffekt Geschwindigkeit",
|
"flEffectDelay": "Displaybeleuchtungeffekt Geschwindigkeit",
|
||||||
"flFlashOnUpd": "Displaybeleuchting bei neuem Block",
|
"flFlashOnUpd": "Displaybeleuchting bei neuem Block",
|
||||||
|
@ -55,25 +55,7 @@
|
||||||
"ledFlashOnZap": "LED blinkt bei Nostr Zap",
|
"ledFlashOnZap": "LED blinkt bei Nostr Zap",
|
||||||
"flFlashOnZap": "Displaybeleuchting bei Nostr Zap",
|
"flFlashOnZap": "Displaybeleuchting bei Nostr Zap",
|
||||||
"showAll": "Alle anzeigen",
|
"showAll": "Alle anzeigen",
|
||||||
"hideAll": "Alles ausblenden",
|
"hideAll": "Alles ausblenden"
|
||||||
"flOffWhenDark": "Displaybeleuchtung aus, wenn es dunkel ist",
|
|
||||||
"luxLightToggleText": "Zum Deaktivieren auf 0 setzen",
|
|
||||||
"verticalDesc": "Vrtikale Bildschirmbeschreibung",
|
|
||||||
"enableDebugLog": "Debug-Protokoll aktivieren",
|
|
||||||
"bitaxeEnabled": "BitAxe-Integration aktivieren",
|
|
||||||
"miningPoolStats": "Mining-Pool-Statistiken Integration Aktivieren",
|
|
||||||
"nostrZapNotify": "Nostr Zap-Benachrichtigungen aktivieren",
|
|
||||||
"thirdPartySource": "mempool.space/coincap.io Verwenden",
|
|
||||||
"dataSource": {
|
|
||||||
"nostr": "Nostr-Verlag",
|
|
||||||
"custom": "Benutzerdefinierter dataquelle"
|
|
||||||
},
|
|
||||||
"fontName": "Schriftart",
|
|
||||||
"timeBasedDnd": "Aktivieren Sie den Zeitplan „Bitte nicht stören“.",
|
|
||||||
"dndStartHour": "Startstunde",
|
|
||||||
"dndStartMinute": "Startminute",
|
|
||||||
"dndEndHour": "Endstunde",
|
|
||||||
"dndEndMinute": "Schlussminute"
|
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"systemInfo": "Systeminfo",
|
"systemInfo": "Systeminfo",
|
||||||
|
@ -101,9 +83,7 @@
|
||||||
"wifiSignalStrength": "WiFi-Signalstärke",
|
"wifiSignalStrength": "WiFi-Signalstärke",
|
||||||
"wsDataConnection": "BTClock-Datenquelle verbindung",
|
"wsDataConnection": "BTClock-Datenquelle verbindung",
|
||||||
"lightSensor": "Lichtsensor",
|
"lightSensor": "Lichtsensor",
|
||||||
"nostrConnection": "Nostr Relay-Verbindung",
|
"nostrConnection": "Nostr Relay-Verbindung"
|
||||||
"doNotDisturb": "Bitte nicht stören",
|
|
||||||
"timeBasedDnd": "Zeitbasierter Zeitplan"
|
|
||||||
},
|
},
|
||||||
"firmwareUpdater": {
|
"firmwareUpdater": {
|
||||||
"fileUploadSuccess": "Datei erfolgreich hochgeladen, Gerät neu gestartet. WebUI in {countdown} Sekunden neu geladen",
|
"fileUploadSuccess": "Datei erfolgreich hochgeladen, Gerät neu gestartet. WebUI in {countdown} Sekunden neu geladen",
|
||||||
|
@ -115,8 +95,7 @@
|
||||||
"latestVersion": "Letzte Version",
|
"latestVersion": "Letzte Version",
|
||||||
"releaseDate": "Veröffentlichungsdatum",
|
"releaseDate": "Veröffentlichungsdatum",
|
||||||
"viewRelease": "Veröffentlichung anzeigen",
|
"viewRelease": "Veröffentlichung anzeigen",
|
||||||
"autoUpdate": "Update installieren (experimentell)",
|
"autoUpdate": "Update installieren (experimentell)"
|
||||||
"autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
"wifiTxPower": "WiFi TX power",
|
"wifiTxPower": "WiFi TX power",
|
||||||
"settingsSaved": "Settings saved",
|
"settingsSaved": "Settings saved",
|
||||||
"errorSavingSettings": "Error saving settings",
|
"errorSavingSettings": "Error saving settings",
|
||||||
"ownDataSource": "BTClock data source",
|
"ownDataSource": "Use BTClock data source",
|
||||||
"flMaxBrightness": "Frontlight brightness",
|
"flMaxBrightness": "Frontlight brightness",
|
||||||
"flAlwaysOn": "Frontlight always on",
|
"flAlwaysOn": "Frontlight always on",
|
||||||
"flEffectDelay": "Frontlight effect speed",
|
"flEffectDelay": "Frontlight effect speed",
|
||||||
|
@ -40,13 +40,10 @@
|
||||||
"nostrPubKey": "Nostr source pubkey",
|
"nostrPubKey": "Nostr source pubkey",
|
||||||
"nostrZapKey": "Nostr zap pubkey",
|
"nostrZapKey": "Nostr zap pubkey",
|
||||||
"nostrRelay": "Nostr Relay",
|
"nostrRelay": "Nostr Relay",
|
||||||
"nostrZapNotify": "Enable Nostr Zap Notifications",
|
"nostrZapNotify": "Nostr Zap Notifications",
|
||||||
"useNostr": "Use Nostr data source",
|
"useNostr": "Use Nostr data source",
|
||||||
"bitaxeHostname": "BitAxe hostname or IP",
|
"bitaxeHostname": "BitAxe hostname or IP",
|
||||||
"bitaxeEnabled": "Enable BitAxe-integration",
|
"bitaxeEnabled": "Enable BitAxe",
|
||||||
"miningPoolStats": "Enable Mining Pool Stats integration",
|
|
||||||
"miningPoolName": "Mining Pool",
|
|
||||||
"miningPoolUser": "Mining Pool username or api key",
|
|
||||||
"nostrZapPubkey": "Nostr Zap pubkey",
|
"nostrZapPubkey": "Nostr Zap pubkey",
|
||||||
"invalidNostrPubkey": "Invalid Nostr pubkey, note that your pubkey does NOT start with npub.",
|
"invalidNostrPubkey": "Invalid Nostr pubkey, note that your pubkey does NOT start with npub.",
|
||||||
"convertingValidNpub": "Converting valid npub to pubkey",
|
"convertingValidNpub": "Converting valid npub to pubkey",
|
||||||
|
@ -56,7 +53,7 @@
|
||||||
"httpAuthPass": "WebUI Password",
|
"httpAuthPass": "WebUI Password",
|
||||||
"httpAuthText": "Only password-protects WebUI, not API-calls.",
|
"httpAuthText": "Only password-protects WebUI, not API-calls.",
|
||||||
"currencies": "Currencies",
|
"currencies": "Currencies",
|
||||||
"customSource": "Use custom data source endpoint",
|
"stagingSource": "Use Staging data source (for development)",
|
||||||
"useNostrTooltip": "Very experimental and unstable. Nostr data source is not required for Nostr Zap notifications.",
|
"useNostrTooltip": "Very experimental and unstable. Nostr data source is not required for Nostr Zap notifications.",
|
||||||
"mowMode": "Mow Suffix Mode",
|
"mowMode": "Mow Suffix Mode",
|
||||||
"suffixShareDot": "Suffix compact notation",
|
"suffixShareDot": "Suffix compact notation",
|
||||||
|
@ -70,27 +67,7 @@
|
||||||
"ledFlashOnZap": "LED flash on Nostr Zap",
|
"ledFlashOnZap": "LED flash on Nostr Zap",
|
||||||
"flFlashOnZap": "Frontlight flash on Nostr Zap",
|
"flFlashOnZap": "Frontlight flash on Nostr Zap",
|
||||||
"showAll": "Show all",
|
"showAll": "Show all",
|
||||||
"hideAll": "Hide all",
|
"hideAll": "Hide all"
|
||||||
"flOffWhenDark": "Frontlight off when dark",
|
|
||||||
"luxLightToggleText": "Set to 0 to disable",
|
|
||||||
"verticalDesc": "Use vertical screen description",
|
|
||||||
"enableDebugLog": "Enable Debug-log",
|
|
||||||
"dataSource": {
|
|
||||||
"label": "Data Source",
|
|
||||||
"btclock": "BTClock Data Source",
|
|
||||||
"thirdParty": "mempool.space/coincap.io",
|
|
||||||
"nostr": "Nostr publisher",
|
|
||||||
"custom": "Custom Endpoint"
|
|
||||||
},
|
|
||||||
"thirdPartySource": "Use mempool.space/coincap.io",
|
|
||||||
"ceDisableSSL": "Disable SSL",
|
|
||||||
"ceEndpoint": "Endpoint hostname",
|
|
||||||
"fontName": "Font",
|
|
||||||
"timeBasedDnd": "Enable Do Not Disturb time schedule",
|
|
||||||
"dndStartHour": "Start hour",
|
|
||||||
"dndStartMinute": "Start minute",
|
|
||||||
"dndEndHour": "End hour",
|
|
||||||
"dndEndMinute": "End minute"
|
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"systemInfo": "System info",
|
"systemInfo": "System info",
|
||||||
|
@ -120,9 +97,7 @@
|
||||||
"wifiSignalStrength": "WiFi Signal strength",
|
"wifiSignalStrength": "WiFi Signal strength",
|
||||||
"wsDataConnection": "BTClock data-source connection",
|
"wsDataConnection": "BTClock data-source connection",
|
||||||
"lightSensor": "Light sensor",
|
"lightSensor": "Light sensor",
|
||||||
"nostrConnection": "Nostr Relay connection",
|
"nostrConnection": "Nostr Relay connection"
|
||||||
"doNotDisturb": "Do not disturb",
|
|
||||||
"timeBasedDnd": "Time-based schedule"
|
|
||||||
},
|
},
|
||||||
"firmwareUpdater": {
|
"firmwareUpdater": {
|
||||||
"fileUploadFailed": "File upload failed. Make sure you have selected the correct file and try again.",
|
"fileUploadFailed": "File upload failed. Make sure you have selected the correct file and try again.",
|
||||||
|
@ -134,8 +109,7 @@
|
||||||
"latestVersion": "Latest Version",
|
"latestVersion": "Latest Version",
|
||||||
"releaseDate": "Release Date",
|
"releaseDate": "Release Date",
|
||||||
"viewRelease": "View Release",
|
"viewRelease": "View Release",
|
||||||
"autoUpdate": "Install update (experimental)",
|
"autoUpdate": "Install update (experimental)"
|
||||||
"autoUpdateInProgress": "Auto-update in progress, please wait..."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
"wifiTxPowerText": "En la mayoría de los casos no es necesario configurar esto.",
|
"wifiTxPowerText": "En la mayoría de los casos no es necesario configurar esto.",
|
||||||
"settingsSaved": "Configuración guardada",
|
"settingsSaved": "Configuración guardada",
|
||||||
"errorSavingSettings": "Error al guardar la configuración",
|
"errorSavingSettings": "Error al guardar la configuración",
|
||||||
"ownDataSource": "fuente de datos BTClock",
|
"ownDataSource": "Utilice la fuente de datos BTClock",
|
||||||
"flMaxBrightness": "Brillo de luz de la pantalla",
|
"flMaxBrightness": "Brillo de luz de la pantalla",
|
||||||
"flAlwaysOn": "Luz de la pantalla siempre encendida",
|
"flAlwaysOn": "Luz de la pantalla siempre encendida",
|
||||||
"flEffectDelay": "Velocidad del efecto de luz de la pantalla",
|
"flEffectDelay": "Velocidad del efecto de luz de la pantalla",
|
||||||
|
@ -54,25 +54,7 @@
|
||||||
"ledFlashOnZap": "LED parpadeante con Nostr Zap",
|
"ledFlashOnZap": "LED parpadeante con Nostr Zap",
|
||||||
"flFlashOnZap": "Flash de luz frontal con Nostr Zap",
|
"flFlashOnZap": "Flash de luz frontal con Nostr Zap",
|
||||||
"showAll": "Mostrar todo",
|
"showAll": "Mostrar todo",
|
||||||
"hideAll": "Ocultar todo",
|
"hideAll": "Ocultar todo"
|
||||||
"flOffWhenDark": "Luz de la pantalla cuando está oscuro",
|
|
||||||
"luxLightToggleText": "Establecer en 0 para desactivar",
|
|
||||||
"verticalDesc": "Descripción de pantalla vertical",
|
|
||||||
"enableDebugLog": "Habilitar registro de depuración",
|
|
||||||
"bitaxeEnabled": "Habilitar la integración de BitAxe",
|
|
||||||
"miningPoolStats": "Habilitar la integración de estadísticas del grupo minero",
|
|
||||||
"nostrZapNotify": "Habilitar notificaciones de Nostr Zap",
|
|
||||||
"thirdPartySource": "Utilice mempool.space/coincap.io",
|
|
||||||
"dataSource": {
|
|
||||||
"nostr": "editorial nostr",
|
|
||||||
"custom": "Punto final personalizado"
|
|
||||||
},
|
|
||||||
"fontName": "Fuente",
|
|
||||||
"timeBasedDnd": "Habilitar el horario de No molestar",
|
|
||||||
"dndStartHour": "Hora de inicio",
|
|
||||||
"dndStartMinute": "Minuto de inicio",
|
|
||||||
"dndEndHour": "Hora final",
|
|
||||||
"dndEndMinute": "Minuto final"
|
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"turnOff": "Apagar",
|
"turnOff": "Apagar",
|
||||||
|
@ -100,9 +82,7 @@
|
||||||
"wifiSignalStrength": "Fuerza de la señal WiFi",
|
"wifiSignalStrength": "Fuerza de la señal WiFi",
|
||||||
"wsDataConnection": "Conexión de fuente de datos BTClock",
|
"wsDataConnection": "Conexión de fuente de datos BTClock",
|
||||||
"lightSensor": "Sensor de luz",
|
"lightSensor": "Sensor de luz",
|
||||||
"nostrConnection": "Conexión de relé Nostr",
|
"nostrConnection": "Conexión de relé Nostr"
|
||||||
"doNotDisturb": "No molestar",
|
|
||||||
"timeBasedDnd": "Horario basado en el tiempo"
|
|
||||||
},
|
},
|
||||||
"firmwareUpdater": {
|
"firmwareUpdater": {
|
||||||
"fileUploadSuccess": "Archivo cargado exitosamente, reiniciando el dispositivo. Recargando WebUI en {countdown} segundos",
|
"fileUploadSuccess": "Archivo cargado exitosamente, reiniciando el dispositivo. Recargando WebUI en {countdown} segundos",
|
||||||
|
@ -114,8 +94,7 @@
|
||||||
"latestVersion": "Ultima versión",
|
"latestVersion": "Ultima versión",
|
||||||
"releaseDate": "Fecha de lanzamiento",
|
"releaseDate": "Fecha de lanzamiento",
|
||||||
"viewRelease": "Ver lanzamiento",
|
"viewRelease": "Ver lanzamiento",
|
||||||
"autoUpdate": "Instalar actualización (experimental)",
|
"autoUpdate": "Instalar actualización (experimental)"
|
||||||
"autoUpdateInProgress": "Actualización automática en progreso, espere..."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
|
|
|
@ -55,16 +55,7 @@
|
||||||
"ledFlashOnZap": "Knipper LED bij Nostr Zap",
|
"ledFlashOnZap": "Knipper LED bij Nostr Zap",
|
||||||
"flFlashOnZap": "Knipper displaylicht bij Nostr Zap",
|
"flFlashOnZap": "Knipper displaylicht bij Nostr Zap",
|
||||||
"showAll": "Toon alles",
|
"showAll": "Toon alles",
|
||||||
"hideAll": "Alles verbergen",
|
"hideAll": "Alles verbergen"
|
||||||
"flOffWhenDark": "Displaylicht uit als het donker is",
|
|
||||||
"luxLightToggleText": "Stel in op 0 om uit te schakelen",
|
|
||||||
"verticalDesc": "Verticale schermbeschrijving",
|
|
||||||
"fontName": "Lettertype",
|
|
||||||
"timeBasedDnd": "Schakel het tijdschema Niet storen in",
|
|
||||||
"dndStartHour": "Begin uur",
|
|
||||||
"dndStartMinute": "Beginminuut",
|
|
||||||
"dndEndHour": "Eind uur",
|
|
||||||
"dndEndMinute": "Einde minuut"
|
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"systemInfo": "Systeeminformatie",
|
"systemInfo": "Systeeminformatie",
|
||||||
|
@ -91,9 +82,7 @@
|
||||||
"wifiSignalStrength": "WiFi signaalsterkte",
|
"wifiSignalStrength": "WiFi signaalsterkte",
|
||||||
"wsDataConnection": "BTClock-gegevensbron verbinding",
|
"wsDataConnection": "BTClock-gegevensbron verbinding",
|
||||||
"lightSensor": "Licht sensor",
|
"lightSensor": "Licht sensor",
|
||||||
"nostrConnection": "Nostr Relay-verbinding",
|
"nostrConnection": "Nostr Relay-verbinding"
|
||||||
"doNotDisturb": "Niet storen",
|
|
||||||
"timeBasedDnd": "Op tijd gebaseerd schema"
|
|
||||||
},
|
},
|
||||||
"firmwareUpdater": {
|
"firmwareUpdater": {
|
||||||
"fileUploadSuccess": "Bestand geüpload, apparaat herstart. WebUI opnieuw geladen over {countdown} seconden",
|
"fileUploadSuccess": "Bestand geüpload, apparaat herstart. WebUI opnieuw geladen over {countdown} seconden",
|
||||||
|
@ -105,8 +94,7 @@
|
||||||
"latestVersion": "Laatste versie",
|
"latestVersion": "Laatste versie",
|
||||||
"releaseDate": "Datum van publicatie",
|
"releaseDate": "Datum van publicatie",
|
||||||
"viewRelease": "Bekijk publicatie",
|
"viewRelease": "Bekijk publicatie",
|
||||||
"autoUpdate": "Update installeren (experimenteel)",
|
"autoUpdate": "Update installeren (experimenteel)"
|
||||||
"autoUpdateInProgress": "Automatische update wordt uitgevoerd. Even geduld a.u.b...."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
|
|
|
@ -1,23 +1,11 @@
|
||||||
@use '@fontsource/ubuntu/scss/mixins' as Ubuntu;
|
|
||||||
@use '@fontsource/antonio/scss/mixins' as Antonio;
|
|
||||||
|
|
||||||
@import '../node_modules/bootstrap/scss/functions';
|
@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/antonio/latin-400.css";
|
||||||
|
@import '@fontsource/ubuntu/latin-400.css';
|
||||||
@include Ubuntu.faces(
|
//@import '@fontsource/oswald/latin-400.css';
|
||||||
$subsets: latin,
|
@import '@fontsource/antonio/latin-400.css';
|
||||||
$weights: 400,
|
|
||||||
$formats: 'woff2',
|
|
||||||
$directory: '@fontsource/ubuntu/files'
|
|
||||||
);
|
|
||||||
|
|
||||||
@include Antonio.faces(
|
|
||||||
$subsets: latin,
|
|
||||||
$weights: 400,
|
|
||||||
$formats: 'woff2',
|
|
||||||
$directory: '@fontsource/antonio/files'
|
|
||||||
);
|
|
||||||
|
|
||||||
@import './satsymbol';
|
@import './satsymbol';
|
||||||
|
|
||||||
|
@ -26,8 +14,6 @@ $font-family-base: 'Ubuntu';
|
||||||
$font-size-base: 0.9rem;
|
$font-size-base: 0.9rem;
|
||||||
$input-font-size-sm: $font-size-base * 0.875;
|
$input-font-size-sm: $font-size-base * 0.875;
|
||||||
|
|
||||||
@import '../node_modules/bootstrap/scss/variables';
|
|
||||||
@import '../node_modules/bootstrap/scss/variables-dark';
|
|
||||||
// $border-radius: .675rem;
|
// $border-radius: .675rem;
|
||||||
|
|
||||||
@import '../node_modules/bootstrap/scss/mixins';
|
@import '../node_modules/bootstrap/scss/mixins';
|
||||||
|
@ -53,46 +39,10 @@ $input-font-size-sm: $font-size-base * 0.875;
|
||||||
@import '../node_modules/bootstrap/scss/tooltip';
|
@import '../node_modules/bootstrap/scss/tooltip';
|
||||||
@import '../node_modules/bootstrap/scss/toasts';
|
@import '../node_modules/bootstrap/scss/toasts';
|
||||||
@import '../node_modules/bootstrap/scss/alert';
|
@import '../node_modules/bootstrap/scss/alert';
|
||||||
@import '../node_modules/bootstrap/scss/placeholders';
|
|
||||||
@import '../node_modules/bootstrap/scss/spinners';
|
|
||||||
|
|
||||||
@import '../node_modules/bootstrap/scss/helpers';
|
@import '../node_modules/bootstrap/scss/helpers';
|
||||||
@import '../node_modules/bootstrap/scss/utilities/api';
|
@import '../node_modules/bootstrap/scss/utilities/api';
|
||||||
|
|
||||||
/* Default state (xs) - sticky */
|
|
||||||
.sticky-xs-top {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1020;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
|
||||||
main {
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove sticky behavior for larger screens */
|
|
||||||
@media (min-width: 576px) {
|
|
||||||
.sticky-xs-top {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include color-mode(dark) {
|
|
||||||
.navbar {
|
|
||||||
--bs-navbar-color: $light;
|
|
||||||
background-color: $dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include color-mode(light) {
|
|
||||||
.navbar {
|
|
||||||
--bs-navbar-color: $dark;
|
|
||||||
background-color: $light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
@ -103,23 +53,6 @@ nav {
|
||||||
|
|
||||||
.btn-group-sm .btn {
|
.btn-group-sm .btn {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
// text-overflow: ellipsis;
|
|
||||||
// white-space: nowrap;
|
|
||||||
// overflow: hidden;
|
|
||||||
// width: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-group-sm {
|
|
||||||
display: flex !important;
|
|
||||||
flex-wrap: wrap !important;
|
|
||||||
gap: 0.25rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the border radius override that Bootstrap applies */
|
|
||||||
.btn-group-sm > .btn {
|
|
||||||
border-radius: 0.25rem !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
position: relative !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#customText {
|
#customText {
|
||||||
|
@ -130,7 +63,7 @@ nav {
|
||||||
.btclock {
|
.btclock {
|
||||||
background: #000;
|
background: #000;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: calc(2vw + 2vh);
|
font-size: calc(3vw + 3vh);
|
||||||
font-family: 'Antonio', sans-serif;
|
font-family: 'Antonio', sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -171,25 +104,19 @@ nav {
|
||||||
flex-direction: column; /* Stack the text and line vertically */
|
flex-direction: column; /* Stack the text and line vertically */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-around; /* Distribute items with space between */
|
justify-content: space-around; /* Distribute items with space between */
|
||||||
padding: 5px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.verticalDesc > .splitText:first-child {
|
.splitText div:first-child::after {
|
||||||
.textcontainer {
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitText .textcontainer :first-child::after {
|
|
||||||
display: block;
|
display: block;
|
||||||
content: '';
|
content: '';
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
border-bottom: 2px solid;
|
border-bottom: 2px solid;
|
||||||
// margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitText {
|
.splitText {
|
||||||
font-size: calc(0.3vw + 1vh);
|
font-size: calc(0.5vw + 1vh);
|
||||||
|
|
||||||
.top-text,
|
.top-text,
|
||||||
.bottom-text {
|
.bottom-text {
|
||||||
|
@ -315,38 +242,3 @@ nav {
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightMode .bitaxelogo {
|
|
||||||
filter: brightness(0) saturate(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.connection-lost-overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.75);
|
|
||||||
z-index: 1050;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.overlay-content {
|
|
||||||
background-color: rgba(255, 255, 255, 0.75);
|
|
||||||
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 1rem;
|
|
||||||
color: $danger;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Satoshi Symbol';
|
font-family: 'Satoshi Symbol';
|
||||||
src: url('/fonts/Satoshi_Symbol.woff2') format('woff2');
|
src:
|
||||||
|
url('/fonts/Satoshi_Symbol.woff2') format('woff2'),
|
||||||
|
url('/fonts/Satoshi_Symbol.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
export enum DataSourceType {
|
|
||||||
BTCLOCK_SOURCE = 0,
|
|
||||||
THIRD_PARTY_SOURCE = 1,
|
|
||||||
NOSTR_SOURCE = 2,
|
|
||||||
CUSTOM_SOURCE = 3
|
|
||||||
}
|
|
|
@ -12,12 +12,10 @@
|
||||||
NavbarBrand,
|
NavbarBrand,
|
||||||
NavbarToggler
|
NavbarToggler
|
||||||
} from '@sveltestrap/sveltestrap';
|
} from '@sveltestrap/sveltestrap';
|
||||||
import { _ } from 'svelte-i18n';
|
|
||||||
|
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { locale, locales, isLoading } from 'svelte-i18n';
|
import { locale, locales, isLoading } from 'svelte-i18n';
|
||||||
import { ColorSchemeSwitcher } from '$lib/components';
|
import ColorSchemeSwitcher from '../components/ColorSchemeSwitcher.svelte';
|
||||||
import { derived } from 'svelte/store';
|
|
||||||
|
|
||||||
export const setLocale = (lang: string) => () => {
|
export const setLocale = (lang: string) => () => {
|
||||||
locale.set(lang);
|
locale.set(lang);
|
||||||
|
@ -40,20 +38,19 @@
|
||||||
return flagMap[lowercaseCode];
|
return flagMap[lowercaseCode];
|
||||||
} else {
|
} else {
|
||||||
// Return null for unsupported language codes
|
// Return null for unsupported language codes
|
||||||
return flagMap['en'];
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let languageNames = {};
|
let languageNames = {};
|
||||||
|
|
||||||
const currentLocale = derived(locale, ($locale) => $locale || 'en');
|
|
||||||
|
|
||||||
locale.subscribe(() => {
|
locale.subscribe(() => {
|
||||||
const localeToUse = $locale || 'en';
|
if ($locale) {
|
||||||
let newLanguageNames = new Intl.DisplayNames([localeToUse], { type: 'language' });
|
let newLanguageNames = new Intl.DisplayNames([$locale], { type: 'language' });
|
||||||
|
|
||||||
for (let l of $locales) {
|
for (let l of $locales) {
|
||||||
languageNames[l] = newLanguageNames.of(l) || l;
|
languageNames[l] = newLanguageNames.of(l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -64,23 +61,8 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Navbar expand="md" sticky="xs-top" theme="auto">
|
<Navbar expand="md">
|
||||||
<NavbarBrand class="d-none d-sm-block">₿TClock</NavbarBrand>
|
<NavbarBrand>₿TClock</NavbarBrand>
|
||||||
<Nav class="d-md-none" pills>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink href="#control" active>{$_('section.control.title', { default: 'Control' })}</NavLink
|
|
||||||
>
|
|
||||||
</NavItem>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink href="#status">{$_('section.status.title', { default: 'Status' })}</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink class="nav-link" href="#settings"
|
|
||||||
>{$_('section.settings.title', { default: 'Settings' })}</NavLink
|
|
||||||
>
|
|
||||||
</NavItem>
|
|
||||||
</Nav>
|
|
||||||
|
|
||||||
<NavbarToggler on:click={toggle} />
|
<NavbarToggler on:click={toggle} />
|
||||||
|
|
||||||
<Collapse {isOpen} navbar expand="sm">
|
<Collapse {isOpen} navbar expand="sm">
|
||||||
|
@ -97,10 +79,7 @@
|
||||||
</Nav>
|
</Nav>
|
||||||
{#if !$isLoading}
|
{#if !$isLoading}
|
||||||
<Dropdown id="nav-language-dropdown" inNavbar class="me-3">
|
<Dropdown id="nav-language-dropdown" inNavbar class="me-3">
|
||||||
<DropdownToggle nav caret
|
<DropdownToggle nav caret>{getFlagEmoji($locale)} {languageNames[$locale]}</DropdownToggle>
|
||||||
>{getFlagEmoji($currentLocale)}
|
|
||||||
{languageNames[$currentLocale] || 'English'}</DropdownToggle
|
|
||||||
>
|
|
||||||
<DropdownMenu end>
|
<DropdownMenu end>
|
||||||
{#each $locales as locale}
|
{#each $locales as locale}
|
||||||
<DropdownItem on:click={setLocale(locale)}
|
<DropdownItem on:click={setLocale(locale)}
|
||||||
|
@ -115,6 +94,4 @@
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|
||||||
<!-- +layout.svelte -->
|
<!-- +layout.svelte -->
|
||||||
<main>
|
<slot />
|
||||||
<slot />
|
|
||||||
</main>
|
|
||||||
|
|
|
@ -6,15 +6,10 @@ import { locale, waitLocale } from 'svelte-i18n';
|
||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from './$types';
|
||||||
|
|
||||||
export const load: LayoutLoad = async () => {
|
export const load: LayoutLoad = async () => {
|
||||||
if (browser) {
|
if (browser && localStorage.getItem('locale')) {
|
||||||
if (localStorage.getItem('locale')) {
|
locale.set(localStorage.getItem('locale'));
|
||||||
locale.set(localStorage.getItem('locale'));
|
} else if (browser) {
|
||||||
} else {
|
locale.set(window.navigator.language);
|
||||||
// Normalize the browser locale
|
|
||||||
const browserLocale = window.navigator.language.split('-')[0].toLowerCase();
|
|
||||||
const supportedLocales = ['en', 'nl', 'es', 'de'];
|
|
||||||
locale.set(supportedLocales.includes(browserLocale) ? browserLocale : 'en');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await waitLocale();
|
await waitLocale();
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { screenSize, updateScreenSize } from '$lib/screen';
|
import { screenSize, updateScreenSize } from '$lib/screen';
|
||||||
|
|
||||||
import { Container, Row, Toast, ToastBody } from '@sveltestrap/sveltestrap';
|
import { Container, Row, Toast, ToastBody } from '@sveltestrap/sveltestrap';
|
||||||
import { replaceState } from '$app/navigation';
|
|
||||||
|
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
@ -14,10 +13,15 @@
|
||||||
|
|
||||||
let settings = writable({
|
let settings = writable({
|
||||||
fgColor: '0',
|
fgColor: '0',
|
||||||
bgColor: '0',
|
bgColor: '0'
|
||||||
isLoaded: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// let uiSettings = writable({
|
||||||
|
// inputSize: 'sm',
|
||||||
|
// selectClass: '',
|
||||||
|
// btnSize: 'lg'
|
||||||
|
// });
|
||||||
|
|
||||||
let status = writable({
|
let status = writable({
|
||||||
data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'],
|
data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'],
|
||||||
espFreeHeap: 0,
|
espFreeHeap: 0,
|
||||||
|
@ -26,126 +30,48 @@
|
||||||
price: false,
|
price: false,
|
||||||
blocks: false
|
blocks: false
|
||||||
},
|
},
|
||||||
leds: [],
|
leds: []
|
||||||
isUpdating: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchStatusData = async () => {
|
const fetchStatusData = () => {
|
||||||
const res = await fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' });
|
fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' })
|
||||||
|
.then((res) => res.json())
|
||||||
if (!res.ok) {
|
.then((data) => {
|
||||||
console.error('Error fetching status data:', res.statusText);
|
status.set(data);
|
||||||
return false;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
status.set(data);
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchSettingsData = async () => {
|
const fetchSettingsData = () => {
|
||||||
const res = await fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' });
|
fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
data.fgColor = String(data.fgColor);
|
||||||
|
data.bgColor = String(data.bgColor);
|
||||||
|
data.timePerScreen = data.timerSeconds / 60;
|
||||||
|
|
||||||
if (!res.ok) {
|
if (data.fgColor > 65535) {
|
||||||
console.error('Error fetching settings data:', res.statusText);
|
data.fgColor = '65535';
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
data.fgColor = String(data.fgColor);
|
|
||||||
data.bgColor = String(data.bgColor);
|
|
||||||
data.timePerScreen = data.timerSeconds / 60;
|
|
||||||
|
|
||||||
if (data.fgColor > 65535) {
|
|
||||||
data.fgColor = '65535';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.bgColor > 65535) {
|
|
||||||
data.bgColor = '65535';
|
|
||||||
}
|
|
||||||
settings.set(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
let sections: (HTMLElement | null)[];
|
|
||||||
let observer: IntersectionObserver;
|
|
||||||
const SM_BREAKPOINT = 576;
|
|
||||||
|
|
||||||
const setupObserver = () => {
|
|
||||||
if (window.innerWidth < SM_BREAKPOINT) {
|
|
||||||
observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
entries.forEach((entry) => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
const id = entry.target.id;
|
|
||||||
replaceState(`#${id}`);
|
|
||||||
|
|
||||||
// Update nav pills
|
|
||||||
document.querySelectorAll('.nav-link').forEach((link) => {
|
|
||||||
link.classList.remove('active');
|
|
||||||
if (link.getAttribute('href') === `#${id}`) {
|
|
||||||
link.classList.add('active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
threshold: 0.25 // Trigger when section is 50% visible
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
sections = ['control', 'status', 'settings'].map((id) => document.getElementById(id));
|
if (data.bgColor > 65535) {
|
||||||
|
data.bgColor = '65535';
|
||||||
sections.forEach((section) => observer.observe(section!));
|
}
|
||||||
}
|
settings.set(data);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(() => {
|
||||||
setupObserver();
|
fetchSettingsData();
|
||||||
|
fetchStatusData();
|
||||||
|
|
||||||
const connectEventSource = () => {
|
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
|
||||||
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
|
|
||||||
|
|
||||||
evtSource.addEventListener('status', (e) => {
|
evtSource.addEventListener('status', (e) => {
|
||||||
let dataObj = JSON.parse(e.data);
|
let dataObj = JSON.parse(e.data);
|
||||||
status.update((s) => ({ ...s, isUpdating: true }));
|
status.set(dataObj);
|
||||||
status.set(dataObj);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
evtSource.addEventListener('message', (e) => {
|
|
||||||
if (e.data == 'closing') {
|
|
||||||
console.log('EventSource closing');
|
|
||||||
status.update((s) => ({ ...s, isUpdating: false }));
|
|
||||||
evtSource.close(); // Close the current connection
|
|
||||||
setTimeout(connectEventSource, 5000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
evtSource.addEventListener('error', (e) => {
|
|
||||||
console.error('EventSource failed:', e);
|
|
||||||
status.update((s) => ({ ...s, isUpdating: false }));
|
|
||||||
evtSource.close(); // Close the current connection
|
|
||||||
setTimeout(connectEventSource, 1000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fetchSettingsData();
|
|
||||||
if (await fetchStatusData()) {
|
|
||||||
settings.update((s) => ({ ...s, isLoaded: true }));
|
|
||||||
connectEventSource();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Error fetching data:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
if (observer) {
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
setupObserver();
|
|
||||||
|
|
||||||
updateScreenSize();
|
updateScreenSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,11 +123,9 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Container fluid>
|
<Container fluid>
|
||||||
<Row class="placeholder-glow">
|
<Row>
|
||||||
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
|
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
|
||||||
|
|
||||||
<Status bind:settings bind:status lg="6" xxl="4"></Status>
|
<Status bind:settings bind:status lg="6" xxl="4"></Status>
|
||||||
|
|
||||||
<Settings bind:settings on:showToast={showToast} on:formReset={fetchSettingsData} lg="3" xxl="4"
|
<Settings bind:settings on:showToast={showToast} on:formReset={fetchSettingsData} lg="3" xxl="4"
|
||||||
></Settings>
|
></Settings>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
} from '@sveltestrap/sveltestrap';
|
} from '@sveltestrap/sveltestrap';
|
||||||
import FirmwareUpdater from './FirmwareUpdater.svelte';
|
import FirmwareUpdater from './FirmwareUpdater.svelte';
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
import { uiSettings } from '$lib/uiSettings';
|
||||||
import { Placeholder } from '$lib/components';
|
|
||||||
|
|
||||||
export let settings = {};
|
export let settings = {};
|
||||||
|
|
||||||
|
@ -106,8 +105,8 @@
|
||||||
export let xxl = xl;
|
export let xxl = xl;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Col {xs} {sm} {md} {lg} {xl} {xxl} class="mb-4 mb-xl-0">
|
<Col {xs} {sm} {md} {lg} {xl} {xxl}>
|
||||||
<Card id="control">
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{$_('section.control.title', { default: 'Control' })}</CardTitle>
|
<CardTitle>{$_('section.control.title', { default: 'Control' })}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@ -215,16 +214,15 @@
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
<li>
|
<li>
|
||||||
{$_('section.control.buildTime')}: <Placeholder
|
{$_('section.control.buildTime')}: {new Date(
|
||||||
value={new Date($settings.lastBuildTime * 1000).toLocaleString()}
|
$settings.lastBuildTime * 1000
|
||||||
checkValue={$settings.lastBuildTime}
|
).toLocaleString()}
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
<li>IP: <Placeholder value={$settings.ip} /></li>
|
<li>IP: {$settings.ip}</li>
|
||||||
<li>HW revision: <Placeholder value={$settings.hwRev} /></li>
|
<li>HW revision: {$settings.hwRev}</li>
|
||||||
<li>{$_('section.control.fwCommit')}: <Placeholder value={$settings.gitRev} /></li>
|
<li>{$_('section.control.fwCommit')}: {$settings.gitRev}</li>
|
||||||
<li>WebUI commit: <Placeholder value={$settings.fsRev} /></li>
|
<li>WebUI commit: {$settings.fsRev}</li>
|
||||||
<li>{$_('section.control.hostname')}: <Placeholder value={$settings.hostname} /></li>
|
<li>{$_('section.control.hostname')}: {$settings.hostname}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<Row>
|
<Row>
|
||||||
<Col class="d-flex justify-content-end">
|
<Col class="d-flex justify-content-end">
|
||||||
|
@ -241,7 +239,7 @@
|
||||||
{#if $settings.otaEnabled}
|
{#if $settings.otaEnabled}
|
||||||
<hr />
|
<hr />
|
||||||
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
||||||
<FirmwareUpdater on:showToast bind:settings bind:status />
|
<FirmwareUpdater on:showToast bind:settings />
|
||||||
{/if}
|
{/if}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -4,12 +4,11 @@
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { Progress, Alert, Button } from '@sveltestrap/sveltestrap';
|
import { Progress, Alert, Button } from '@sveltestrap/sveltestrap';
|
||||||
import HourglassSplitIcon from 'svelte-bootstrap-icons/lib/HourglassSplit.svelte';
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let settings = { hwRev: '' };
|
export let settings = { hwRev: '' };
|
||||||
export let status = writable({ isOTAUpdating: false });
|
|
||||||
let currentVersion: string = $settings.gitTag; // Replace with your current version
|
let currentVersion: string = $settings.gitTag; // Replace with your current version
|
||||||
|
|
||||||
let latestVersion: string = '';
|
let latestVersion: string = '';
|
||||||
|
@ -113,25 +112,6 @@
|
||||||
return binaryFilename;
|
return binaryFilename;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWebUiBinaryName = () => {
|
|
||||||
let webuiFilename = '';
|
|
||||||
switch ($settings.hwRev) {
|
|
||||||
case 'REV_V8_EPD_2_13':
|
|
||||||
webuiFilename = 'littlefs_16MB.bin';
|
|
||||||
break;
|
|
||||||
case 'REV_B_EPD_2_13':
|
|
||||||
webuiFilename = 'littlefs_8MB.bin';
|
|
||||||
break;
|
|
||||||
case 'REV_A_EPD_2_13':
|
|
||||||
webuiFilename = 'littlefs_4MB.bin';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
webuiFilename = 'Unsupported hardware, unable to determine WebUI binary filename';
|
|
||||||
}
|
|
||||||
|
|
||||||
return webuiFilename;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAutoUpdate = async (e: Event) => {
|
const onAutoUpdate = async (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
@ -208,12 +188,8 @@
|
||||||
)}: {releaseDate} -
|
)}: {releaseDate} -
|
||||||
<a href={releaseUrl} target="_blank">{$_('section.firmwareUpdater.viewRelease')}</a><br />
|
<a href={releaseUrl} target="_blank">{$_('section.firmwareUpdater.viewRelease')}</a><br />
|
||||||
{#if isNewerVersionAvailable}
|
{#if isNewerVersionAvailable}
|
||||||
{#if !$status.isOTAUpdating}
|
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
||||||
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
||||||
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
|
||||||
{:else}
|
|
||||||
<HourglassSplitIcon /> {$_('section.firmwareUpdater.autoUpdateInProgress')}
|
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
{$_('section.firmwareUpdater.swUpToDate')}
|
{$_('section.firmwareUpdater.swUpToDate')}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -223,59 +199,57 @@
|
||||||
{:else}
|
{:else}
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !$status.isOTAUpdating}
|
<section class="row row-cols-lg-auto align-items-end">
|
||||||
<section class="row row-cols-lg-auto align-items-end">
|
<div class="col-12">
|
||||||
<div class="col flex-fill">
|
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
||||||
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
<input
|
||||||
<input
|
type="file"
|
||||||
type="file"
|
id="firmwareFile"
|
||||||
id="firmwareFile"
|
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
||||||
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
name="update"
|
||||||
name="update"
|
class="form-control"
|
||||||
class="form-control"
|
accept=".bin"
|
||||||
accept=".bin"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div class="flex-fill">
|
||||||
<div class="flex-fill">
|
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
||||||
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
>Update firmware</Button
|
||||||
>Update firmware</Button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col flex-fill">
|
|
||||||
<label for="webuiFile" class="form-label">WebUI file ({getWebUiBinaryName()})</label>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="webuiFile"
|
|
||||||
name="update"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="littlefs.bin"
|
|
||||||
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
|
||||||
accept=".bin"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex-fill">
|
|
||||||
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
|
||||||
>Update WebUI</Button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{#if firmwareUploadProgress > 0}
|
|
||||||
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
|
||||||
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
|
||||||
>
|
>
|
||||||
{/if}
|
</div>
|
||||||
{#if firmwareUploadSuccess}
|
<div class="col mt-2">
|
||||||
<Alert color="success" class="firmwareUploadStatusAlert"
|
<label for="webuiFile" class="form-label">WebUI file (littlefs.bin)</label>
|
||||||
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
<input
|
||||||
</Alert>
|
type="file"
|
||||||
{/if}
|
id="webuiFile"
|
||||||
|
name="update"
|
||||||
{#if firmwareUploadError}
|
class="form-control"
|
||||||
<Alert color="danger" class="firmwareUploadStatusAlert"
|
placeholder="littlefs.bin"
|
||||||
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
||||||
|
accept=".bin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex-fill">
|
||||||
|
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
||||||
|
>Update WebUI</Button
|
||||||
>
|
>
|
||||||
{/if}
|
</div>
|
||||||
<small
|
</section>
|
||||||
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
{#if firmwareUploadProgress > 0}
|
||||||
|
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
||||||
|
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if firmwareUploadSuccess}
|
||||||
|
<Alert color="success" class="firmwareUploadStatusAlert"
|
||||||
|
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
||||||
|
</Alert>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if firmwareUploadError}
|
||||||
|
<Alert color="danger" class="firmwareUploadStatusAlert"
|
||||||
|
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<small
|
||||||
|
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
||||||
|
>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
export let className = 'btclock-wrapper';
|
export let className = 'btclock-wrapper';
|
||||||
export let verticalDesc = false;
|
|
||||||
// Define the currency symbols as constants
|
// Define the currency symbols as constants
|
||||||
const CURRENCY_USD = '$';
|
const CURRENCY_USD = '$';
|
||||||
const CURRENCY_EUR = '[';
|
const CURRENCY_EUR = '[';
|
||||||
|
@ -44,22 +44,21 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={className} id={className}>
|
<div class={className} id={className}>
|
||||||
<div class={'btclock' + (verticalDesc ? ' verticalDesc' : '')}>
|
<div class="btclock">
|
||||||
{#each status.data as char}
|
{#each status.data as char}
|
||||||
{#if isSplitText(char)}
|
{#if isSplitText(char)}
|
||||||
<div class="splitText">
|
<div class="splitText">
|
||||||
<div class="textcontainer">
|
{#if char.split('/').length}
|
||||||
{#if char.split('/').length}
|
<span class="top-text">{char.split('/')[0]}</span>
|
||||||
<span class="top-text">{char.split('/')[0]}</span>
|
<hr />
|
||||||
<span class="bottom-text">{char.split('/')[1]}</span>
|
<span class="bottom-text">{char.split('/')[1]}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
<!-- {#each char.split('/') as part}
|
<!-- {#each char.split('/') as part}
|
||||||
<div class="flex-items">{part}</div>
|
<div class="flex-items">{part}</div>
|
||||||
{/each} -->
|
{/each} -->
|
||||||
</div>
|
</div>
|
||||||
{:else if char.startsWith('mdi')}
|
{:else if char.startsWith('mdi')}
|
||||||
<div class={'digit icon' + (char.endsWith('bitaxe') ? ' icon-img' : '')}>
|
<div class="digit icon">
|
||||||
{#if char.endsWith('rocket')}
|
{#if char.endsWith('rocket')}
|
||||||
<RocketIcon></RocketIcon>
|
<RocketIcon></RocketIcon>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -69,12 +68,6 @@
|
||||||
{#if char.endsWith('bolt')}
|
{#if char.endsWith('bolt')}
|
||||||
<ZapIcon></ZapIcon>
|
<ZapIcon></ZapIcon>
|
||||||
{/if}
|
{/if}
|
||||||
{#if char.endsWith('bitaxe')}
|
|
||||||
<img src="/bitaxe.webp" class="bitaxelogo" alt="BitAxe logo" />
|
|
||||||
{/if}
|
|
||||||
{#if char.endsWith('miningpool')}
|
|
||||||
<span class="pool-logo">Mining Pool Logo</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{:else if char === 'STS'}
|
{:else if char === 'STS'}
|
||||||
<div class="digit sats">S</div>
|
<div class="digit sats">S</div>
|
||||||
|
@ -89,26 +82,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style>
|
||||||
.icon {
|
.icon {
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btclock-wrapper .btclock .icon.icon-img {
|
|
||||||
// padding: 0 15px;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
width: calc(100 / 7);
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 95%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bitaxelogo {
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pool-logo {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,6 @@
|
||||||
Row
|
Row
|
||||||
} from '@sveltestrap/sveltestrap';
|
} from '@sveltestrap/sveltestrap';
|
||||||
import Rendered from './Rendered.svelte';
|
import Rendered from './Rendered.svelte';
|
||||||
import { DataSourceType } from '$lib/types/dataSource';
|
|
||||||
|
|
||||||
export let settings;
|
export let settings;
|
||||||
export let status: writable<object>;
|
export let status: writable<object>;
|
||||||
|
@ -75,7 +74,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.subscribe((value: object) => {
|
settings.subscribe((value: object) => {
|
||||||
lightMode = !value.invertedColor;
|
lightMode = value.bgColor > value.fgColor;
|
||||||
|
|
||||||
if (value.screens) buttonChunks = chunkArray(value.screens, 5);
|
if (value.screens) buttonChunks = chunkArray(value.screens, 5);
|
||||||
});
|
});
|
||||||
|
@ -97,16 +96,6 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDoNotDisturb = (currentStatus: boolean) => (e: Event) => {
|
|
||||||
e.preventDefault();
|
|
||||||
console.log(currentStatus);
|
|
||||||
if (!currentStatus) {
|
|
||||||
fetch(`${PUBLIC_BASE_URL}/api/dnd/enable`);
|
|
||||||
} else {
|
|
||||||
fetch(`${PUBLIC_BASE_URL}/api/dnd/disable`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export let xs = 12;
|
export let xs = 12;
|
||||||
export let sm = xs;
|
export let sm = xs;
|
||||||
export let md = sm;
|
export let md = sm;
|
||||||
|
@ -115,35 +104,17 @@
|
||||||
export let xxl = xl;
|
export let xxl = xl;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Col {xs} {sm} {md} {lg} {xl} {xxl} class="mb-4 mb-xl-0">
|
<Col {xs} {sm} {md} {lg} {xl} {xxl}>
|
||||||
<Card id="status">
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
|
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{#if $settings.isLoaded === false}
|
{#if $settings.screens}
|
||||||
<div class="d-flex align-items-center">
|
<div class=" d-block d-sm-none mx-auto text-center">
|
||||||
<strong role="status">Loading...</strong>
|
{#each buttonChunks as chunk}
|
||||||
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
<ButtonGroup size="sm" class="mx-auto mb-1">
|
||||||
</div>
|
{#each chunk as s}
|
||||||
{:else}
|
|
||||||
{#if $settings.screens}
|
|
||||||
<div class=" d-block d-sm-none mx-auto text-center">
|
|
||||||
{#each buttonChunks as chunk}
|
|
||||||
<ButtonGroup size="sm" class="mx-auto mb-1">
|
|
||||||
{#each chunk as s}
|
|
||||||
<Button
|
|
||||||
color="outline-primary"
|
|
||||||
active={$status.currentScreen == s.id}
|
|
||||||
on:click={setScreen(s.id)}>{s.name}</Button
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</ButtonGroup>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
<div class="d-flex justify-content-center d-none d-sm-flex">
|
|
||||||
<ButtonGroup size="sm">
|
|
||||||
{#each $settings.screens as s}
|
|
||||||
<Button
|
<Button
|
||||||
color="outline-primary"
|
color="outline-primary"
|
||||||
active={$status.currentScreen == s.id}
|
active={$status.currentScreen == s.id}
|
||||||
|
@ -151,169 +122,144 @@
|
||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
{/each}
|
||||||
{#if $settings.actCurrencies && ($settings.dataSource == DataSourceType.BTCLOCK_SOURCE || $settings.dataSource == DataSourceType.CUSTOM_SOURCE)}
|
</div>
|
||||||
<div class="d-flex justify-content-center d-sm-flex mt-2">
|
<div class="d-flex justify-content-center d-none d-sm-flex">
|
||||||
<ButtonGroup size="sm">
|
<ButtonGroup size="sm">
|
||||||
{#each $settings.actCurrencies as c}
|
{#each $settings.screens as s}
|
||||||
<Button
|
<Button
|
||||||
color="outline-success"
|
color="outline-primary"
|
||||||
active={$status.currency == c}
|
active={$status.currentScreen == s.id}
|
||||||
on:click={setCurrency(c)}>{c}</Button
|
on:click={setScreen(s.id)}>{s.name}</Button
|
||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{#if $settings.actCurrencies && $settings.ownDataSource}
|
||||||
<hr />
|
<div class="d-flex justify-content-center d-sm-flex mt-2">
|
||||||
{#if $status.data}
|
<ButtonGroup size="sm">
|
||||||
<section class={lightMode ? 'lightMode' : 'darkMode'} style="position: relative;">
|
{#each $settings.actCurrencies as c}
|
||||||
{#if $status.isUpdating === false && ($status.isFake ?? false) === false}
|
<Button
|
||||||
<div class="connection-lost-overlay">
|
color="outline-success"
|
||||||
<div class="overlay-content">
|
active={$status.currency == c}
|
||||||
<i class="bi bi-wifi-off"></i>
|
on:click={setCurrency(c)}>{c}</Button
|
||||||
<h4>Lost connection</h4>
|
>
|
||||||
<p>Trying to reconnect...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<Rendered
|
|
||||||
status={$status}
|
|
||||||
className="btclock-wrapper"
|
|
||||||
verticalDesc={$settings.verticalDesc}
|
|
||||||
></Rendered>
|
|
||||||
</section>
|
|
||||||
{$_('section.status.screenCycle')}:
|
|
||||||
<a
|
|
||||||
id="timerStatusText"
|
|
||||||
href={'#'}
|
|
||||||
style="cursor: pointer"
|
|
||||||
tabindex="0"
|
|
||||||
role="button"
|
|
||||||
aria-pressed="false"
|
|
||||||
on:click={toggleTimer($status.timerRunning)}
|
|
||||||
>{#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_(
|
|
||||||
'timer.stopped'
|
|
||||||
)}{/if}</a
|
|
||||||
><br />
|
|
||||||
|
|
||||||
{$_('section.status.doNotDisturb')}:
|
|
||||||
<a
|
|
||||||
id="dndStatusText"
|
|
||||||
href={'#'}
|
|
||||||
style="cursor: pointer"
|
|
||||||
tabindex="0"
|
|
||||||
role="button"
|
|
||||||
aria-pressed="false"
|
|
||||||
on:click={toggleDoNotDisturb($status.dnd?.enabled)}
|
|
||||||
>
|
|
||||||
{#if $status.dnd?.active}⏵ {$_('on')}{:else}⏸ {$_('off')}{/if}</a
|
|
||||||
>
|
|
||||||
<small>
|
|
||||||
{#if $status.dnd?.timeBasedEnabled}
|
|
||||||
{$_('section.status.timeBasedDnd')} ( {$settings.dnd
|
|
||||||
.startHour}:{$settings.dnd.startMinute.toString().padStart(2, '0')} - {$settings
|
|
||||||
.dnd.endHour}:{$settings.dnd.endMinute.toString().padStart(2, '0')} )
|
|
||||||
{/if}
|
|
||||||
</small>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
<hr />
|
|
||||||
{#if !$settings.disableLeds}
|
|
||||||
<Row class="justify-content-evenly">
|
|
||||||
{#if $status.leds}
|
|
||||||
{#each $status.leds as led}
|
|
||||||
<Col>
|
|
||||||
<Input
|
|
||||||
type="color"
|
|
||||||
id="ledColorPicker"
|
|
||||||
bind:value={led.hex}
|
|
||||||
class="mx-auto"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
</ButtonGroup>
|
||||||
</Row>
|
|
||||||
<hr />
|
|
||||||
{/if}
|
|
||||||
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
|
|
||||||
<div class="d-flex justify-content-between">
|
|
||||||
<div>{$_('section.status.memoryFree')}</div>
|
|
||||||
<div>
|
|
||||||
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
{#if $settings.hasLightLevel}
|
|
||||||
{$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux
|
|
||||||
<hr />
|
|
||||||
{/if}
|
{/if}
|
||||||
<Progress striped id="rssiBar" color={wifiStrengthColor} value={rssiPercent}
|
<hr />
|
||||||
>{rssiPercent}%</Progress
|
{#if $status.data}
|
||||||
>
|
<section class={lightMode ? 'lightMode' : 'darkMode'}>
|
||||||
<Tooltip target="rssiBar" placement="bottom">{$_('rssiBar.tooltip')}</Tooltip>
|
<Rendered status={$status} className="btclock-wrapper"></Rendered>
|
||||||
|
</section>
|
||||||
|
{$_('section.status.screenCycle')}:
|
||||||
|
<a
|
||||||
|
id="timerStatusText"
|
||||||
|
href={'#'}
|
||||||
|
style="cursor: pointer"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
|
aria-pressed="false"
|
||||||
|
on:click={toggleTimer($status.timerRunning)}
|
||||||
|
>{#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_(
|
||||||
|
'timer.stopped'
|
||||||
|
)}{/if}</a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
<hr />
|
||||||
|
{#if !$settings.disableLeds}
|
||||||
|
<Row class="justify-content-evenly">
|
||||||
|
{#if $status.leds}
|
||||||
|
{#each $status.leds as led}
|
||||||
|
<Col>
|
||||||
|
<Input
|
||||||
|
type="color"
|
||||||
|
id="ledColorPicker"
|
||||||
|
bind:value={led.hex}
|
||||||
|
class="mx-auto"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</Row>
|
||||||
|
<hr />
|
||||||
|
{/if}
|
||||||
|
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div>{$_('section.status.memoryFree')}</div>
|
||||||
|
<div>
|
||||||
|
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
{#if $settings.hasLightLevel}
|
||||||
|
{$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux
|
||||||
|
<hr />
|
||||||
|
{/if}
|
||||||
|
<Progress striped id="rssiBar" color={wifiStrengthColor} value={rssiPercent}
|
||||||
|
>{rssiPercent}%</Progress
|
||||||
|
>
|
||||||
|
<Tooltip target="rssiBar" placement="bottom">{$_('rssiBar.tooltip')}</Tooltip>
|
||||||
|
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<div>{$_('section.status.wifiSignalStrength')}</div>
|
<div>{$_('section.status.wifiSignalStrength')}</div>
|
||||||
<div>
|
<div>
|
||||||
{$status.rssi} dBm
|
{$status.rssi} dBm
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
</div>
|
||||||
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
|
<hr />
|
||||||
<br />
|
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
|
||||||
<p>
|
<br />
|
||||||
{#if $settings.dataSource == DataSourceType.NOSTR_SOURCE || $settings.nostrZapNotify}
|
<p>
|
||||||
{$_('section.status.nostrConnection')}:
|
{#if $settings.useNostr || $settings.nostrZapNotify}
|
||||||
|
{$_('section.status.nostrConnection')}:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.nostr}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{#if !$settings.useNostr}
|
||||||
|
{#if !$settings.ownDataSource}
|
||||||
|
{$_('section.status.wsPriceConnection')}:
|
||||||
<span>
|
<span>
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.nostr}
|
{#if $status.connectionStatus && $status.connectionStatus.price}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
-
|
||||||
|
{$_('section.status.wsMempoolConnection', {
|
||||||
|
values: { instance: $settings.mempoolInstance }
|
||||||
|
})}:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span><br />
|
||||||
|
{:else}
|
||||||
|
{$_('section.status.wsDataConnection')}:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.V2}
|
||||||
✅
|
✅
|
||||||
{:else}
|
{:else}
|
||||||
❌
|
❌
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $settings.dataSource != DataSourceType.NOSTR_SOURCE}
|
{/if}
|
||||||
{#if $settings.dataSource == DataSourceType.THIRD_PARTY_SOURCE}
|
{#if $settings.fetchEurPrice}
|
||||||
{$_('section.status.wsPriceConnection')}:
|
<small>{$_('section.status.fetchEuroNote')}</small>
|
||||||
<span>
|
{/if}
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.price}
|
</p>
|
||||||
✅
|
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
-
|
|
||||||
{$_('section.status.wsMempoolConnection', {
|
|
||||||
values: { instance: $settings.mempoolInstance }
|
|
||||||
})}:
|
|
||||||
<span>
|
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
|
||||||
✅
|
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
|
||||||
</span><br />
|
|
||||||
{:else}
|
|
||||||
{$_('section.status.wsDataConnection')}:
|
|
||||||
<span>
|
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.V2}
|
|
||||||
✅
|
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{#if $settings.fetchEurPrice}
|
|
||||||
<small>{$_('section.status.fetchEuroNote')}</small>
|
|
||||||
{/if}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.4 KiB |
BIN
static/fonts/Satoshi_Symbol.woff
Normal file
BIN
static/fonts/Satoshi_Symbol.woff
Normal file
Binary file not shown.
|
@ -1,70 +0,0 @@
|
||||||
import { test, expect } from '@playwright/test';
|
|
||||||
|
|
||||||
import { initMock, settingsJson, statusJson } from '../shared';
|
|
||||||
import sharp from 'sharp';
|
|
||||||
|
|
||||||
test.beforeEach(initMock);
|
|
||||||
|
|
||||||
// Define the translations for the headings
|
|
||||||
const headings = {
|
|
||||||
en: {
|
|
||||||
control: 'Control',
|
|
||||||
status: 'Status',
|
|
||||||
settings: 'Settings',
|
|
||||||
language: 'English'
|
|
||||||
},
|
|
||||||
de: {
|
|
||||||
control: 'Steuerung',
|
|
||||||
status: 'Status',
|
|
||||||
settings: 'Einstellungen',
|
|
||||||
language: 'Deutsch'
|
|
||||||
},
|
|
||||||
nl: {
|
|
||||||
control: 'Besturing',
|
|
||||||
status: 'Status',
|
|
||||||
settings: 'Instellingen',
|
|
||||||
language: 'Nederlands'
|
|
||||||
},
|
|
||||||
es: {
|
|
||||||
control: 'Control',
|
|
||||||
status: 'Estado',
|
|
||||||
settings: 'Ajustes',
|
|
||||||
language: 'Español'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test('capture screenshots across devices', async ({ page }, testInfo) => {
|
|
||||||
// Get the locale from the browser or default to 'en'
|
|
||||||
const locale = testInfo.project.use?.locale?.split('-')[0].toLowerCase() || 'en';
|
|
||||||
const translations = headings[locale] || headings.en;
|
|
||||||
|
|
||||||
statusJson.isUpdating = true;
|
|
||||||
// Set the color scheme
|
|
||||||
if (testInfo.project.use?.colorScheme === 'dark') {
|
|
||||||
settingsJson.invertedColor = true;
|
|
||||||
} else {
|
|
||||||
settingsJson.invertedColor = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('heading', { name: translations.control })).toBeVisible();
|
|
||||||
await expect(page.getByRole('heading', { name: translations.status })).toBeVisible();
|
|
||||||
await expect(page.getByRole('heading', { name: translations.settings })).toBeVisible();
|
|
||||||
|
|
||||||
if (await page.locator('#nav-language-dropdown').isVisible()) {
|
|
||||||
await expect(page.getByRole('link', { name: translations.language })).toBeVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
const screenshot = await page.screenshot({
|
|
||||||
fullPage: true
|
|
||||||
});
|
|
||||||
|
|
||||||
await sharp(screenshot)
|
|
||||||
.toFormat('webp', {
|
|
||||||
quality: 95,
|
|
||||||
nearLossless: true
|
|
||||||
})
|
|
||||||
.toFile(
|
|
||||||
`./doc/screenshot-${test.info().project.use.colorScheme?.toLowerCase().replace(' ', '_')}.webp`
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -1,132 +0,0 @@
|
||||||
import { test, expect } from '@playwright/test';
|
|
||||||
|
|
||||||
import { initMock, settingsJson, statusJson } from '../shared';
|
|
||||||
|
|
||||||
test.beforeEach(initMock);
|
|
||||||
|
|
||||||
// Define the translations for the headings
|
|
||||||
const headings = {
|
|
||||||
en: {
|
|
||||||
control: 'Control',
|
|
||||||
status: 'Status',
|
|
||||||
settings: 'Settings',
|
|
||||||
language: 'English'
|
|
||||||
},
|
|
||||||
de: {
|
|
||||||
control: 'Steuerung',
|
|
||||||
status: 'Status',
|
|
||||||
settings: 'Einstellungen',
|
|
||||||
language: 'Deutsch'
|
|
||||||
},
|
|
||||||
nl: {
|
|
||||||
control: 'Besturing',
|
|
||||||
status: 'Status',
|
|
||||||
settings: 'Instellingen',
|
|
||||||
language: 'Nederlands'
|
|
||||||
},
|
|
||||||
es: {
|
|
||||||
control: 'Control',
|
|
||||||
status: 'Estado',
|
|
||||||
settings: 'Ajustes',
|
|
||||||
language: 'Español'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test('capture screenshots across devices', async ({ page }, testInfo) => {
|
|
||||||
// Get the locale from the browser or default to 'en'
|
|
||||||
const locale = testInfo.project.use?.locale?.split('-')[0].toLowerCase() || 'en';
|
|
||||||
const translations = headings[locale] || headings.en;
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('heading', { name: translations.control })).toBeVisible();
|
|
||||||
await expect(page.getByRole('heading', { name: translations.status })).toBeVisible();
|
|
||||||
await expect(page.getByRole('heading', { name: translations.settings })).toBeVisible();
|
|
||||||
|
|
||||||
if (await page.locator('#nav-language-dropdown').isVisible()) {
|
|
||||||
await expect(page.getByRole('link', { name: translations.language })).toBeVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
const screenshot = await page.screenshot({
|
|
||||||
path: `./test-results/screenshots/default-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`
|
|
||||||
});
|
|
||||||
|
|
||||||
await testInfo.attach(`default`, {
|
|
||||||
body: screenshot,
|
|
||||||
contentType: 'image/png'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('capture screenshots across devices with bitaxe screens', async ({ page }, testInfo) => {
|
|
||||||
const locale = testInfo.project.use?.locale?.split('-')[0].toLowerCase() || 'en';
|
|
||||||
const translations = headings[locale] || headings.en;
|
|
||||||
|
|
||||||
settingsJson.screens = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: 'Block Height',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Time',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'Halving countdown',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: 'Block Fee Rate',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
name: 'Sats per dollar',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 20,
|
|
||||||
name: 'Ticker',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 30,
|
|
||||||
name: 'Market Cap',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 80,
|
|
||||||
name: 'BitAxe Hashrate',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 81,
|
|
||||||
name: 'BitAxe Best Difficulty',
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
statusJson.data = ['mdi:bitaxe', '', 'mdi:pickaxe', '6', '3', '7', 'GH/S'];
|
|
||||||
statusJson.rendered = ['mdi:bitaxe', '', 'mdi:pickaxe', '6', '3', '7', 'GH/S'];
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
|
|
||||||
await expect(page.getByRole('heading', { name: translations.control })).toBeVisible();
|
|
||||||
await expect(page.getByRole('heading', { name: translations.status })).toBeVisible();
|
|
||||||
await expect(page.getByRole('heading', { name: translations.settings })).toBeVisible();
|
|
||||||
|
|
||||||
if (await page.locator('#nav-language-dropdown').isVisible()) {
|
|
||||||
await expect(page.getByRole('link', { name: translations.language })).toBeVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.screenshot({
|
|
||||||
path: `./test-results/screenshots/bitaxe-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`
|
|
||||||
});
|
|
||||||
|
|
||||||
await testInfo.attach(`bitaxe`, {
|
|
||||||
path: `./test-results/screenshots/bitaxe-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`,
|
|
||||||
contentType: 'image/png'
|
|
||||||
});
|
|
||||||
});
|
|
218
tests/shared.ts
218
tests/shared.ts
|
@ -1,218 +0,0 @@
|
||||||
export const statusJson = {
|
|
||||||
currentScreen: 20,
|
|
||||||
numScreens: 7,
|
|
||||||
timerRunning: true,
|
|
||||||
isOTAUpdating: false,
|
|
||||||
espUptime: 4479,
|
|
||||||
espFreeHeap: 58508,
|
|
||||||
espHeapSize: 342108,
|
|
||||||
connectionStatus: {
|
|
||||||
price: false,
|
|
||||||
blocks: false,
|
|
||||||
V2: true,
|
|
||||||
nostr: true
|
|
||||||
},
|
|
||||||
rssi: -66,
|
|
||||||
data: ['BLOCK/HEIGHT', '8', '7', '6', '5', '4', '3'],
|
|
||||||
currency: 'USD',
|
|
||||||
leds: [
|
|
||||||
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
|
||||||
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
|
||||||
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
|
||||||
{ red: 0, green: 0, blue: 0, hex: '#000000' }
|
|
||||||
],
|
|
||||||
isUpdating: true,
|
|
||||||
isFake: true,
|
|
||||||
dnd: {
|
|
||||||
enabled: true,
|
|
||||||
timeBasedEnabled: true,
|
|
||||||
startTime: '23:00',
|
|
||||||
endTime: '7:00',
|
|
||||||
active: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const settingsJson = {
|
|
||||||
numScreens: 7,
|
|
||||||
timerSeconds: 1800,
|
|
||||||
timerRunning: true,
|
|
||||||
minSecPriceUpd: 30,
|
|
||||||
fullRefreshMin: 60,
|
|
||||||
wpTimeout: 600,
|
|
||||||
tzOffset: 0,
|
|
||||||
dataSource: 0,
|
|
||||||
mempoolInstance: 'mempool.space',
|
|
||||||
ledTestOnPower: true,
|
|
||||||
ledFlashOnUpd: true,
|
|
||||||
ledBrightness: 128,
|
|
||||||
stealFocus: true,
|
|
||||||
mcapBigChar: true,
|
|
||||||
mdnsEnabled: true,
|
|
||||||
otaEnabled: true,
|
|
||||||
fetchEurPrice: false,
|
|
||||||
hostnamePrefix: 'btclock',
|
|
||||||
hostname: 'btclock-d60b14',
|
|
||||||
ip: '192.168.20.231',
|
|
||||||
txPower: 78,
|
|
||||||
gitRev: '25d8b92bcbc8938417c140355ea3ba99ff9eb4b7',
|
|
||||||
gitTag: '3.2.23',
|
|
||||||
bitaxeEnabled: false,
|
|
||||||
bitaxeHostname: 'bitaxe1',
|
|
||||||
miningPoolStats: false,
|
|
||||||
miningPoolName: 'ocean',
|
|
||||||
miningPoolUser: '38Qkkei3SuF1Eo45BaYmRHUneRD54yyTFy',
|
|
||||||
nostrZapNotify: true,
|
|
||||||
hwRev: 'REV_A_EPD_2_13',
|
|
||||||
fsRev: '64e518bf58f89749753167a8b6826e10bb6455c5',
|
|
||||||
nostrZapPubkey: 'b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422',
|
|
||||||
lastBuildTime: Math.round(new Date().getTime() / 1000),
|
|
||||||
screens: [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: 'Block Height',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Time',
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'Halving countdown',
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: 'Block Fee Rate',
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
name: 'Sats per dollar',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 20,
|
|
||||||
name: 'Ticker',
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 30,
|
|
||||||
name: 'Market Cap',
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
actCurrencies: ['USD', 'EUR'],
|
|
||||||
availableCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD'],
|
|
||||||
availablePools: [
|
|
||||||
'ocean',
|
|
||||||
'noderunners',
|
|
||||||
'satoshi_radio',
|
|
||||||
'braiins',
|
|
||||||
'public_pool',
|
|
||||||
'gobrrr_pool',
|
|
||||||
'ckpool',
|
|
||||||
'eu_ckpool'
|
|
||||||
],
|
|
||||||
dnd: {
|
|
||||||
enabled: false,
|
|
||||||
timeBasedEnabled: true,
|
|
||||||
startHour: 23,
|
|
||||||
startMinute: 0,
|
|
||||||
endHour: 7,
|
|
||||||
endMinute: 0
|
|
||||||
},
|
|
||||||
availableFonts: ['antonio', 'oswald'],
|
|
||||||
invertedColor: false,
|
|
||||||
isLoaded: true,
|
|
||||||
isFake: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export const latestReleaseFake = {
|
|
||||||
id: 782,
|
|
||||||
tag_name: '3.2.24',
|
|
||||||
target_commitish: '',
|
|
||||||
name: '3.2.24',
|
|
||||||
body: '',
|
|
||||||
url: 'https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/782',
|
|
||||||
html_url: 'https://git.btclock.dev/btclock/btclock_v3/releases/tag/3.2.24',
|
|
||||||
tarball_url: 'https://git.btclock.dev/btclock/btclock_v3/archive/3.2.24.tar.gz',
|
|
||||||
zipball_url: 'https://git.btclock.dev/btclock/btclock_v3/archive/3.2.24.zip',
|
|
||||||
hide_archive_links: false,
|
|
||||||
upload_url: 'https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/782/assets',
|
|
||||||
draft: false,
|
|
||||||
prerelease: false,
|
|
||||||
created_at: '2024-12-28T17:48:05Z',
|
|
||||||
published_at: '2024-12-28T17:48:05Z',
|
|
||||||
author: {},
|
|
||||||
assets: [],
|
|
||||||
archive_download_count: {
|
|
||||||
zip: 0,
|
|
||||||
tar_gz: 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initMock = async ({ page }) => {
|
|
||||||
await page.route('*/**/api/status', async (route) => {
|
|
||||||
await route.fulfill({ json: statusJson });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.route('*/**/api/show/screen/10', async (route) => {
|
|
||||||
//if (route.request().url().includes('*/**/api/show/screen/1')) {
|
|
||||||
statusJson.currentScreen = 1;
|
|
||||||
statusJson.data = ['MSCW/TIME', ' ', ' ', '2', '6', '4', '4'];
|
|
||||||
|
|
||||||
await route.fulfill({ json: statusJson });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.route('*/**/api/show/screen/20', async (route) => {
|
|
||||||
statusJson.currentScreen = 2;
|
|
||||||
statusJson.data = ['BTC/USD', '$', '3', '7', '8', '2', '4'];
|
|
||||||
|
|
||||||
await route.fulfill({ json: statusJson });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.route('*/**/api/show/screen/4', async (route) => {
|
|
||||||
statusJson.currentScreen = 4;
|
|
||||||
statusJson.data = ['BIT/COIN', 'HALV/ING', '0/YRS', '149/DAYS', '8/HRS', '30/MINS', 'TO/GO'];
|
|
||||||
|
|
||||||
await route.fulfill({ json: statusJson });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.route('*/**/api/settings', async (route) => {
|
|
||||||
await route.fulfill({ json: settingsJson });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.route('**/events', async (route) => {
|
|
||||||
const newStatus = statusJson;
|
|
||||||
newStatus.data = ['BLOCK/HEIGHT', '8', '0', '0', '8', '1', '5'];
|
|
||||||
newStatus.isUpdating = true;
|
|
||||||
|
|
||||||
// Format the SSE message correctly
|
|
||||||
const sseMessage = `data: ${JSON.stringify(newStatus)}\n\n`;
|
|
||||||
|
|
||||||
// Create a readable stream for SSE
|
|
||||||
const stream = new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(new TextEncoder().encode(sseMessage));
|
|
||||||
// Keep the connection open
|
|
||||||
// controller.close(); // Don't close if you want to send more events
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await route.fulfill({
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'text/event-stream',
|
|
||||||
'Cache-Control': 'no-cache',
|
|
||||||
Connection: 'keep-alive'
|
|
||||||
},
|
|
||||||
body: stream
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.route('**/api/v1/repos/btclock/btclock_v3/releases/latest', async (route) => {
|
|
||||||
await route.fulfill({ json: latestReleaseFake });
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,7 +1,114 @@
|
||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
import { initMock, settingsJson, statusJson } from '../shared';
|
|
||||||
|
|
||||||
test.beforeEach(initMock);
|
const statusJson = {
|
||||||
|
currentScreen: 0,
|
||||||
|
numScreens: 7,
|
||||||
|
timerRunning: true,
|
||||||
|
espUptime: 4479,
|
||||||
|
espFreeHeap: 58508,
|
||||||
|
espHeapSize: 342108,
|
||||||
|
connectionStatus: { price: true, blocks: true },
|
||||||
|
rssi: -66,
|
||||||
|
data: ['BLOCK/HEIGHT', '8', '1', '8', '0', '2', '6'],
|
||||||
|
rendered: ['BLOCK/HEIGHT', '8', '1', '8', '0', '2', '6'],
|
||||||
|
leds: [
|
||||||
|
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
||||||
|
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
||||||
|
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
||||||
|
{ red: 0, green: 0, blue: 0, hex: '#000000' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const settingsJson = {
|
||||||
|
numScreens: 7,
|
||||||
|
fgColor: 415029,
|
||||||
|
bgColor: 0,
|
||||||
|
timerSeconds: 1800,
|
||||||
|
timerRunning: true,
|
||||||
|
minSecPriceUpd: 30,
|
||||||
|
fullRefreshMin: 60,
|
||||||
|
wpTimeout: 600,
|
||||||
|
tzOffset: 0,
|
||||||
|
useBitcoinNode: false,
|
||||||
|
mempoolInstance: 'mempool.space',
|
||||||
|
ledTestOnPower: true,
|
||||||
|
ledFlashOnUpd: true,
|
||||||
|
ledBrightness: 128,
|
||||||
|
stealFocus: true,
|
||||||
|
mcapBigChar: true,
|
||||||
|
mdnsEnabled: true,
|
||||||
|
otaEnabled: true,
|
||||||
|
fetchEurPrice: false,
|
||||||
|
hostnamePrefix: 'btclock',
|
||||||
|
hostname: 'btclock-d60b14',
|
||||||
|
ip: '192.168.20.231',
|
||||||
|
txPower: 78,
|
||||||
|
gitRev: '25d8b92bcbc8938417c140355ea3ba99ff9eb4b7',
|
||||||
|
gitTag: '3.1.9',
|
||||||
|
bitaxeEnabled: false,
|
||||||
|
bitaxeHostname: 'bitaxe1',
|
||||||
|
nostrZapNotify: true,
|
||||||
|
hwRev: 'REV_A_EPD_2_13',
|
||||||
|
fsRev: '4c5d9616212b27e3f05c35370f0befcf2c5a04b2',
|
||||||
|
nostrZapPubkey: 'b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422',
|
||||||
|
lastBuildTime: '1700666677',
|
||||||
|
screens: [
|
||||||
|
{ id: 0, name: 'Block Height', enabled: true },
|
||||||
|
{ id: 1, name: 'Sats per dollar', enabled: true },
|
||||||
|
{ id: 2, name: 'Ticker', enabled: true },
|
||||||
|
{ id: 3, name: 'Time', enabled: true },
|
||||||
|
{ id: 4, name: 'Halving countdown', enabled: true },
|
||||||
|
{ id: 5, name: 'Market Cap', enabled: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.route('*/**/api/status', async (route) => {
|
||||||
|
await route.fulfill({ json: statusJson });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('*/**/api/show/screen/1', async (route) => {
|
||||||
|
//if (route.request().url().includes('*/**/api/show/screen/1')) {
|
||||||
|
statusJson.currentScreen = 1;
|
||||||
|
statusJson.data = ['MSCW/TIME', ' ', ' ', '2', '6', '4', '4'];
|
||||||
|
statusJson.rendered = statusJson.data;
|
||||||
|
//}
|
||||||
|
|
||||||
|
await route.fulfill({ json: statusJson });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('*/**/api/show/screen/2', async (route) => {
|
||||||
|
statusJson.currentScreen = 2;
|
||||||
|
statusJson.data = ['BTC/USD', '$', '3', '7', '8', '2', '4'];
|
||||||
|
statusJson.rendered = statusJson.data;
|
||||||
|
|
||||||
|
await route.fulfill({ json: statusJson });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('*/**/api/show/screen/4', async (route) => {
|
||||||
|
statusJson.currentScreen = 4;
|
||||||
|
statusJson.data = ['BIT/COIN', 'HALV/ING', '0/YRS', '149/DAYS', '8/HRS', '30/MINS', 'TO/GO'];
|
||||||
|
statusJson.rendered = statusJson.data;
|
||||||
|
|
||||||
|
await route.fulfill({ json: statusJson });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('*/**/api/settings', async (route) => {
|
||||||
|
await route.fulfill({ json: settingsJson });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route('**/events', (route) => {
|
||||||
|
const newStatus = statusJson;
|
||||||
|
newStatus.data = ['BLOCK/HEIGHT', '8', '0', '0', '8', '1', '5'];
|
||||||
|
|
||||||
|
// Respond with a custom SSE message
|
||||||
|
route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'text/event-stream',
|
||||||
|
json: `${JSON.stringify(newStatus)}\n\n`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('index page has expected columns control, status, settings', async ({ page }) => {
|
test('index page has expected columns control, status, settings', async ({ page }) => {
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
|
@ -74,8 +181,6 @@ test('time values can not be zero or negative', async ({ page }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('info message when fetch eur price is enabled', async ({ page }) => {
|
test('info message when fetch eur price is enabled', async ({ page }) => {
|
||||||
delete (settingsJson as { actCurrencies?: string[] }).actCurrencies;
|
|
||||||
|
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
await page.getByRole('button', { name: 'Show all' }).click();
|
await page.getByRole('button', { name: 'Show all' }).click();
|
||||||
|
|
||||||
|
@ -132,7 +237,7 @@ test('screens should be able to change', async ({ page }) => {
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Sats per Dollar' }).click();
|
await page.getByRole('button', { name: 'Sats per Dollar' }).click();
|
||||||
const response = await responsePromise;
|
const response = await responsePromise;
|
||||||
expect(response.url()).toContain('api/show/screen/10');
|
expect(response.url()).toContain('api/show/screen/1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parse all types of EPD content correctly', async ({ page }) => {
|
test('parse all types of EPD content correctly', async ({ page }) => {
|
|
@ -1,18 +0,0 @@
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
|
||||||
import { defineConfig } from 'vite';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [sveltekit()],
|
|
||||||
build: {
|
|
||||||
sourcemap: true,
|
|
||||||
minify: false,
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
manualChunks: undefined // Disable code splitting
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
test: {
|
|
||||||
include: ['tests/**/*.{test,spec}.{js,ts}']
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vitest/config';
|
||||||
// import { visualizer } from 'rollup-plugin-visualizer';
|
import GithubActionsReporter from 'vitest-github-actions-reporter';
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
@ -65,43 +65,21 @@ export default defineConfig({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// visualizer({
|
|
||||||
// emitFile: true,
|
|
||||||
// filename: "stats.html",
|
|
||||||
// })
|
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
minify: 'esbuild',
|
minify: true,
|
||||||
cssCodeSplit: false,
|
cssCodeSplit: false,
|
||||||
chunkSizeWarningLimit: 550,
|
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// assetFileNames: '[hash][extname]',
|
manualChunks: () => 'app',
|
||||||
entryFileNames: `[hash][extname]`,
|
assetFileNames: '[name][extname]'
|
||||||
chunkFileNames: `[hash][extname]`,
|
|
||||||
assetFileNames: `[hash][extname]`,
|
|
||||||
preserveModules: false,
|
|
||||||
|
|
||||||
manualChunks: () => {
|
|
||||||
return 'app';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
preprocessorOptions: {
|
|
||||||
scss: {
|
|
||||||
quietDeps: true,
|
|
||||||
silenceDeprecations: ['import']
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||||
globals: true,
|
globals: true,
|
||||||
environment: 'jsdom'
|
environment: 'jsdom',
|
||||||
},
|
reporters: process.env.GITHUB_ACTIONS ? ['default', new GithubActionsReporter()] : 'default'
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue