feat: Lint fixes, add forgejo workflow and e2e tests
This commit is contained in:
parent
af2f593fb8
commit
5917713b0d
39 changed files with 1666 additions and 1506 deletions
|
@ -3,11 +3,11 @@
|
|||
import { CardContainer, InputField, Toggle } from '$lib/components';
|
||||
import { settings, status } from '$lib/stores';
|
||||
import { onDestroy } from 'svelte';
|
||||
import {
|
||||
setCustomText,
|
||||
setLEDcolor,
|
||||
turnOffLeds,
|
||||
restartClock,
|
||||
import {
|
||||
setCustomText,
|
||||
setLEDcolor,
|
||||
turnOffLeds,
|
||||
restartClock,
|
||||
forceFullRefresh,
|
||||
generateRandomColor,
|
||||
flashFrontlight,
|
||||
|
@ -17,10 +17,10 @@
|
|||
import type { LedStatus } from '$lib/types';
|
||||
|
||||
let ledStatus = $state<LedStatus[]>([
|
||||
{hex: '#000000'},
|
||||
{hex: '#000000'},
|
||||
{hex: '#000000'},
|
||||
{hex: '#000000'}
|
||||
{ hex: '#000000' },
|
||||
{ hex: '#000000' },
|
||||
{ hex: '#000000' },
|
||||
{ hex: '#000000' }
|
||||
]);
|
||||
let customText = $state('');
|
||||
let keepLedsSameColor = $state(false);
|
||||
|
@ -28,7 +28,7 @@
|
|||
const checkSyncLeds = (e: Event) => {
|
||||
if (keepLedsSameColor && e.target instanceof HTMLInputElement) {
|
||||
const targetValue = e.target.value;
|
||||
|
||||
|
||||
ledStatus.forEach((element, i) => {
|
||||
if (ledStatus[i].hex != targetValue) {
|
||||
ledStatus[i].hex = targetValue;
|
||||
|
@ -81,19 +81,21 @@
|
|||
<div class="flex justify-between gap-2">
|
||||
<div class="mb-4 flex flex-wrap gap-2">
|
||||
{#if ledStatus.length > 0}
|
||||
{#each ledStatus as led}
|
||||
{#each ledStatus as led (led)}
|
||||
<input
|
||||
type="color"
|
||||
class="btn btn-square"
|
||||
bind:value={led.hex}
|
||||
onchange={checkSyncLeds}
|
||||
/>
|
||||
{/each}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
<Toggle label={m['sections.control.keepSameColor']()} bind:checked={keepLedsSameColor} />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-secondary" onclick={turnOffLeds}>{m['section.control.turnOff']()}</button>
|
||||
<button class="btn btn-secondary" onclick={turnOffLeds}
|
||||
>{m['section.control.turnOff']()}</button
|
||||
>
|
||||
<button class="btn btn-primary" onclick={() => setLEDcolor(ledStatus)}
|
||||
>{m['section.control.setColor']()}</button
|
||||
>
|
||||
|
@ -102,19 +104,24 @@
|
|||
</div>
|
||||
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<div>
|
||||
<h3 class="mb-2 font-medium">{m['section.control.frontlight']()}</h3>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button class="btn btn-secondary" onclick={() => turnOnFrontlight()}>{m['section.control.turnOn']()}</button>
|
||||
<button class="btn btn-primary" onclick={() => turnOffFrontlight()}>{m['section.control.turnOff']()}</button>
|
||||
<button class="btn btn-accent" onclick={() => flashFrontlight()}>{m['section.control.flashFrontlight']()}</button>
|
||||
<div>
|
||||
<h3 class="mb-2 font-medium">{m['section.control.frontlight']()}</h3>
|
||||
<div class="flex justify-end gap-2">
|
||||
<button class="btn btn-secondary" onclick={() => turnOnFrontlight()}
|
||||
>{m['section.control.turnOn']()}</button
|
||||
>
|
||||
<button class="btn btn-primary" onclick={() => turnOffFrontlight()}
|
||||
>{m['section.control.turnOff']()}</button
|
||||
>
|
||||
<button class="btn btn-accent" onclick={() => flashFrontlight()}
|
||||
>{m['section.control.flashFrontlight']()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<h3 class="mb-2 font-medium">{m['section.control.title']()}</h3>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button class="btn btn-error" onclick={restartClock}>{m['button.restart']()}</button>
|
||||
<button class="btn" onclick={forceFullRefresh}>{m['button.forceFullRefresh']()}</button>
|
||||
</div>
|
||||
|
|
|
@ -1,341 +1,361 @@
|
|||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { CardContainer, Toggle, CollapsibleSection } from '$lib/components';
|
||||
import { settings } from '$lib/stores';
|
||||
|
||||
let { ...restProps } = $props();
|
||||
|
||||
// Show/hide toggles
|
||||
let showAll = $state(false);
|
||||
let hideAll = $state(false);
|
||||
|
||||
function toggleShowAll() {
|
||||
showAll = true;
|
||||
hideAll = false;
|
||||
}
|
||||
|
||||
function toggleHideAll() {
|
||||
hideAll = true;
|
||||
showAll = false;
|
||||
}
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { CardContainer, Toggle, CollapsibleSection } from '$lib/components';
|
||||
import { settings } from '$lib/stores';
|
||||
|
||||
let { ...restProps } = $props();
|
||||
|
||||
// Show/hide toggles
|
||||
let showAll = $state(false);
|
||||
let hideAll = $state(false);
|
||||
|
||||
function toggleShowAll() {
|
||||
showAll = true;
|
||||
hideAll = false;
|
||||
}
|
||||
|
||||
function toggleHideAll() {
|
||||
hideAll = true;
|
||||
showAll = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<CardContainer title={m["section.settings.title"]()} {...restProps}>
|
||||
<div class="flex justify-end gap-2 mb-4">
|
||||
<button class="btn btn-sm" onclick={toggleShowAll}>{m["section.settings.showAll"]()}</button>
|
||||
<button class="btn btn-sm" onclick={toggleHideAll}>{m["section.settings.hideAll"]()}</button>
|
||||
</div>
|
||||
<CardContainer title={m['section.settings.title']()} {...restProps}>
|
||||
<div class="mb-4 flex justify-end gap-2">
|
||||
<button class="btn btn-sm" onclick={toggleShowAll}>{m['section.settings.showAll']()}</button>
|
||||
<button class="btn btn-sm" onclick={toggleHideAll}>{m['section.settings.hideAll']()}</button>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 grid-cols-2">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<CollapsibleSection
|
||||
title={m['section.settings.section.screenSettings']()}
|
||||
open={showAll || !hideAll}
|
||||
>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.StealFocusOnNewBlock']()}
|
||||
bind:checked={$settings.stealFocus}
|
||||
/>
|
||||
<p class="text-xs">
|
||||
When a new block is mined, it will switch focus from the current screen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.section.screenSettings"]()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4 grid-cols-2">
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.StealFocusOnNewBlock"]()}
|
||||
bind:checked={$settings.stealFocus}
|
||||
/>
|
||||
<p class="text-xs">When a new block is mined, it will switch focus from the current screen.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.useBigCharsMcap"]()}
|
||||
bind:checked={$settings.mcapBigChar}
|
||||
/>
|
||||
<p class="text-xs">Use big characters for the market cap screen instead of using a suffix.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.useBlkCountdown"]()}
|
||||
bind:checked={$settings.useBlkCountdown}
|
||||
/>
|
||||
<p class="text-xs">When enabled it count down blocks instead of years/monts/days/hours/minutes.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.useSatsSymbol"]()}
|
||||
bind:checked={$settings.useSatsSymbol}
|
||||
/>
|
||||
<p class="text-xs">Prefix satoshi amounts with the sats symbol.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.suffixPrice"]()}
|
||||
bind:checked={$settings.suffixPrice}
|
||||
/>
|
||||
<p class="text-xs">Always use a suffix for the ticker screen.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.verticalDesc"]()}
|
||||
bind:checked={$settings.verticalDesc}
|
||||
/>
|
||||
<p class="text-xs">Rotate the description of the screen 90 degrees.</p>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.useBigCharsMcap']()}
|
||||
bind:checked={$settings.mcapBigChar}
|
||||
/>
|
||||
<p class="text-xs">
|
||||
Use big characters for the market cap screen instead of using a suffix.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.screens"]()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4 grid-cols-2">
|
||||
{#each $settings.screens as screen}
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={screen.name}
|
||||
checked={screen.enabled}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.useBlkCountdown']()}
|
||||
bind:checked={$settings.useBlkCountdown}
|
||||
/>
|
||||
<p class="text-xs">
|
||||
When enabled it count down blocks instead of years/monts/days/hours/minutes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.currencies"]()} open={showAll || !hideAll}>
|
||||
<div class="alert alert-warning">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
|
||||
<span>restart required</span>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.useSatsSymbol']()}
|
||||
bind:checked={$settings.useSatsSymbol}
|
||||
/>
|
||||
<p class="text-xs">Prefix satoshi amounts with the sats symbol.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 grid-cols-2">
|
||||
|
||||
{#each $settings.actCurrencies as currency}
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={currency}
|
||||
checked={$settings.actCurrencies.includes(currency)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.suffixPrice']()}
|
||||
bind:checked={$settings.suffixPrice}
|
||||
/>
|
||||
<p class="text-xs">Always use a suffix for the ticker screen.</p>
|
||||
</div>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.section.displaysAndLed"]()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.textColor"]()}</span>
|
||||
</label>
|
||||
<select class="select select-bordered w-full">
|
||||
<option>White on Black</option>
|
||||
<option>Black on White</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Font</span>
|
||||
</label>
|
||||
<select class="select select-bordered w-full">
|
||||
<option>Oswald</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.timePerScreen"]()}</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="number" class="input input-bordered w-20" min="1" max="60" value="1" />
|
||||
<span>{m["time.minutes"]()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control flex justify-between">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.fullRefreshEvery"]()}</span>
|
||||
</label>
|
||||
<div class="w-auto input">
|
||||
<input type="number" class="" min="1" max="60" value="60" />
|
||||
<span class="label">{m["time.minutes"]()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control flex justify-between">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.timeBetweenPriceUpdates"]()}</span>
|
||||
</label>
|
||||
<div class="w-auto input">
|
||||
<input type="number" class="" min="1" max="60" value="30" />
|
||||
<span class="label">{m["time.seconds"]()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.ledBrightness"]()}</span>
|
||||
</label>
|
||||
<input type="range" min="0" max="100" class="range" value="50" />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.ledPowerOnTest"]()}
|
||||
checked={$settings.ledTestOnPower}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.ledFlashOnBlock"]()}
|
||||
checked={$settings.ledFlashOnUpd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.disableLeds"]()}
|
||||
checked={$settings.disableLeds}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.verticalDesc']()}
|
||||
bind:checked={$settings.verticalDesc}
|
||||
/>
|
||||
<p class="text-xs">Rotate the description of the screen 90 degrees.</p>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
{#if $settings.hasFrontlight}
|
||||
<CollapsibleSection title="Frontlight Settings" open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label="Disable Frontlight"
|
||||
checked={$settings.flDisable}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label="Always On"
|
||||
checked={$settings.flAlwaysOn}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label="Flash on Updates"
|
||||
checked={$settings.flFlashOnUpd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label="Flash on Zaps"
|
||||
checked={$settings.flFlashOnZap}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if $settings.hasLightLevel}
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label="Turn Off in Dark"
|
||||
checked={$settings.flOffWhenDark}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Light Level Threshold</span>
|
||||
</label>
|
||||
<input type="range" min="0" max="255" class="range" value={$settings.luxLightToggle} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Maximum Brightness</span>
|
||||
</label>
|
||||
<input type="range" min="0" max="4095" class="range" value={$settings.flMaxBrightness} />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Effect Delay (ms)</span>
|
||||
</label>
|
||||
<input type="number" class="input input-bordered w-20" min="10" max="1000" value={$settings.flEffectDelay} />
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
{/if}
|
||||
<CollapsibleSection title={m['section.settings.screens']()} open={showAll || !hideAll}>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{#each $settings.screens as screen (screen.id)}
|
||||
<div class="form-control">
|
||||
<Toggle label={screen.name} checked={screen.enabled} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.section.dataSource"]()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.dataSource.label"]()}</span>
|
||||
</label>
|
||||
<select class="select select-bordered w-full">
|
||||
<option value="btclock">{m["section.settings.dataSource.btclock"]()}</option>
|
||||
<option value="thirdparty">{m["section.settings.dataSource.thirdParty"]()}</option>
|
||||
<option value="nostr">{m["section.settings.dataSource.nostr"]()}</option>
|
||||
<option value="custom">{m["section.settings.dataSource.custom"]()}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.mempoolnstance"]()}</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered w-full" value="mempool.space/coinlcp.io" />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.ceEndpoint"]()}</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered w-full" placeholder="Custom Endpoint URL" />
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<CollapsibleSection title={m['section.settings.currencies']()} open={showAll || !hideAll}>
|
||||
<div class="alert alert-warning">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/></svg
|
||||
>
|
||||
<span>restart required</span>
|
||||
</div>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.section.extraFeatures"]()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m["section.settings.timeBasedDnd"]()}
|
||||
checked={$settings.dnd.enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{#each $settings.actCurrencies as currency (currency)}
|
||||
<div class="form-control">
|
||||
<Toggle label={currency} checked={$settings.actCurrencies.includes(currency)} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<CollapsibleSection title={m["section.settings.section.system"]()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.timezoneOffset"]()}</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<select class="select select-bordered w-full">
|
||||
<option>Europe/Amsterdam</option>
|
||||
</select>
|
||||
<button class="btn">{m["auto-detect"]()}</button>
|
||||
</div>
|
||||
<p class="text-sm mt-1">{m["section.settings.tzOffsetHelpText"]()}</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.hostnamePrefix"]()}</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered w-full" value="btclock" />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m["section.settings.wpTimeout"]()}</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="number" class="input input-bordered w-20" min="1" max="900" value="600" />
|
||||
<span>{m["time.seconds"]()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<CollapsibleSection
|
||||
title={m['section.settings.section.displaysAndLed']()}
|
||||
open={showAll || !hideAll}
|
||||
>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.textColor']()}</span>
|
||||
</label>
|
||||
<select class="select select-bordered w-full">
|
||||
<option>White on Black</option>
|
||||
<option>Black on White</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex justify-between mt-6">
|
||||
<button class="btn btn-error">{m["button.reset"]()}</button>
|
||||
<button class="btn btn-primary">{m["button.save"]()}</button>
|
||||
</div>
|
||||
</CardContainer>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Font</span>
|
||||
</label>
|
||||
<select class="select select-bordered w-full">
|
||||
<option>Oswald</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.timePerScreen']()}</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="number" class="input input-bordered w-20" min="1" max="60" value="1" />
|
||||
<span>{m['time.minutes']()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control flex justify-between">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.fullRefreshEvery']()}</span>
|
||||
</label>
|
||||
<div class="input w-auto">
|
||||
<input type="number" class="" min="1" max="60" value="60" />
|
||||
<span class="label">{m['time.minutes']()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control flex justify-between">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.timeBetweenPriceUpdates']()}</span>
|
||||
</label>
|
||||
<div class="input w-auto">
|
||||
<input type="number" class="" min="1" max="60" value="30" />
|
||||
<span class="label">{m['time.seconds']()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.ledBrightness']()}</span>
|
||||
</label>
|
||||
<input type="range" min="0" max="100" class="range" value="50" />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.ledPowerOnTest']()}
|
||||
checked={$settings.ledTestOnPower}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle
|
||||
label={m['section.settings.ledFlashOnBlock']()}
|
||||
checked={$settings.ledFlashOnUpd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle label={m['section.settings.disableLeds']()} checked={$settings.disableLeds} />
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
{#if $settings.hasFrontlight}
|
||||
<CollapsibleSection title="Frontlight Settings" open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<Toggle label="Disable Frontlight" checked={$settings.flDisable} />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle label="Always On" checked={$settings.flAlwaysOn} />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle label="Flash on Updates" checked={$settings.flFlashOnUpd} />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<Toggle label="Flash on Zaps" checked={$settings.flFlashOnZap} />
|
||||
</div>
|
||||
|
||||
{#if $settings.hasLightLevel}
|
||||
<div class="form-control">
|
||||
<Toggle label="Turn Off in Dark" checked={$settings.flOffWhenDark} />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Light Level Threshold</span>
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="255"
|
||||
class="range"
|
||||
value={$settings.luxLightToggle}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Maximum Brightness</span>
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="4095"
|
||||
class="range"
|
||||
value={$settings.flMaxBrightness}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Effect Delay (ms)</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
class="input input-bordered w-20"
|
||||
min="10"
|
||||
max="1000"
|
||||
value={$settings.flEffectDelay}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
{/if}
|
||||
|
||||
<CollapsibleSection
|
||||
title={m['section.settings.section.dataSource']()}
|
||||
open={showAll || !hideAll}
|
||||
>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.dataSource.label']()}</span>
|
||||
</label>
|
||||
<select class="select select-bordered w-full">
|
||||
<option value="btclock">{m['section.settings.dataSource.btclock']()}</option>
|
||||
<option value="thirdparty">{m['section.settings.dataSource.thirdParty']()}</option>
|
||||
<option value="nostr">{m['section.settings.dataSource.nostr']()}</option>
|
||||
<option value="custom">{m['section.settings.dataSource.custom']()}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.mempoolnstance']()}</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered w-full" value="mempool.space/coinlcp.io" />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.ceEndpoint']()}</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
placeholder="Custom Endpoint URL"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<CollapsibleSection
|
||||
title={m['section.settings.section.extraFeatures']()}
|
||||
open={showAll || !hideAll}
|
||||
>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<Toggle label={m['section.settings.timeBasedDnd']()} checked={$settings.dnd.enabled} />
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<CollapsibleSection title={m['section.settings.section.system']()} open={showAll || !hideAll}>
|
||||
<div class="grid gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.timezoneOffset']()}</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<select class="select select-bordered w-full">
|
||||
<option>Europe/Amsterdam</option>
|
||||
</select>
|
||||
<button class="btn">{m['auto-detect']()}</button>
|
||||
</div>
|
||||
<p class="mt-1 text-sm">{m['section.settings.tzOffsetHelpText']()}</p>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.hostnamePrefix']()}</span>
|
||||
</label>
|
||||
<input type="text" class="input input-bordered w-full" value="btclock" />
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{m['section.settings.wpTimeout']()}</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="number" class="input input-bordered w-20" min="1" max="900" value="600" />
|
||||
<span>{m['time.seconds']()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
</div>
|
||||
<div class="mt-6 flex justify-between">
|
||||
<button class="btn btn-error">{m['button.reset']()}</button>
|
||||
<button class="btn btn-primary">{m['button.save']()}</button>
|
||||
</div>
|
||||
</CardContainer>
|
||||
|
|
|
@ -2,53 +2,54 @@
|
|||
import { m } from '$lib/paraglide/messages';
|
||||
import { CardContainer, TabButton, CurrencyButton, Stat, Status } from '$lib/components';
|
||||
import { status, settings } from '$lib/stores';
|
||||
import { setActiveScreen, setActiveCurrency } from '$lib/clockControl';
|
||||
import { setActiveScreen, setActiveCurrency } from '$lib/clockControl';
|
||||
import BTClock from '../BTClock.svelte';
|
||||
import { DataSourceType } from '$lib/types';
|
||||
import { toUptimestring } from '$lib/utils';
|
||||
|
||||
const screens = $settings.screens.map(screen => ({
|
||||
id: screen.id,
|
||||
label: screen.name
|
||||
}));
|
||||
const screens = $settings.screens.map((screen) => ({
|
||||
id: screen.id,
|
||||
label: screen.name
|
||||
}));
|
||||
</script>
|
||||
|
||||
<CardContainer title={m['section.status.title']()}>
|
||||
<div class="space-y-4 mx-auto">
|
||||
<div class="mx-auto space-y-4">
|
||||
<div class="join">
|
||||
{#each screens as screen}
|
||||
<TabButton active={$status.currentScreen === screen.id} onClick={() => setActiveScreen(screen.id)}>
|
||||
{#each screens as screen (screen.id)}
|
||||
<TabButton
|
||||
active={$status.currentScreen === screen.id}
|
||||
onClick={() => setActiveScreen(screen.id)}
|
||||
>
|
||||
{screen.label}
|
||||
</TabButton>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="join flex justify-center">
|
||||
{#each $settings.actCurrencies as currency}
|
||||
<CurrencyButton
|
||||
currency={currency}
|
||||
active={$status.currency === currency}
|
||||
onClick={() => setActiveCurrency(currency)}
|
||||
/>
|
||||
{/each}
|
||||
{#each $settings.actCurrencies as currency (currency)}
|
||||
<CurrencyButton
|
||||
{currency}
|
||||
active={$status.currency === currency}
|
||||
onClick={() => setActiveCurrency(currency)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex justify-center">
|
||||
<div class="w-3/4">
|
||||
<!-- Bitcoin value display showing blocks/price -->
|
||||
<BTClock displays={$status.data} verticalDesc={$settings.verticalDesc} />
|
||||
{$settings.verticalDesc}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-sm text-gray-500">
|
||||
{m['section.status.screenCycle']()}: is {$status.timerRunning ? 'running' : 'stopped'}<br />
|
||||
{m['section.status.doNotDisturb']()}: {$status.dnd.enabled ? m['on']() : m['off']()}
|
||||
{m['section.status.doNotDisturb']()}: {$status.dnd.enabled ? m['on']() : m['off']()}
|
||||
<small>
|
||||
{#if $status.dnd?.timeBasedEnabled}
|
||||
{m['section.status.timeBasedDnd']()} ( {$settings.dnd
|
||||
.startHour}:{$settings.dnd.startMinute.toString().padStart(2, '0')} - {$settings
|
||||
.dnd.endHour}:{$settings.dnd.endMinute.toString().padStart(2, '0')} )
|
||||
.startHour}:{$settings.dnd.startMinute.toString().padStart(2, '0')} - {$settings.dnd
|
||||
.endHour}:{$settings.dnd.endMinute.toString().padStart(2, '0')} )
|
||||
{/if}
|
||||
</small>
|
||||
</div>
|
||||
|
@ -58,18 +59,28 @@
|
|||
<Status text="Nostr Relay connection" status={$status.nostr ? 'online' : 'offline'} />
|
||||
{/if}
|
||||
{#if $settings.dataSource === DataSourceType.THIRD_PARTY_SOURCE}
|
||||
<Status text={m['section.status.wsPriceConnection']()} status={$status.connectionStatus.price ? 'online' : 'offline'} />
|
||||
<Status text={m['section.status.wsMempoolConnection']({ instance: $settings.mempoolInstance })} status={$status.connectionStatus.blocks ? 'online' : 'offline'} />
|
||||
<Status
|
||||
text={m['section.status.wsPriceConnection']()}
|
||||
status={$status.connectionStatus.price ? 'online' : 'offline'}
|
||||
/>
|
||||
<Status
|
||||
text={m['section.status.wsMempoolConnection']({ instance: $settings.mempoolInstance })}
|
||||
status={$status.connectionStatus.blocks ? 'online' : 'offline'}
|
||||
/>
|
||||
{:else}
|
||||
<Status text={m['section.status.wsDataConnection']()} status={$status.connectionStatus.V2 ? 'online' : 'offline'} />
|
||||
<Status
|
||||
text={m['section.status.wsDataConnection']()}
|
||||
status={$status.connectionStatus.V2 ? 'online' : 'offline'}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="flex justify-center stats shadow mt-4">
|
||||
<Stat title={m['section.status.memoryFree']()} value={`${Math.round($status.espFreeHeap / 1024)} / ${Math.round($status.espHeapSize / 1024)} KiB`} />
|
||||
<Stat title={m['section.status.wifiSignalStrength']()} value={`${$status.rssi} dBm`} />
|
||||
<Stat title={m['section.status.uptime']()} value={`${toUptimestring($status.espUptime)}`} />
|
||||
</div>
|
||||
<div class="stats mt-4 flex justify-center shadow">
|
||||
<Stat
|
||||
title={m['section.status.memoryFree']()}
|
||||
value={`${Math.round($status.espFreeHeap / 1024)} / ${Math.round($status.espHeapSize / 1024)} KiB`}
|
||||
/>
|
||||
<Stat title={m['section.status.wifiSignalStrength']()} value={`${$status.rssi} dBm`} />
|
||||
<Stat title={m['section.status.uptime']()} value={`${toUptimestring($status.espUptime)}`} />
|
||||
</div>
|
||||
</CardContainer>
|
||||
|
|
|
@ -1,102 +1,99 @@
|
|||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { CardContainer } from '$lib/components';
|
||||
import { settings } from '$lib/stores';
|
||||
import {
|
||||
restartClock,
|
||||
forceFullRefresh
|
||||
} from '$lib/clockControl';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { CardContainer } from '$lib/components';
|
||||
import { settings } from '$lib/stores';
|
||||
import { restartClock, forceFullRefresh } from '$lib/clockControl';
|
||||
</script>
|
||||
|
||||
<CardContainer title="System Information">
|
||||
<div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-sm table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">System info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-sm table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">System info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{m['section.control.version']()}</td>
|
||||
<td>{$settings.gitTag}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{m['section.control.buildTime']()}</td>
|
||||
<td>{$settings.lastBuildTime}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP</td>
|
||||
<td>{$settings.ip}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HW revision</td>
|
||||
<td>{$settings.hwRev}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{m['section.control.fwCommit']()}</td>
|
||||
<td class="text-xs">{$settings.gitRev}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{m['section.control.hostname']()}</td>
|
||||
<td>{$settings.hostname}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-4 flex gap-2">
|
||||
<button class="btn btn-error" onclick={restartClock}>{m['button.restart']()}</button>
|
||||
<button class="btn" onclick={forceFullRefresh}>{m['button.forceFullRefresh']()}</button>
|
||||
</div>
|
||||
</div>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{m['section.control.version']()}</td>
|
||||
<td>{$settings.gitTag}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{m['section.control.buildTime']()}</td>
|
||||
<td>{$settings.lastBuildTime}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP</td>
|
||||
<td>{$settings.ip}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HW revision</td>
|
||||
<td>{$settings.hwRev}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{m['section.control.fwCommit']()}</td>
|
||||
<td class="text-xs">{$settings.gitRev}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{m['section.control.hostname']()}</td>
|
||||
<td>{$settings.hostname}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-4 flex gap-2">
|
||||
<button class="btn btn-error" onclick={restartClock}>{m['button.restart']()}</button>
|
||||
<button class="btn" onclick={forceFullRefresh}>{m['button.forceFullRefresh']()}</button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContainer>
|
||||
|
||||
<CardContainer title={m['section.control.firmwareUpdate']()} className="mt-4">
|
||||
<div>
|
||||
<p class="mb-2 text-sm">
|
||||
Latest Version: 3.3.5 - Release Date: 5/2/2025, 12:37:14 AM - <a
|
||||
href="#"
|
||||
class="link link-primary">{m['section.firmwareUpdater.viewRelease']()}</a
|
||||
>
|
||||
</p>
|
||||
<p class="text-success mb-4 text-sm">{m['section.firmwareUpdater.swUpToDate']()}</p>
|
||||
<div>
|
||||
<p class="mb-2 text-sm">
|
||||
Latest Version: 3.3.5 - Release Date: 5/2/2025, 12:37:14 AM - <a
|
||||
href="#"
|
||||
class="link link-primary">{m['section.firmwareUpdater.viewRelease']()}</a
|
||||
>
|
||||
</p>
|
||||
<p class="text-success mb-4 text-sm">{m['section.firmwareUpdater.swUpToDate']()}</p>
|
||||
|
||||
<div class="form-control mb-4">
|
||||
<label class="label" for="firmwareFile">
|
||||
<span class="label-text">Firmware File (blib_s3_mini_213epd_firmware.bin)</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="file" class="file-input file-input-bordered w-full" id="firmwareFile" />
|
||||
<button class="btn btn-primary">{m['section.control.firmwareUpdate']()}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control mb-4">
|
||||
<label class="label" for="firmwareFile">
|
||||
<span class="label-text">Firmware File (blib_s3_mini_213epd_firmware.bin)</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="file" class="file-input file-input-bordered w-full" id="firmwareFile" />
|
||||
<button class="btn btn-primary">{m['section.control.firmwareUpdate']()}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label" for="webuiFile">
|
||||
<span class="label-text">WebUI File (littlefs_4MB.bin)</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="file" class="file-input file-input-bordered w-full" id="webuiFile" />
|
||||
<button class="btn btn-primary">Update WebUI</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="webuiFile">
|
||||
<span class="label-text">WebUI File (littlefs_4MB.bin)</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="file" class="file-input file-input-bordered w-full" id="webuiFile" />
|
||||
<button class="btn btn-primary">Update WebUI</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning mt-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/></svg
|
||||
>
|
||||
<span>{m['section.firmwareUpdater.firmwareUpdateText']()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContainer>
|
||||
<div class="alert alert-warning mt-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/></svg
|
||||
>
|
||||
<span>{m['section.firmwareUpdater.firmwareUpdateText']()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContainer>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue