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/* node-version: lts/*
cache: yarn cache: yarn
cache-dependency-path: '**/yarn.lock' cache-dependency-path: '**/yarn.lock'
- uses: actions/cache@v3 - uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/pip ~/.cache/pip
@ -80,6 +80,8 @@ jobs:
run: mkdir -p output && echo "$BLOCK_HEIGHT" > output/version.txt run: mkdir -p output && echo "$BLOCK_HEIGHT" > output/version.txt
- name: gzip build for LittleFS - 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' _ {} \; 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 - name: Check GZipped directory size
run: | run: |
# Set the threshold size in bytes # Set the threshold size in bytes

View File

@ -60,6 +60,12 @@
"uptime": "Betriebszeit", "uptime": "Betriebszeit",
"wifiSignalStrength": "WiFi-Signalstärke", "wifiSignalStrength": "WiFi-Signalstärke",
"wsDataConnection": "BTClock-Datenquelle verbindung" "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": { "colors": {
@ -88,5 +94,6 @@
}, },
"rssiBar": { "rssiBar": {
"tooltip": "Werte > -67 dBm gelten als gut. > -30 dBm ist erstaunlich" "tooltip": "Werte > -67 dBm gelten als gut. > -30 dBm ist erstaunlich"
} },
"warning": "Achtung"
} }

View File

@ -48,7 +48,9 @@
"hostname": "Hostname", "hostname": "Hostname",
"frontlight": "Frontlight", "frontlight": "Frontlight",
"turnOn": "Turn on", "turnOn": "Turn on",
"flashFrontlight": "Flash" "flashFrontlight": "Flash",
"firmwareUpdate": "Firmware update",
"fwCommit": "Firmware commit"
}, },
"status": { "status": {
"title": "Status", "title": "Status",
@ -60,6 +62,12 @@
"uptime": "Uptime", "uptime": "Uptime",
"wifiSignalStrength": "WiFi Signal strength", "wifiSignalStrength": "WiFi Signal strength",
"wsDataConnection": "BTClock data-source connection" "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": { "colors": {
@ -88,5 +96,6 @@
}, },
"rssiBar": { "rssiBar": {
"tooltip": "Values > -67 dBm are considered good. > -30 dBm is amazing" "tooltip": "Values > -67 dBm are considered good. > -30 dBm is amazing"
} },
"warning": "Warning"
} }

View File

@ -59,6 +59,12 @@
"title": "Estado", "title": "Estado",
"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"
},
"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": { "button": {
@ -87,5 +93,6 @@
}, },
"rssiBar": { "rssiBar": {
"tooltip": "Se consideran buenos valores > -67 dBm. > -30 dBm es increíble" "tooltip": "Se consideran buenos valores > -67 dBm. > -30 dBm es increíble"
} },
"warning": "Aviso"
} }

View File

@ -59,6 +59,12 @@
"uptime": "Uptime", "uptime": "Uptime",
"wifiSignalStrength": "WiFi signaalsterkte", "wifiSignalStrength": "WiFi signaalsterkte",
"wsDataConnection": "BTClock-gegevensbron verbinding" "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": { "colors": {
@ -87,5 +93,6 @@
}, },
"rssiBar": { "rssiBar": {
"tooltip": "Waarden > -67 dBm zijn goed. > -30 dBm is verbazingwekkend" "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/progress';
@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/helpers'; @import '../node_modules/bootstrap/scss/helpers';
@import '../node_modules/bootstrap/scss/utilities/api'; @import '../node_modules/bootstrap/scss/utilities/api';
@include media-breakpoint-down(xl) { // @include media-breakpoint-down(xl) {
html { // html {
font-size: 85%; // // font-size: 85%;
} // }
button.btn, // button.btn,
input[type='button'].btn, // input[type='button'].btn,
input[type='submit'].btn, // input[type='submit'].btn,
input[type='reset'].btn { // input[type='reset'].btn {
@include button-size( // @include button-size(
$btn-padding-y-sm, // $btn-padding-y-sm,
$btn-padding-x-sm, // $btn-padding-x-sm,
$font-size-sm, // $font-size-sm,
$btn-border-radius-sm // $btn-border-radius-sm
); // );
} // }
} // }
@include media-breakpoint-down(lg) { // @include media-breakpoint-down(lg) {
html { // html {
font-size: 75%; // font-size: 75%;
} // }
} // }
nav { nav {
margin-bottom: 15px; margin-bottom: 15px;
@ -208,3 +209,8 @@ nav {
font-style: italic; font-style: italic;
font-weight: 600; font-weight: 600;
} }
.firmwareUploadStatusAlert,
#firmwareUploadProgress {
@extend .my-2;
}

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { PUBLIC_BASE_URL } from '$lib/config'; import { PUBLIC_BASE_URL } from '$lib/config';
import { screenSize, updateScreenSize } from '$lib/screen';
import { Container, Row, Toast, ToastBody } from 'sveltestrap'; import { Container, Row, Toast, ToastBody } from 'sveltestrap';
@ -13,6 +14,12 @@
fgColor: '0' fgColor: '0'
}); });
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,
@ -61,8 +68,43 @@
let dataObj = JSON.parse(e.data); let dataObj = JSON.parse(e.data);
status.set(dataObj); 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 toastIsOpen = false;
let toastColor = 'success'; let toastColor = 'success';
let toastBody = ''; let toastBody = '';
@ -79,10 +121,15 @@
</svelte:head> </svelte:head>
<Container fluid> <Container fluid>
<Row> <Row cols={{ lg: 3, sm: 1 }}>
<Control bind:settings bind:status></Control> <Control bind:settings bind:uiSettings bind:status></Control>
<Status bind:settings bind:status></Status> <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> </Row>
</Container> </Container>
<div class="position-fixed bottom-0 end-0 p-2"> <div class="position-fixed bottom-0 end-0 p-2">

View File

@ -15,8 +15,11 @@
Label, Label,
Row Row
} from 'sveltestrap'; } from 'sveltestrap';
import FirmwareUpdater from './FirmwareUpdater.svelte';
export let settings = {}; export let settings = {};
export let uiSettings;
export let customText: string; export let customText: string;
export let status: Writable<{ leds: [] }>; export let status: Writable<{ leds: [] }>;
let ledStatus = []; let ledStatus = [];
@ -102,25 +105,35 @@
<CardBody> <CardBody>
<Form> <Form>
<Row> <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"> <Col md="8">
<Input <Input
type="text" type="text"
id="customText" id="customText"
bind:value={customText} bind:value={customText}
bsSize="sm" bsSize="$uiSettings.inputSize"
maxLength={$settings.numScreens} maxLength={$settings.numScreens}
/> />
</Col> </Col>
</Row> </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> </Form>
<hr /> <hr />
{#if !$settings.disableLeds} {#if !$settings.disableLeds}
<h3>LEDs</h3> <h3>LEDs</h3>
<Form> <Form>
<Row> <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"> <Col md="8">
<Row class="justify-content-between"> <Row class="justify-content-between">
{#if ledStatus} {#if ledStatus}
@ -137,51 +150,84 @@
{/each} {/each}
{/if} {/if}
</Row> </Row>
<Row class="justify-content-between"> <Row>
<Col> <Col class="d-flex justify-content-end">
<Input <Input
bind:checked={keepLedsSameColor} bind:checked={keepLedsSameColor}
type="switch" type="switch"
class="mx-auto"
label={$_('sections.control.keepSameColor')} label={$_('sections.control.keepSameColor')}
bsSize={$uiSettings.inputSize}
/> />
</Col> </Col>
</Row> </Row>
</Col> </Col>
</Row> </Row>
<Button color="secondary" id="turnOffLedsBtn" on:click={turnOffLeds} <Row>
>{$_('section.control.turnOff')}</Button <Col class="d-flex justify-content-end">
> <Button
<Button color="primary" on:click={setLEDcolor}>{$_('section.control.setColor')}</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> </Form>
<hr /> <hr />
{/if} {/if}
{#if $settings.hasFrontlight} {#if $settings.hasFrontlight}
<h3>{$_('section.control.frontlight')}</h3> <h3>{$_('section.control.frontlight')}</h3>
<Button color="secondary" id="turnOffFrontlightBtn" on:click={turnOffFrontlight} <Row class="d-flex justify-content-between justify-content-md-end">
>{$_('section.control.turnOff')}</Button <Col md="auto" class="">
> <Button color="secondary" id="turnOffFrontlightBtn" on:click={turnOffFrontlight}
<Button color="primary" on:click={turnOnFrontlight}>{$_('section.control.turnOn')}</Button> >{$_('section.control.turnOff')}</Button
<Button color="success" id="flashFrontlight" on:click={flashFrontlight} >
>{$_('section.control.flashFrontlight')}</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 /> <hr />
{/if} {/if}
<h3>{$_('section.control.systemInfo')}</h3> <h3>{$_('section.control.systemInfo')}</h3>
<ul class="small system_info"> <ul class="small system_info">
<li>{$_('section.control.version')}: {$settings.gitRev}</li>
<li> <li>
{$_('section.control.buildTime')}: {new Date( {$_('section.control.buildTime')}: {new Date(
$settings.lastBuildTime * 1000 $settings.lastBuildTime * 1000
).toLocaleString()} ).toLocaleString()}
</li> </li>
<li>IP: {$settings.ip}</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> <li>{$_('section.control.hostname')}: {$settings.hostname}</li>
</ul> </ul>
<Button color="danger" id="restartBtn" on:click={restartClock}>{$_('button.restart')}</Button> <Row>
<Button color="warning" id="forceFullRefresh" on:click={forceFullRefresh} <Col class="d-flex justify-content-end">
>{$_('button.forceFullRefresh')}</Button <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> </CardBody>
</Card> </Card>
</Col> </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'; } from 'sveltestrap';
export let settings; export let settings;
export let uiSettings;
const wifiTxPowerMap = new Map<string, number>([ const wifiTxPowerMap = new Map<string, number>([
['Default', 80], ['Default', 80],
@ -89,7 +90,7 @@
<CardBody> <CardBody>
<Form on:submit={onSave}> <Form on:submit={onSave}>
<Row> <Row>
<Label md={6} for="fgColor" size="sm" <Label md={6} for="fgColor" size={$uiSettings.inputSize}
>{$_('section.settings.textColor', { default: 'Text color' })}</Label >{$_('section.settings.textColor', { default: 'Text color' })}</Label
> >
<Col md="6"> <Col md="6">
@ -98,8 +99,8 @@
bind:value={$settings.fgColor} bind:value={$settings.fgColor}
name="select" name="select"
id="fgColor" id="fgColor"
bsSize="sm" bsSize={$uiSettings.inputSize}
class="form-select-sm" class={$uiSettings.selectClass}
> >
<option value="0">{$_('colors.black')}</option> <option value="0">{$_('colors.black')}</option>
<option value="65535">{$_('colors.white')}</option> <option value="65535">{$_('colors.white')}</option>
@ -107,15 +108,17 @@
</Col> </Col>
</Row> </Row>
<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"> <Col md="6">
<Input <Input
type="select" type="select"
bind:value={$settings.bgColor} bind:value={$settings.bgColor}
name="select" name="select"
id="bgColor" id="bgColor"
bsSize="sm" bsSize={$uiSettings.inputSize}
class="form-select-sm" class={$uiSettings.selectClass}
> >
<option value="0">{$_('colors.black')}</option> <option value="0">{$_('colors.black')}</option>
<option value="65535">{$_('colors.white')}</option> <option value="65535">{$_('colors.white')}</option>
@ -123,9 +126,11 @@
</Col> </Col>
</Row> </Row>
<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"> <Col md="6">
<InputGroup size="sm"> <InputGroup size={$uiSettings.inputSize}>
<Input <Input
type="number" type="number"
id="timePerScreen" id="timePerScreen"
@ -138,11 +143,11 @@
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Label md={6} for="fullRefreshMin" size="sm" <Label md={6} for="fullRefreshMin" size={$uiSettings.inputSize}
>{$_('section.settings.fullRefreshEvery')}</Label >{$_('section.settings.fullRefreshEvery')}</Label
> >
<Col md="6"> <Col md="6">
<InputGroup size="sm"> <InputGroup size={$uiSettings.inputSize}>
<Input <Input
type="number" type="number"
id="fullRefreshMin" id="fullRefreshMin"
@ -155,11 +160,11 @@
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Label md={6} for="minSecPriceUpd" size="sm" <Label md={6} for="minSecPriceUpd" size={$uiSettings.inputSize}
>{$_('section.settings.timeBetweenPriceUpdates')}</Label >{$_('section.settings.timeBetweenPriceUpdates')}</Label
> >
<Col md="6"> <Col md="6">
<InputGroup size="sm"> <InputGroup size={$uiSettings.inputSize}>
<Input <Input
type="number" type="number"
id="minSecPriceUpd" id="minSecPriceUpd"
@ -173,9 +178,11 @@
</Col> </Col>
</Row> </Row>
<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"> <Col md="6">
<InputGroup size="sm"> <InputGroup size={$uiSettings.inputSize}>
<Input <Input
type="number" type="number"
step="1" step="1"
@ -189,7 +196,9 @@
</Col> </Col>
</Row> </Row>
<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"> <Col md="6">
<Input <Input
type="range" type="range"
@ -204,7 +213,7 @@
</Row> </Row>
{#if $settings.hasFrontlight} {#if $settings.hasFrontlight}
<Row> <Row>
<Label md={6} for="flMaxBrightness" size="sm" <Label md={6} for="flMaxBrightness" size={$uiSettings.inputSize}
>{$_('section.settings.flMaxBrightness')}</Label >{$_('section.settings.flMaxBrightness')}</Label
> >
<Col md="6"> <Col md="6">
@ -221,7 +230,7 @@
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Label md={6} for="flEffectDelay" size="sm" <Label md={6} for="flEffectDelay" size={$uiSettings.inputSize}
>{$_('section.settings.flEffectDelay')}</Label >{$_('section.settings.flEffectDelay')}</Label
> >
<Col md="6"> <Col md="6">
@ -238,7 +247,7 @@
</Row> </Row>
{/if} {/if}
<Row> <Row>
<Label md={6} for="hostnamePrefix" size="sm" <Label md={6} for="hostnamePrefix" size={$uiSettings.inputSize}
>{$_('section.settings.hostnamePrefix')}</Label >{$_('section.settings.hostnamePrefix')}</Label
> >
<Col md="6"> <Col md="6">
@ -247,12 +256,12 @@
bind:value={$settings.hostnamePrefix} bind:value={$settings.hostnamePrefix}
name="hostnamePrefix" name="hostnamePrefix"
id="hostnamePrefix" id="hostnamePrefix"
bsSize="sm" bsSize={$uiSettings.inputSize}
></Input> ></Input>
</Col> </Col>
</Row> </Row>
<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 >{$_('section.settings.wifiTxPower', { default: 'WiFi Tx Power' })}</Label
> >
<Col md="6"> <Col md="6">
@ -261,8 +270,8 @@
bind:value={$settings.txPower} bind:value={$settings.txPower}
name="select" name="select"
id="fgColor" id="fgColor"
bsSize="sm" bsSize={$uiSettings.inputSize}
class="form-select-sm" class={$uiSettings.selectClass}
> >
{#each wifiTxPowerMap as [key, value]} {#each wifiTxPowerMap as [key, value]}
<option {value}>{key}</option> <option {value}>{key}</option>
@ -277,7 +286,7 @@
id="ledTestOnPower" id="ledTestOnPower"
bind:checked={$settings.ledTestOnPower} bind:checked={$settings.ledTestOnPower}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.ledPowerOnTest')} label={$_('section.settings.ledPowerOnTest')}
/> />
</Col> </Col>
@ -286,7 +295,7 @@
id="ledFlashOnUpd" id="ledFlashOnUpd"
bind:checked={$settings.ledFlashOnUpd} bind:checked={$settings.ledFlashOnUpd}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.ledFlashOnBlock')} label={$_('section.settings.ledFlashOnBlock')}
/> />
</Col> </Col>
@ -295,7 +304,7 @@
id="stealFocus" id="stealFocus"
bind:checked={$settings.stealFocus} bind:checked={$settings.stealFocus}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.StealFocusOnNewBlock')} label={$_('section.settings.StealFocusOnNewBlock')}
/> />
</Col> </Col>
@ -304,7 +313,7 @@
id="mcapBigChar" id="mcapBigChar"
bind:checked={$settings.mcapBigChar} bind:checked={$settings.mcapBigChar}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.useBigCharsMcap')} label={$_('section.settings.useBigCharsMcap')}
/> />
</Col> </Col>
@ -313,7 +322,7 @@
id="otaEnabled" id="otaEnabled"
bind:checked={$settings.otaEnabled} bind:checked={$settings.otaEnabled}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})" label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
/> />
</Col> </Col>
@ -322,7 +331,7 @@
id="mdnsEnabled" id="mdnsEnabled"
bind:checked={$settings.mdnsEnabled} bind:checked={$settings.mdnsEnabled}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})" label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
/> />
</Col> </Col>
@ -331,7 +340,7 @@
id="fetchEurPrice" id="fetchEurPrice"
bind:checked={$settings.fetchEurPrice} bind:checked={$settings.fetchEurPrice}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})" label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
/> />
</Col> </Col>
@ -340,7 +349,7 @@
id="useBlkCountdown" id="useBlkCountdown"
bind:checked={$settings.useBlkCountdown} bind:checked={$settings.useBlkCountdown}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.useBlkCountdown')} label={$_('section.settings.useBlkCountdown')}
/> />
</Col> </Col>
@ -349,7 +358,7 @@
id="useSatsSymbol" id="useSatsSymbol"
bind:checked={$settings.useSatsSymbol} bind:checked={$settings.useSatsSymbol}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.useSatsSymbol')} label={$_('section.settings.useSatsSymbol')}
/> />
</Col> </Col>
@ -358,7 +367,7 @@
id="suffixPrice" id="suffixPrice"
bind:checked={$settings.suffixPrice} bind:checked={$settings.suffixPrice}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.suffixPrice')} label={$_('section.settings.suffixPrice')}
/> />
</Col> </Col>
@ -367,7 +376,7 @@
id="disableLeds" id="disableLeds"
bind:checked={$settings.disableLeds} bind:checked={$settings.disableLeds}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.disableLeds')} label={$_('section.settings.disableLeds')}
/> />
</Col> </Col>
@ -376,7 +385,7 @@
id="ownDataSource" id="ownDataSource"
bind:checked={$settings.ownDataSource} bind:checked={$settings.ownDataSource}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label="{$_('section.settings.ownDataSource')} ({$_('restartRequired')})" label="{$_('section.settings.ownDataSource')} ({$_('restartRequired')})"
/> />
</Col> </Col>
@ -386,7 +395,7 @@
id="flAlwaysOn" id="flAlwaysOn"
bind:checked={$settings.flAlwaysOn} bind:checked={$settings.flAlwaysOn}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.flAlwaysOn')} label={$_('section.settings.flAlwaysOn')}
/> />
</Col> </Col>
@ -395,7 +404,7 @@
id="flFlashOnUpd" id="flFlashOnUpd"
bind:checked={$settings.flFlashOnUpd} bind:checked={$settings.flFlashOnUpd}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={$_('section.settings.flFlashOnUpd')} label={$_('section.settings.flFlashOnUpd')}
/> />
</Col> </Col>
@ -411,15 +420,20 @@
id="screens_{s.id}" id="screens_{s.id}"
bind:checked={s.enabled} bind:checked={s.enabled}
type="switch" type="switch"
bsSize="sm" bsSize={$uiSettings.inputSize}
label={s.name} label={s.name}
/> />
</Col> </Col>
{/each} {/each}
{/if} {/if}
</Row> </Row>
<Button on:click={handleReset} color="secondary">{$_('button.reset')}</Button> <Row>
<Button color="primary">{$_('button.save')}</Button> <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> </Form>
</CardBody> </CardBody>
</Card> </Card>