Add Web Updater functionality, improved layout for small displays

This commit is contained in:
Djuri Baars 2024-06-09 00:11:27 +02:00
parent 4f15eee72b
commit e53b487236
11 changed files with 401 additions and 92 deletions

View File

@ -41,7 +41,7 @@ jobs:
node-version: lts/*
cache: yarn
cache-dependency-path: '**/yarn.lock'
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: |
~/.cache/pip
@ -80,6 +80,8 @@ jobs:
run: mkdir -p output && echo "$BLOCK_HEIGHT" > output/version.txt
- name: gzip build for LittleFS
run: find dist -type f ! -name ".*" -exec sh -c 'mkdir -p "build_gz/$(dirname "${1#dist/}")" && gzip -k "$1" -c > "build_gz/${1#dist/}".gz' _ {} \;
- name: Write git rev to file
run: echo "$GITHUB_SHA" > build_gz/fs_hash.txt
- name: Check GZipped directory size
run: |
# Set the threshold size in bytes

View File

@ -60,6 +60,12 @@
"uptime": "Betriebszeit",
"wifiSignalStrength": "WiFi-Signalstärke",
"wsDataConnection": "BTClock-Datenquelle verbindung"
},
"firmwareUpdater": {
"fileUploadSuccess": "Datei erfolgreich hochgeladen, Gerät neu gestartet. WebUI in {countdown} Sekunden neu geladen",
"fileUploadFailed": "Das Hochladen der Datei ist fehlgeschlagen. \nStellen Sie sicher, dass Sie die richtige Datei ausgewählt haben, und versuchen Sie es erneut.",
"uploading": "Hochladen",
"firmwareUpdateText": "Wenn Sie die Firmware-Upload-Funktion verwenden, stellen Sie sicher, dass Sie die richtigen Dateien verwenden. \nDas Hochladen der falschen Dateien kann dazu führen, dass das Gerät nicht mehr funktioniert. \nWenn es schief geht, können Sie die Firmware wiederherstellen, indem Sie das vollständige Image hochladen, nachdem Sie das Gerät in den BOOT-Modus versetzt haben."
}
},
"colors": {
@ -88,5 +94,6 @@
},
"rssiBar": {
"tooltip": "Werte > -67 dBm gelten als gut. > -30 dBm ist erstaunlich"
}
},
"warning": "Achtung"
}

View File

@ -48,7 +48,9 @@
"hostname": "Hostname",
"frontlight": "Frontlight",
"turnOn": "Turn on",
"flashFrontlight": "Flash"
"flashFrontlight": "Flash",
"firmwareUpdate": "Firmware update",
"fwCommit": "Firmware commit"
},
"status": {
"title": "Status",
@ -60,6 +62,12 @@
"uptime": "Uptime",
"wifiSignalStrength": "WiFi Signal strength",
"wsDataConnection": "BTClock data-source connection"
},
"firmwareUpdater": {
"fileUploadFailed": "File upload failed. Make sure you have selected the correct file and try again.",
"fileUploadSuccess": "File uploaded successfully, restarting device and reloading WebUI in {countdown} seconds",
"uploading": "Uploading",
"firmwareUpdateText": "When you use the firmware upload functionality, make sure you use the correct files. Uploading the wrong files can result in a non-working device. If it goes wrong, you can restore firmware by uploading the full image after setting the device in BOOT-mode."
}
},
"colors": {
@ -88,5 +96,6 @@
},
"rssiBar": {
"tooltip": "Values > -67 dBm are considered good. > -30 dBm is amazing"
}
},
"warning": "Warning"
}

View File

@ -59,6 +59,12 @@
"title": "Estado",
"wifiSignalStrength": "Fuerza de la señal WiFi",
"wsDataConnection": "Conexión de fuente de datos BTClock"
},
"firmwareUpdater": {
"fileUploadSuccess": "Archivo cargado exitosamente, reiniciando el dispositivo. Recargando WebUI en {countdown} segundos",
"fileUploadFailed": "Error al cargar el archivo. \nAsegúrese de haber seleccionado el archivo correcto e inténtelo nuevamente.",
"uploading": "Subiendo",
"firmwareUpdateText": "Cuando utilice la función de carga de firmware, asegúrese de utilizar los archivos correctos. \nCargar archivos incorrectos puede provocar que el dispositivo no funcione. \nSi sale mal, puede restaurar el firmware cargando la imagen completa después de configurar el dispositivo en modo BOOT."
}
},
"button": {
@ -87,5 +93,6 @@
},
"rssiBar": {
"tooltip": "Se consideran buenos valores > -67 dBm. > -30 dBm es increíble"
}
},
"warning": "Aviso"
}

View File

@ -59,6 +59,12 @@
"uptime": "Uptime",
"wifiSignalStrength": "WiFi signaalsterkte",
"wsDataConnection": "BTClock-gegevensbron verbinding"
},
"firmwareUpdater": {
"fileUploadSuccess": "Bestand geüpload, apparaat herstart. WebUI opnieuw geladen over {countdown} seconden",
"fileUploadFailed": "Bestandsupload mislukt. \nZorg ervoor dat het juiste bestand is geselecteerd en probeer het opnieuw.",
"uploading": "Uploaden",
"firmwareUpdateText": "Zorg bij het gebruiken van de firmware upload dat de juiste bestanden gebruikt worden. \nHet uploaden van de verkeerde bestanden kan resulteren in een niet-werkend apparaat. \nAls het misgaat, kunt u de firmware herstellen door de volledige afbeelding te uploaden nadat u het apparaat in de BOOT-modus hebt gezet."
}
},
"colors": {
@ -87,5 +93,6 @@
},
"rssiBar": {
"tooltip": "Waarden > -67 dBm zijn goed. > -30 dBm is verbazingwekkend"
}
},
"warning": "Waarschuwing"
}

9
src/lib/screen.ts Normal file
View File

@ -0,0 +1,9 @@
import { writable } from 'svelte/store';
// Create a writable store to track screen size
export const screenSize = writable<number>(window.innerWidth);
// Function to update the screen size
export const updateScreenSize = () => {
screenSize.set(window.innerWidth);
};

View File

@ -36,33 +36,34 @@ $input-font-size-sm: $font-size-base * 0.875;
@import '../node_modules/bootstrap/scss/progress';
@import '../node_modules/bootstrap/scss/tooltip';
@import '../node_modules/bootstrap/scss/toasts';
@import '../node_modules/bootstrap/scss/alert';
@import '../node_modules/bootstrap/scss/helpers';
@import '../node_modules/bootstrap/scss/utilities/api';
@include media-breakpoint-down(xl) {
html {
font-size: 85%;
}
// @include media-breakpoint-down(xl) {
// html {
// // font-size: 85%;
// }
button.btn,
input[type='button'].btn,
input[type='submit'].btn,
input[type='reset'].btn {
@include button-size(
$btn-padding-y-sm,
$btn-padding-x-sm,
$font-size-sm,
$btn-border-radius-sm
);
}
}
// button.btn,
// input[type='button'].btn,
// input[type='submit'].btn,
// input[type='reset'].btn {
// @include button-size(
// $btn-padding-y-sm,
// $btn-padding-x-sm,
// $font-size-sm,
// $btn-border-radius-sm
// );
// }
// }
@include media-breakpoint-down(lg) {
html {
font-size: 75%;
}
}
// @include media-breakpoint-down(lg) {
// html {
// font-size: 75%;
// }
// }
nav {
margin-bottom: 15px;
@ -208,3 +209,8 @@ nav {
font-style: italic;
font-weight: 600;
}
.firmwareUploadStatusAlert,
#firmwareUploadProgress {
@extend .my-2;
}

View File

@ -1,5 +1,6 @@
<script lang="ts">
import { PUBLIC_BASE_URL } from '$lib/config';
import { screenSize, updateScreenSize } from '$lib/screen';
import { Container, Row, Toast, ToastBody } from 'sveltestrap';
@ -13,6 +14,12 @@
fgColor: '0'
});
let uiSettings = writable({
inputSize: 'sm',
selectClass: '',
btnSize: 'lg'
});
let status = writable({
data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'],
espFreeHeap: 0,
@ -61,8 +68,43 @@
let dataObj = JSON.parse(e.data);
status.set(dataObj);
});
function handleResize() {
updateScreenSize();
}
// Add an event listener to update the screen size when the window is resized
window.addEventListener('resize', handleResize);
// Call the function initially to set the initial screen size
updateScreenSize();
// Cleanup function to remove the event listener when the component is destroyed
return () => {
window.removeEventListener('resize', handleResize);
};
});
$: {
const lgBreakpoint = parseInt(
getComputedStyle(document.documentElement).getPropertyValue('--bs-breakpoint-lg')
);
if ($screenSize >= lgBreakpoint) {
uiSettings.set({
inputSize: 'sm',
selectClass: 'form-select-sm',
btnSize: 'sm'
});
} else {
uiSettings.set({
inputSize: 'lg',
selectClass: 'form-select-lg',
btnSize: 'xl'
});
}
}
let toastIsOpen = false;
let toastColor = 'success';
let toastBody = '';
@ -79,10 +121,15 @@
</svelte:head>
<Container fluid>
<Row>
<Control bind:settings bind:status></Control>
<Row cols={{ lg: 3, sm: 1 }}>
<Control bind:settings bind:uiSettings bind:status></Control>
<Status bind:settings bind:status></Status>
<Settings bind:settings on:showToast={showToast} on:formReset={fetchSettingsData}></Settings>
<Settings
bind:settings
bind:uiSettings
on:showToast={showToast}
on:formReset={fetchSettingsData}
></Settings>
</Row>
</Container>
<div class="position-fixed bottom-0 end-0 p-2">

View File

@ -15,8 +15,11 @@
Label,
Row
} from 'sveltestrap';
import FirmwareUpdater from './FirmwareUpdater.svelte';
export let settings = {};
export let uiSettings;
export let customText: string;
export let status: Writable<{ leds: [] }>;
let ledStatus = [];
@ -102,25 +105,35 @@
<CardBody>
<Form>
<Row>
<Label md={4} for="customText">{$_('section.control.text')}</Label>
<Label md={4} for="customText" size={$uiSettings.inputSize}
>{$_('section.control.text')}</Label
>
<Col md="8">
<Input
type="text"
id="customText"
bind:value={customText}
bsSize="sm"
bsSize="$uiSettings.inputSize"
maxLength={$settings.numScreens}
/>
</Col>
</Row>
<Button color="primary" on:click={setCustomText}>{$_('section.control.showText')}</Button>
<Row>
<Col class="d-flex justify-content-end">
<Button color="primary" on:click={setCustomText} bsSize={$uiSettings.btnSize}
>{$_('section.control.showText')}</Button
>
</Col>
</Row>
</Form>
<hr />
{#if !$settings.disableLeds}
<h3>LEDs</h3>
<Form>
<Row>
<Label md={4} for="ledColorPicker" size="sm">{$_('section.control.ledColor')}</Label>
<Label md={4} for="ledColorPicker" size={$uiSettings.inputSize}
>{$_('section.control.ledColor')}</Label
>
<Col md="8">
<Row class="justify-content-between">
{#if ledStatus}
@ -137,51 +150,84 @@
{/each}
{/if}
</Row>
<Row class="justify-content-between">
<Col>
<Row>
<Col class="d-flex justify-content-end">
<Input
bind:checked={keepLedsSameColor}
type="switch"
class="mx-auto"
label={$_('sections.control.keepSameColor')}
bsSize={$uiSettings.inputSize}
/>
</Col>
</Row>
</Col>
</Row>
<Button color="secondary" id="turnOffLedsBtn" on:click={turnOffLeds}
>{$_('section.control.turnOff')}</Button
>
<Button color="primary" on:click={setLEDcolor}>{$_('section.control.setColor')}</Button>
<Row>
<Col class="d-flex justify-content-end">
<Button
color="secondary"
id="turnOffLedsBtn"
on:click={turnOffLeds}
bsSize={$uiSettings.inputSize}>{$_('section.control.turnOff')}</Button
>
<div class="mx-2"></div>
<Button color="primary" on:click={setLEDcolor} bsSize={$uiSettings.inputSize}
>{$_('section.control.setColor')}</Button
>
</Col>
</Row>
</Form>
<hr />
{/if}
{#if $settings.hasFrontlight}
<h3>{$_('section.control.frontlight')}</h3>
<Button color="secondary" id="turnOffFrontlightBtn" on:click={turnOffFrontlight}
>{$_('section.control.turnOff')}</Button
>
<Button color="primary" on:click={turnOnFrontlight}>{$_('section.control.turnOn')}</Button>
<Button color="success" id="flashFrontlight" on:click={flashFrontlight}
>{$_('section.control.flashFrontlight')}</Button
>
<Row class="d-flex justify-content-between justify-content-md-end">
<Col md="auto" class="">
<Button color="secondary" id="turnOffFrontlightBtn" on:click={turnOffFrontlight}
>{$_('section.control.turnOff')}</Button
>
</Col><Col md="auto" class="">
<Button color="primary" on:click={turnOnFrontlight}
>{$_('section.control.turnOn')}</Button
>
</Col><Col md="auto" class="">
<Button color="success" id="flashFrontlight" on:click={flashFrontlight}
>{$_('section.control.flashFrontlight')}</Button
>
</Col>
</Row>
<hr />
{/if}
<h3>{$_('section.control.systemInfo')}</h3>
<ul class="small system_info">
<li>{$_('section.control.version')}: {$settings.gitRev}</li>
<li>
{$_('section.control.buildTime')}: {new Date(
$settings.lastBuildTime * 1000
).toLocaleString()}
</li>
<li>IP: {$settings.ip}</li>
<li>HW revision: {$settings.hwRev}</li>
<li>{$_('section.control.fwCommit')}: {$settings.gitRev}</li>
<li>WebUI commit: {$settings.fsRev}</li>
<li>{$_('section.control.hostname')}: {$settings.hostname}</li>
</ul>
<Button color="danger" id="restartBtn" on:click={restartClock}>{$_('button.restart')}</Button>
<Button color="warning" id="forceFullRefresh" on:click={forceFullRefresh}
>{$_('button.forceFullRefresh')}</Button
>
<Row>
<Col class="d-flex justify-content-end">
<Button color="danger" id="restartBtn" on:click={restartClock}
>{$_('button.restart')}</Button
>
<div class="mx-2"></div>
<Button color="warning" id="forceFullRefresh" on:click={forceFullRefresh}
>{$_('button.forceFullRefresh')}</Button
>
</Col>
</Row>
{#if $settings.otaEnabled}
<hr />
<h3>{$_('section.control.firmwareUpdate')}</h3>
<FirmwareUpdater bind:settings />
{/if}
</CardBody>
</Card>
</Col>

View File

@ -0,0 +1,155 @@
<script lang="ts">
import { PUBLIC_BASE_URL } from '$lib/config';
import { _ } from 'svelte-i18n';
import { writable } from 'svelte/store';
import { Progress, Alert, Button } from 'sveltestrap';
export let settings = { hwRev: '' };
const countdown = writable(10);
let firmwareUploadFile: File | null = null;
let firmwareWebUiFile: File | null = null;
let firmwareUploadProgress = 0;
let firmwareUploadSuccess = false;
let firmwareUploadError = false;
const handleFileChange = (event: Event, setFile: (file: File) => void) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
setFile(target.files[0]);
}
};
function startCountdownToReload(duration: number) {
let timeRemaining = duration;
const interval = setInterval(() => {
timeRemaining -= 1;
countdown.set(timeRemaining);
if (timeRemaining <= 0) {
clearInterval(interval);
location.reload();
}
}, 1000); // Update every second
}
const uploadFile = async (file: File | null, endpoint: string) => {
if (!file) return;
const formData = new FormData();
formData.append('file', file);
firmwareUploadSuccess = false;
firmwareUploadError = false;
try {
const xhr = new XMLHttpRequest();
xhr.open('POST', endpoint);
xhr.upload.onprogress = (event: ProgressEvent) => {
if (event.lengthComputable) {
firmwareUploadProgress = Math.round((event.loaded * 100) / event.total);
}
};
xhr.onload = () => {
if (xhr.status === 200 && xhr.responseText != 'FAIL') {
firmwareUploadSuccess = true;
startCountdownToReload(10);
} else {
firmwareUploadError = true;
}
};
xhr.onerror = () => {
firmwareUploadError = true;
};
xhr.send(formData);
} catch (error) {
firmwareUploadError = true;
console.error(error);
}
};
const uploadFirmwareFile = () => {
uploadFile(firmwareUploadFile, `${PUBLIC_BASE_URL}/upload/firmware`);
};
const uploadWebUiFile = () => {
uploadFile(firmwareWebUiFile, `${PUBLIC_BASE_URL}/upload/webui`);
};
const getFirmwareBinaryName = () => {
let binaryFilename = '';
switch ($settings.hwRev) {
case 'REV_B_EPD_2_13':
binaryFilename = 'btclock_rev_b_213epd_firmware.bin';
break;
case 'REV_A_EPD_2_13':
binaryFilename = 'lolin_s3_mini_213epd_firmware.bin';
break;
case 'REV_A_EPD_2_9':
binaryFilename = 'lolin_s3_mini_29epd_firmware.bin';
break;
default:
binaryFilename = 'Unsupported hardware, unable to determine firmware binary filename';
}
return binaryFilename;
};
</script>
<section class="row row-cols-lg-auto align-items-end">
<div class="col-12">
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
<input
type="file"
id="firmwareFile"
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
name="update"
class="form-control"
accept=".bin"
/>
</div>
<div class="flex-fill">
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
>Update firmware</Button
>
</div>
<div class="col mt-2">
<label for="webuiFile" class="form-label">WebUI file (littlefs.bin)</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}
{#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
>

View File

@ -20,6 +20,7 @@
} from 'sveltestrap';
export let settings;
export let uiSettings;
const wifiTxPowerMap = new Map<string, number>([
['Default', 80],
@ -89,7 +90,7 @@
<CardBody>
<Form on:submit={onSave}>
<Row>
<Label md={6} for="fgColor" size="sm"
<Label md={6} for="fgColor" size={$uiSettings.inputSize}
>{$_('section.settings.textColor', { default: 'Text color' })}</Label
>
<Col md="6">
@ -98,8 +99,8 @@
bind:value={$settings.fgColor}
name="select"
id="fgColor"
bsSize="sm"
class="form-select-sm"
bsSize={$uiSettings.inputSize}
class={$uiSettings.selectClass}
>
<option value="0">{$_('colors.black')}</option>
<option value="65535">{$_('colors.white')}</option>
@ -107,15 +108,17 @@
</Col>
</Row>
<Row>
<Label md={6} for="bgColor" size="sm">{$_('section.settings.backgroundColor')}</Label>
<Label md={6} for="bgColor" size={$uiSettings.inputSize}
>{$_('section.settings.backgroundColor')}</Label
>
<Col md="6">
<Input
type="select"
bind:value={$settings.bgColor}
name="select"
id="bgColor"
bsSize="sm"
class="form-select-sm"
bsSize={$uiSettings.inputSize}
class={$uiSettings.selectClass}
>
<option value="0">{$_('colors.black')}</option>
<option value="65535">{$_('colors.white')}</option>
@ -123,9 +126,11 @@
</Col>
</Row>
<Row>
<Label md={6} for="timePerScreen" size="sm">{$_('section.settings.timePerScreen')}</Label>
<Label md={6} for="timePerScreen" size={$uiSettings.inputSize}
>{$_('section.settings.timePerScreen')}</Label
>
<Col md="6">
<InputGroup size="sm">
<InputGroup size={$uiSettings.inputSize}>
<Input
type="number"
id="timePerScreen"
@ -138,11 +143,11 @@
</Col>
</Row>
<Row>
<Label md={6} for="fullRefreshMin" size="sm"
<Label md={6} for="fullRefreshMin" size={$uiSettings.inputSize}
>{$_('section.settings.fullRefreshEvery')}</Label
>
<Col md="6">
<InputGroup size="sm">
<InputGroup size={$uiSettings.inputSize}>
<Input
type="number"
id="fullRefreshMin"
@ -155,11 +160,11 @@
</Col>
</Row>
<Row>
<Label md={6} for="minSecPriceUpd" size="sm"
<Label md={6} for="minSecPriceUpd" size={$uiSettings.inputSize}
>{$_('section.settings.timeBetweenPriceUpdates')}</Label
>
<Col md="6">
<InputGroup size="sm">
<InputGroup size={$uiSettings.inputSize}>
<Input
type="number"
id="minSecPriceUpd"
@ -173,9 +178,11 @@
</Col>
</Row>
<Row>
<Label md={6} for="tzOffset" size="sm">{$_('section.settings.timezoneOffset')}</Label>
<Label md={6} for="tzOffset" size={$uiSettings.inputSize}
>{$_('section.settings.timezoneOffset')}</Label
>
<Col md="6">
<InputGroup size="sm">
<InputGroup size={$uiSettings.inputSize}>
<Input
type="number"
step="1"
@ -189,7 +196,9 @@
</Col>
</Row>
<Row>
<Label md={6} for="ledBrightness" size="sm">{$_('section.settings.ledBrightness')}</Label>
<Label md={6} for="ledBrightness" size={$uiSettings.inputSize}
>{$_('section.settings.ledBrightness')}</Label
>
<Col md="6">
<Input
type="range"
@ -204,7 +213,7 @@
</Row>
{#if $settings.hasFrontlight}
<Row>
<Label md={6} for="flMaxBrightness" size="sm"
<Label md={6} for="flMaxBrightness" size={$uiSettings.inputSize}
>{$_('section.settings.flMaxBrightness')}</Label
>
<Col md="6">
@ -221,7 +230,7 @@
</Col>
</Row>
<Row>
<Label md={6} for="flEffectDelay" size="sm"
<Label md={6} for="flEffectDelay" size={$uiSettings.inputSize}
>{$_('section.settings.flEffectDelay')}</Label
>
<Col md="6">
@ -238,7 +247,7 @@
</Row>
{/if}
<Row>
<Label md={6} for="hostnamePrefix" size="sm"
<Label md={6} for="hostnamePrefix" size={$uiSettings.inputSize}
>{$_('section.settings.hostnamePrefix')}</Label
>
<Col md="6">
@ -247,12 +256,12 @@
bind:value={$settings.hostnamePrefix}
name="hostnamePrefix"
id="hostnamePrefix"
bsSize="sm"
bsSize={$uiSettings.inputSize}
></Input>
</Col>
</Row>
<Row>
<Label md={6} for="wifiTxPower" size="sm"
<Label md={6} for="wifiTxPower" size={$uiSettings.inputSize}
>{$_('section.settings.wifiTxPower', { default: 'WiFi Tx Power' })}</Label
>
<Col md="6">
@ -261,8 +270,8 @@
bind:value={$settings.txPower}
name="select"
id="fgColor"
bsSize="sm"
class="form-select-sm"
bsSize={$uiSettings.inputSize}
class={$uiSettings.selectClass}
>
{#each wifiTxPowerMap as [key, value]}
<option {value}>{key}</option>
@ -277,7 +286,7 @@
id="ledTestOnPower"
bind:checked={$settings.ledTestOnPower}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.ledPowerOnTest')}
/>
</Col>
@ -286,7 +295,7 @@
id="ledFlashOnUpd"
bind:checked={$settings.ledFlashOnUpd}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.ledFlashOnBlock')}
/>
</Col>
@ -295,7 +304,7 @@
id="stealFocus"
bind:checked={$settings.stealFocus}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.StealFocusOnNewBlock')}
/>
</Col>
@ -304,7 +313,7 @@
id="mcapBigChar"
bind:checked={$settings.mcapBigChar}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.useBigCharsMcap')}
/>
</Col>
@ -313,7 +322,7 @@
id="otaEnabled"
bind:checked={$settings.otaEnabled}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
/>
</Col>
@ -322,7 +331,7 @@
id="mdnsEnabled"
bind:checked={$settings.mdnsEnabled}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
/>
</Col>
@ -331,7 +340,7 @@
id="fetchEurPrice"
bind:checked={$settings.fetchEurPrice}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
/>
</Col>
@ -340,7 +349,7 @@
id="useBlkCountdown"
bind:checked={$settings.useBlkCountdown}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.useBlkCountdown')}
/>
</Col>
@ -349,7 +358,7 @@
id="useSatsSymbol"
bind:checked={$settings.useSatsSymbol}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.useSatsSymbol')}
/>
</Col>
@ -358,7 +367,7 @@
id="suffixPrice"
bind:checked={$settings.suffixPrice}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.suffixPrice')}
/>
</Col>
@ -367,7 +376,7 @@
id="disableLeds"
bind:checked={$settings.disableLeds}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.disableLeds')}
/>
</Col>
@ -376,7 +385,7 @@
id="ownDataSource"
bind:checked={$settings.ownDataSource}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label="{$_('section.settings.ownDataSource')} ({$_('restartRequired')})"
/>
</Col>
@ -386,7 +395,7 @@
id="flAlwaysOn"
bind:checked={$settings.flAlwaysOn}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.flAlwaysOn')}
/>
</Col>
@ -395,7 +404,7 @@
id="flFlashOnUpd"
bind:checked={$settings.flFlashOnUpd}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={$_('section.settings.flFlashOnUpd')}
/>
</Col>
@ -411,15 +420,20 @@
id="screens_{s.id}"
bind:checked={s.enabled}
type="switch"
bsSize="sm"
bsSize={$uiSettings.inputSize}
label={s.name}
/>
</Col>
{/each}
{/if}
</Row>
<Button on:click={handleReset} color="secondary">{$_('button.reset')}</Button>
<Button color="primary">{$_('button.save')}</Button>
<Row>
<Col class="d-flex justify-content-end">
<Button on:click={handleReset} color="secondary">{$_('button.reset')}</Button>
<div class="mx-2"></div>
<Button color="primary">{$_('button.save')}</Button>
</Col>
</Row>
</Form>
</CardBody>
</Card>