diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml new file mode 100644 index 0000000..c14d6e5 --- /dev/null +++ b/.forgejo/workflows/build.yaml @@ -0,0 +1,132 @@ +on: + push: + branches: + - main + pull_request: + +jobs: + check-changes: + runs-on: docker + outputs: + all_changed_and_modified_files_count: ${{ steps.changed-files.outputs.all_changed_and_modified_files_count }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get changed files count + id: changed-files + uses: tj-actions/changed-files@v45 + with: + files_ignore: 'doc/**,README.md,Dockerfile,.*' + files_ignore_separator: ',' + - name: Print changed files count + run: > + echo "Changed files count: ${{ + steps.changed-files.outputs.all_changed_and_modified_files_count }}" + + build: + needs: check-changes + runs-on: docker + container: + image: ghcr.io/catthehacker/ubuntu:js-22.04 + if: ${{ needs.check-changes.outputs.all_changed_and_modified_files_count >= 1 }} + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - uses: actions/setup-node@v4 + with: + token: ${{ secrets.GH_TOKEN }} + node-version: lts/* + cache: yarn + cache-dependency-path: '**/yarn.lock' + - uses: actions/cache@v4 + with: + path: | + ~/.cache/pip + ~/node_modules + ~/.cache/ms-playwright + key: ${{ runner.os }}-pio-playwright-${{ hashFiles('**/yarn.lock') }} + - name: Get current date + id: dateAndTime + run: echo "dateAndTime=$(date +'%Y-%m-%d-%H:%M')" >> $GITHUB_OUTPUT + - name: Install mklittlefs + run: > + git clone https://github.com/earlephilhower/mklittlefs.git /tmp/mklittlefs && + cd /tmp/mklittlefs && + git submodule update --init && + make dist + - name: Install yarn + run: pnpm + - name: Run linter + run: pnpm lint + - name: Run vitest tests + run: pnpm vitest run + - name: Install Playwright Browsers + if: steps.cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - name: Build WebUI + run: yarn build + + # The following steps only run on push to main + - name: Get current block + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + id: getBlockHeight + run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT + + - name: Write block height to file + env: + BLOCK_HEIGHT: ${{ steps.getBlockHeight.outputs.blockHeight }} + 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 && echo "$GITHUB_SHA" > output/commit.txt + - name: Check GZipped directory size + run: | + # Set the threshold size in bytes + THRESHOLD=410000 + + # Calculate the total size of files in the directory + DIRECTORY_SIZE=$(du -b -s build_gz | awk '{print $1}') + + # Fail the workflow if the size exceeds the threshold + if [ "$DIRECTORY_SIZE" -gt "$THRESHOLD" ]; then + echo "Directory size exceeds the threshold of $THRESHOLD bytes" + exit 1 + else + echo "Directory size is within the threshold $DIRECTORY_SIZE" + fi + - name: Create tarball + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: tar czf webui.tgz --strip-components=1 dist + - name: Build LittleFS + run: | + set -e + /tmp/mklittlefs/mklittlefs -c build_gz -s 410000 output/littlefs.bin + - name: Upload artifacts + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + path: | + webui.tgz + output/littlefs.bin + - name: Create release + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: https://code.forgejo.org/actions/forgejo-release@v2.6.0 + with: + url: 'https://git.btclock.dev/' + repo: '${{ github.repository }}' + direction: upload + tag: ${{ steps.getBlockHeight.outputs.blockHeight }} + sha: '${{ github.sha }}' + release-dir: output + token: ${{ secrets.TOKEN }} + override: false + verbose: false + release-notes-assistant: false diff --git a/e2e/demo.test.ts b/e2e/demo.test.ts index 9985ce1..454ad0c 100644 --- a/e2e/demo.test.ts +++ b/e2e/demo.test.ts @@ -1,6 +1,7 @@ import { expect, test } from '@playwright/test'; -test('home page has expected h1', async ({ page }) => { +test('index page has expected columns control, status, settings', async ({ page }) => { await page.goto('/'); - await expect(page.locator('h1')).toBeVisible(); + await expect(page.getByRole('heading', { name: 'Control' })).toBeVisible(); + await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible(); }); diff --git a/eslint.config.js b/eslint.config.js index ef07d32..4fa0657 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -20,7 +20,9 @@ export default ts.config( languageOptions: { globals: { ...globals.browser, ...globals.node } }, - rules: { 'no-undef': 'off' } + rules: { + 'no-undef': 'off' + } }, { files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], @@ -32,5 +34,10 @@ export default ts.config( svelteConfig } } + }, + { + rules: { + 'svelte/no-at-html-tags': 'off' + } } ); diff --git a/messages/de-DE.json b/messages/de-DE.json index 7981c8c..32a8ab8 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1,152 +1,152 @@ { - "$schema": "https://inlang.com/schema/inlang-message-format", - "section": { - "settings": { - "title": "Einstellungen", - "textColor": "Textfarbe", - "backgroundColor": "Hintergrundfarbe", - "ledPowerOnTest": "LED-Einschalttest", - "ledFlashOnBlock": "LED blinkt bei neuem Block", - "timePerScreen": "Zeit pro Bildschirm", - "ledBrightness": "LED-Helligkeit", - "flMaxBrightness": "Displaybeleuchtung Helligkeit", - "timezoneOffset": "Zeitzonenoffset", - "timeBetweenPriceUpdates": "Zeit zwischen Preisaktualisierungen", - "fullRefreshEvery": "Vollständige Aktualisierung alle", - "mempoolnstance": "Mempool Instance", - "hostnamePrefix": "Hostnamen-Präfix", - "StealFocusOnNewBlock": "Steal focus on new block", - "useBigCharsMcap": "Verwende große Zeichen für die Marktkapitalisierung", - "useBlkCountdown": "Blocks Countdown zur Halbierung", - "useSatsSymbol": "Sats-Symbol verwenden", - "suffixPrice": "Suffix-Preisformat", - "disableLeds": "Alle LED-Effekte deaktivieren", - "otaUpdates": "OTA updates", - "enableMdns": "mDNS", - "fetchEuroPrice": "€-Preis abrufen", - "shortAmountsWarning": "Geringe Beträge können die Lebensdauer der Displays verkürzen", - "tzOffsetHelpText": "Ein Neustart ist erforderlich, um den TZ-Offset anzuwenden.", - "screens": "Bildschirme", - "wifiTxPowerText": "In den meisten Fällen muss dies nicht eingestellt werden.", - "wifiTxPower": "WiFi-TX-Leistung", - "settingsSaved": "Einstellungen gespeichert", - "errorSavingSettings": "Fehler beim Speichern der Einstellungen", - "ownDataSource": "BTClock-Datenquelle", - "flAlwaysOn": "Displaybeleuchtung immer an", - "flEffectDelay": "Displaybeleuchtungeffekt Geschwindigkeit", - "flFlashOnUpd": "Displaybeleuchting bei neuem Block", - "mempoolInstanceHelpText": "Nur wirksam, wenn die BTClock-Datenquelle deaktiviert ist. \nZur Anwendung ist ein Neustart erforderlich.", - "luxLightToggle": "Automatisches Umschalten des Frontlichts bei Lux", - "wpTimeout": "WiFi-Konfigurationsportal timeout", - "useNostr": "Nostr-Datenquelle verwenden", - "flDisable": "Displaybeleuchtung deaktivieren", - "httpAuthUser": "WebUI-Benutzername", - "httpAuthPass": "WebUI-Passwort", - "httpAuthText": "Schützt nur die WebUI mit einem Passwort, nicht API-Aufrufe.", - "currencies": "Währungen", - "mowMode": "Mow suffixmodus", - "suffixShareDot": "Kompakte Suffix-Notation", - "section": { - "displaysAndLed": "Anzeigen und LEDs", - "screenSettings": "Infospezifisch", - "dataSource": "Datenquelle", - "extraFeatures": "Zusätzliche Funktionen", - "system": "System" - }, - "ledFlashOnZap": "LED blinkt bei Nostr Zap", - "flFlashOnZap": "Displaybeleuchting bei Nostr Zap", - "showAll": "Alle anzeigen", - "hideAll": "Alles ausblenden", - "flOffWhenDark": "Displaybeleuchtung aus, wenn es dunkel ist", - "luxLightToggleText": "Zum Deaktivieren auf 0 setzen", - "verticalDesc": "Vrtikale Bildschirmbeschreibung", - "enableDebugLog": "Debug-Protokoll aktivieren", - "bitaxeEnabled": "BitAxe-Integration aktivieren", - "miningPoolStats": "Mining-Pool-Statistiken Integration Aktivieren", - "nostrZapNotify": "Nostr Zap-Benachrichtigungen aktivieren", - "thirdPartySource": "mempool.space/coincap.io Verwenden", - "dataSource": { - "nostr": "Nostr-Verlag", - "custom": "Benutzerdefinierter dataquelle" - }, - "fontName": "Schriftart", - "timeBasedDnd": "Aktivieren Sie den Zeitplan „Bitte nicht stören“.", - "dndStartHour": "Startstunde", - "dndStartMinute": "Startminute", - "dndEndHour": "Endstunde", - "dndEndMinute": "Schlussminute" - }, - "control": { - "systemInfo": "Systeminfo", - "version": "Version", - "buildTime": "Build time", - "ledColor": "LED-Farbe", - "turnOff": "Ausschalten", - "setColor": "Farbe festlegen", - "showText": "Text anzeigen", - "text": "Text", - "title": "Kontrolle", - "hostname": "Hostname", - "frontlight": "Displaybeleuchtung", - "turnOn": "Einschalten", - "flashFrontlight": "Blinken" - }, - "status": { - "title": "Status", - "screenCycle": "Bildschirmzyklus", - "memoryFree": "Speicher frei", - "wsPriceConnection": "WS-Preisverbindung", - "wsMempoolConnection": "WS {instance}-Verbindung", - "fetchEuroNote": "If you use \"Fetch € price\" the WS Price connection will show ❌ since it uses another data source.", - "uptime": "Betriebszeit", - "wifiSignalStrength": "WiFi-Signalstärke", - "wsDataConnection": "BTClock-Datenquelle verbindung", - "lightSensor": "Lichtsensor", - "nostrConnection": "Nostr Relay-Verbindung", - "doNotDisturb": "Bitte nicht stören", - "timeBasedDnd": "Zeitbasierter Zeitplan" - }, - "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.", - "swUpToDate": "Du hast die neueste Version.", - "swUpdateAvailable": "Eine neuere Version ist verfügbar!", - "latestVersion": "Letzte Version", - "releaseDate": "Veröffentlichungsdatum", - "viewRelease": "Veröffentlichung anzeigen", - "autoUpdate": "Update installieren (experimentell)", - "autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..." - } - }, - "colors": { - "black": "Schwarz", - "white": "Weiss" - }, - "time": { - "minutes": "Minuten", - "seconds": "Sekunden" - }, - "restartRequired": "Neustart erforderlich", - "button": { - "save": "Speichern", - "reset": "Zurücksetzen", - "restart": "Neustart", - "forceFullRefresh": "Vollständige Aktualisierung erzwingen" - }, - "timer": { - "running": "läuft", - "stopped": "gestoppt" - }, - "sections": { - "control": { - "keepSameColor": "Gleiche Farbe beibehalten" - } - }, - "rssiBar": { - "tooltip": "Werte > -67 dBm gelten als gut. > -30 dBm ist erstaunlich" - }, - "warning": "Achtung", - "auto-detect": "Automatische Erkennung" -} \ No newline at end of file + "$schema": "https://inlang.com/schema/inlang-message-format", + "section": { + "settings": { + "title": "Einstellungen", + "textColor": "Textfarbe", + "backgroundColor": "Hintergrundfarbe", + "ledPowerOnTest": "LED-Einschalttest", + "ledFlashOnBlock": "LED blinkt bei neuem Block", + "timePerScreen": "Zeit pro Bildschirm", + "ledBrightness": "LED-Helligkeit", + "flMaxBrightness": "Displaybeleuchtung Helligkeit", + "timezoneOffset": "Zeitzonenoffset", + "timeBetweenPriceUpdates": "Zeit zwischen Preisaktualisierungen", + "fullRefreshEvery": "Vollständige Aktualisierung alle", + "mempoolnstance": "Mempool Instance", + "hostnamePrefix": "Hostnamen-Präfix", + "StealFocusOnNewBlock": "Steal focus on new block", + "useBigCharsMcap": "Verwende große Zeichen für die Marktkapitalisierung", + "useBlkCountdown": "Blocks Countdown zur Halbierung", + "useSatsSymbol": "Sats-Symbol verwenden", + "suffixPrice": "Suffix-Preisformat", + "disableLeds": "Alle LED-Effekte deaktivieren", + "otaUpdates": "OTA updates", + "enableMdns": "mDNS", + "fetchEuroPrice": "€-Preis abrufen", + "shortAmountsWarning": "Geringe Beträge können die Lebensdauer der Displays verkürzen", + "tzOffsetHelpText": "Ein Neustart ist erforderlich, um den TZ-Offset anzuwenden.", + "screens": "Bildschirme", + "wifiTxPowerText": "In den meisten Fällen muss dies nicht eingestellt werden.", + "wifiTxPower": "WiFi-TX-Leistung", + "settingsSaved": "Einstellungen gespeichert", + "errorSavingSettings": "Fehler beim Speichern der Einstellungen", + "ownDataSource": "BTClock-Datenquelle", + "flAlwaysOn": "Displaybeleuchtung immer an", + "flEffectDelay": "Displaybeleuchtungeffekt Geschwindigkeit", + "flFlashOnUpd": "Displaybeleuchting bei neuem Block", + "mempoolInstanceHelpText": "Nur wirksam, wenn die BTClock-Datenquelle deaktiviert ist. \nZur Anwendung ist ein Neustart erforderlich.", + "luxLightToggle": "Automatisches Umschalten des Frontlichts bei Lux", + "wpTimeout": "WiFi-Konfigurationsportal timeout", + "useNostr": "Nostr-Datenquelle verwenden", + "flDisable": "Displaybeleuchtung deaktivieren", + "httpAuthUser": "WebUI-Benutzername", + "httpAuthPass": "WebUI-Passwort", + "httpAuthText": "Schützt nur die WebUI mit einem Passwort, nicht API-Aufrufe.", + "currencies": "Währungen", + "mowMode": "Mow suffixmodus", + "suffixShareDot": "Kompakte Suffix-Notation", + "section": { + "displaysAndLed": "Anzeigen und LEDs", + "screenSettings": "Infospezifisch", + "dataSource": "Datenquelle", + "extraFeatures": "Zusätzliche Funktionen", + "system": "System" + }, + "ledFlashOnZap": "LED blinkt bei Nostr Zap", + "flFlashOnZap": "Displaybeleuchting bei Nostr Zap", + "showAll": "Alle anzeigen", + "hideAll": "Alles ausblenden", + "flOffWhenDark": "Displaybeleuchtung aus, wenn es dunkel ist", + "luxLightToggleText": "Zum Deaktivieren auf 0 setzen", + "verticalDesc": "Vrtikale Bildschirmbeschreibung", + "enableDebugLog": "Debug-Protokoll aktivieren", + "bitaxeEnabled": "BitAxe-Integration aktivieren", + "miningPoolStats": "Mining-Pool-Statistiken Integration Aktivieren", + "nostrZapNotify": "Nostr Zap-Benachrichtigungen aktivieren", + "thirdPartySource": "mempool.space/coincap.io Verwenden", + "dataSource": { + "nostr": "Nostr-Verlag", + "custom": "Benutzerdefinierter dataquelle" + }, + "fontName": "Schriftart", + "timeBasedDnd": "Aktivieren Sie den Zeitplan „Bitte nicht stören“.", + "dndStartHour": "Startstunde", + "dndStartMinute": "Startminute", + "dndEndHour": "Endstunde", + "dndEndMinute": "Schlussminute" + }, + "control": { + "systemInfo": "Systeminfo", + "version": "Version", + "buildTime": "Build time", + "ledColor": "LED-Farbe", + "turnOff": "Ausschalten", + "setColor": "Farbe festlegen", + "showText": "Text anzeigen", + "text": "Text", + "title": "Kontrolle", + "hostname": "Hostname", + "frontlight": "Displaybeleuchtung", + "turnOn": "Einschalten", + "flashFrontlight": "Blinken" + }, + "status": { + "title": "Status", + "screenCycle": "Bildschirmzyklus", + "memoryFree": "Speicher frei", + "wsPriceConnection": "WS-Preisverbindung", + "wsMempoolConnection": "WS {instance}-Verbindung", + "fetchEuroNote": "If you use \"Fetch € price\" the WS Price connection will show ❌ since it uses another data source.", + "uptime": "Betriebszeit", + "wifiSignalStrength": "WiFi-Signalstärke", + "wsDataConnection": "BTClock-Datenquelle verbindung", + "lightSensor": "Lichtsensor", + "nostrConnection": "Nostr Relay-Verbindung", + "doNotDisturb": "Bitte nicht stören", + "timeBasedDnd": "Zeitbasierter Zeitplan" + }, + "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.", + "swUpToDate": "Du hast die neueste Version.", + "swUpdateAvailable": "Eine neuere Version ist verfügbar!", + "latestVersion": "Letzte Version", + "releaseDate": "Veröffentlichungsdatum", + "viewRelease": "Veröffentlichung anzeigen", + "autoUpdate": "Update installieren (experimentell)", + "autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..." + } + }, + "colors": { + "black": "Schwarz", + "white": "Weiss" + }, + "time": { + "minutes": "Minuten", + "seconds": "Sekunden" + }, + "restartRequired": "Neustart erforderlich", + "button": { + "save": "Speichern", + "reset": "Zurücksetzen", + "restart": "Neustart", + "forceFullRefresh": "Vollständige Aktualisierung erzwingen" + }, + "timer": { + "running": "läuft", + "stopped": "gestoppt" + }, + "sections": { + "control": { + "keepSameColor": "Gleiche Farbe beibehalten" + } + }, + "rssiBar": { + "tooltip": "Werte > -67 dBm gelten als gut. > -30 dBm ist erstaunlich" + }, + "warning": "Achtung", + "auto-detect": "Automatische Erkennung" +} diff --git a/messages/en-US.json b/messages/en-US.json index 201efb7..41f093d 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -171,4 +171,4 @@ "auto-detect": "Auto-detect", "on": "on", "off": "off" -} \ No newline at end of file +} diff --git a/messages/es-ES.json b/messages/es-ES.json index 59b89f8..2937974 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1,152 +1,152 @@ { - "$schema": "https://inlang.com/schema/inlang-message-format", - "hello_world": "Hello, {name} from es!", - "section": { - "settings": { - "title": "Configuración", - "textColor": "Color de texto", - "backgroundColor": "Color de fondo", - "ledBrightness": "Brillo LED", - "screens": "Pantallas", - "shortAmountsWarning": "Pequeñas cantidades pueden acortar la vida útil de los displays", - "fullRefreshEvery": "Actualización completa cada", - "timePerScreen": "Tiempo por pantalla", - "tzOffsetHelpText": "Es necesario reiniciar para aplicar la compensación.", - "timezoneOffset": "Compensación de zona horaria", - "StealFocusOnNewBlock": "Presta atención al nuevo bloque", - "ledFlashOnBlock": "El LED parpadea con un bloque nuevo", - "useBigCharsMcap": "Utilice caracteres grandes para la market cap", - "useBlkCountdown": "Cuenta regresiva en bloques", - "useSatsSymbol": "Usar símbolo sats", - "fetchEuroPrice": "Obtener precio en €", - "timeBetweenPriceUpdates": "Tiempo entre actualizaciones de precios", - "ledPowerOnTest": "Prueba de encendido del LED", - "enableMdns": "mDNS", - "hostnamePrefix": "Prefijo de nombre de host", - "mempoolnstance": "Instancia de Mempool", - "suffixPrice": "Precio con sufijos", - "disableLeds": "Desactivar efectos de LED", - "otaUpdates": "Actualización por aire", - "wifiTxPowerText": "En la mayoría de los casos no es necesario configurar esto.", - "settingsSaved": "Configuración guardada", - "errorSavingSettings": "Error al guardar la configuración", - "ownDataSource": "fuente de datos BTClock", - "flMaxBrightness": "Brillo de luz de la pantalla", - "flAlwaysOn": "Luz de la pantalla siempre encendida", - "flEffectDelay": "Velocidad del efecto de luz de la pantalla", - "flFlashOnUpd": "Luz de la pantalla parpadea con un nuevo bloque", - "mempoolInstanceHelpText": "Solo es efectivo cuando la fuente de datos BTClock está deshabilitada. \nEs necesario reiniciar para aplicar.", - "luxLightToggle": "Cambio automático de luz frontal en lux", - "wpTimeout": "Portal de configuración WiFi timeout", - "useNostr": "Utilice la fuente de datos Nostr", - "flDisable": "Desactivar luz de la pantalla", - "httpAuthUser": "Nombre de usuario WebUI", - "httpAuthPass": "Contraseña WebUI", - "httpAuthText": "Solo la WebUI está protegida con contraseña, no las llamadas API.", - "currencies": "Monedas", - "mowMode": "Modo de sufijo Mow", - "suffixShareDot": "Notación compacta de sufijo", - "section": { - "displaysAndLed": "Pantallas y LED", - "screenSettings": "Específico de la pantalla", - "dataSource": "fuente de datos", - "extraFeatures": "Funciones adicionales", - "system": "Sistema" - }, - "ledFlashOnZap": "LED parpadeante con Nostr Zap", - "flFlashOnZap": "Flash de luz frontal con Nostr Zap", - "showAll": "Mostrar todo", - "hideAll": "Ocultar todo", - "flOffWhenDark": "Luz de la pantalla cuando está oscuro", - "luxLightToggleText": "Establecer en 0 para desactivar", - "verticalDesc": "Descripción de pantalla vertical", - "enableDebugLog": "Habilitar registro de depuración", - "bitaxeEnabled": "Habilitar la integración de BitAxe", - "miningPoolStats": "Habilitar la integración de estadísticas del grupo minero", - "nostrZapNotify": "Habilitar notificaciones de Nostr Zap", - "thirdPartySource": "Utilice mempool.space/coincap.io", - "dataSource": { - "nostr": "editorial nostr", - "custom": "Punto final personalizado" - }, - "fontName": "Fuente", - "timeBasedDnd": "Habilitar el horario de No molestar", - "dndStartHour": "Hora de inicio", - "dndStartMinute": "Minuto de inicio", - "dndEndHour": "Hora final", - "dndEndMinute": "Minuto final" - }, - "control": { - "turnOff": "Apagar", - "setColor": "Establecer el color", - "version": "Versión", - "ledColor": "color del LED", - "systemInfo": "Info del sistema", - "showText": "Mostrar texto", - "text": "Texto", - "title": "Control", - "buildTime": "Tiempo de compilación", - "hostname": "Nombre del host", - "turnOn": "Encender", - "frontlight": "Luz de la pantalla", - "flashFrontlight": "Luz intermitente" - }, - "status": { - "memoryFree": "Memoria RAM libre", - "wsPriceConnection": "Conexión WebSocket Precio", - "wsMempoolConnection": "Conexión WebSocket {instance}", - "screenCycle": "Ciclo de pantalla", - "uptime": "Tiempo de funcionamiento", - "fetchEuroNote": "Si utiliza \"Obtener precio en €\", la conexión de Precio WS mostrará ❌ ya que utiliza otra fuente de datos.", - "title": "Estado", - "wifiSignalStrength": "Fuerza de la señal WiFi", - "wsDataConnection": "Conexión de fuente de datos BTClock", - "lightSensor": "Sensor de luz", - "nostrConnection": "Conexión de relé Nostr", - "doNotDisturb": "No molestar", - "timeBasedDnd": "Horario basado en el tiempo" - }, - "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.", - "swUpToDate": "Tienes la ultima version.", - "swUpdateAvailable": "¡Una nueva versión está disponible!", - "latestVersion": "Ultima versión", - "releaseDate": "Fecha de lanzamiento", - "viewRelease": "Ver lanzamiento", - "autoUpdate": "Instalar actualización (experimental)", - "autoUpdateInProgress": "Actualización automática en progreso, espere..." - } - }, - "button": { - "save": "Guardar", - "reset": "Restaurar", - "restart": "Reiniciar", - "forceFullRefresh": "Forzar refresco" - }, - "colors": { - "black": "Negro", - "white": "Blanco" - }, - "restartRequired": "reinicio requerido", - "time": { - "minutes": "minutos", - "seconds": "segundos" - }, - "timer": { - "running": "funcionando", - "stopped": "detenido" - }, - "sections": { - "control": { - "keepSameColor": "Mantén el mismo color" - } - }, - "rssiBar": { - "tooltip": "Se consideran buenos valores > -67 dBm. > -30 dBm es increíble" - }, - "warning": "Aviso", - "auto-detect": "Detección automática" -} \ No newline at end of file + "$schema": "https://inlang.com/schema/inlang-message-format", + "hello_world": "Hello, {name} from es!", + "section": { + "settings": { + "title": "Configuración", + "textColor": "Color de texto", + "backgroundColor": "Color de fondo", + "ledBrightness": "Brillo LED", + "screens": "Pantallas", + "shortAmountsWarning": "Pequeñas cantidades pueden acortar la vida útil de los displays", + "fullRefreshEvery": "Actualización completa cada", + "timePerScreen": "Tiempo por pantalla", + "tzOffsetHelpText": "Es necesario reiniciar para aplicar la compensación.", + "timezoneOffset": "Compensación de zona horaria", + "StealFocusOnNewBlock": "Presta atención al nuevo bloque", + "ledFlashOnBlock": "El LED parpadea con un bloque nuevo", + "useBigCharsMcap": "Utilice caracteres grandes para la market cap", + "useBlkCountdown": "Cuenta regresiva en bloques", + "useSatsSymbol": "Usar símbolo sats", + "fetchEuroPrice": "Obtener precio en €", + "timeBetweenPriceUpdates": "Tiempo entre actualizaciones de precios", + "ledPowerOnTest": "Prueba de encendido del LED", + "enableMdns": "mDNS", + "hostnamePrefix": "Prefijo de nombre de host", + "mempoolnstance": "Instancia de Mempool", + "suffixPrice": "Precio con sufijos", + "disableLeds": "Desactivar efectos de LED", + "otaUpdates": "Actualización por aire", + "wifiTxPowerText": "En la mayoría de los casos no es necesario configurar esto.", + "settingsSaved": "Configuración guardada", + "errorSavingSettings": "Error al guardar la configuración", + "ownDataSource": "fuente de datos BTClock", + "flMaxBrightness": "Brillo de luz de la pantalla", + "flAlwaysOn": "Luz de la pantalla siempre encendida", + "flEffectDelay": "Velocidad del efecto de luz de la pantalla", + "flFlashOnUpd": "Luz de la pantalla parpadea con un nuevo bloque", + "mempoolInstanceHelpText": "Solo es efectivo cuando la fuente de datos BTClock está deshabilitada. \nEs necesario reiniciar para aplicar.", + "luxLightToggle": "Cambio automático de luz frontal en lux", + "wpTimeout": "Portal de configuración WiFi timeout", + "useNostr": "Utilice la fuente de datos Nostr", + "flDisable": "Desactivar luz de la pantalla", + "httpAuthUser": "Nombre de usuario WebUI", + "httpAuthPass": "Contraseña WebUI", + "httpAuthText": "Solo la WebUI está protegida con contraseña, no las llamadas API.", + "currencies": "Monedas", + "mowMode": "Modo de sufijo Mow", + "suffixShareDot": "Notación compacta de sufijo", + "section": { + "displaysAndLed": "Pantallas y LED", + "screenSettings": "Específico de la pantalla", + "dataSource": "fuente de datos", + "extraFeatures": "Funciones adicionales", + "system": "Sistema" + }, + "ledFlashOnZap": "LED parpadeante con Nostr Zap", + "flFlashOnZap": "Flash de luz frontal con Nostr Zap", + "showAll": "Mostrar todo", + "hideAll": "Ocultar todo", + "flOffWhenDark": "Luz de la pantalla cuando está oscuro", + "luxLightToggleText": "Establecer en 0 para desactivar", + "verticalDesc": "Descripción de pantalla vertical", + "enableDebugLog": "Habilitar registro de depuración", + "bitaxeEnabled": "Habilitar la integración de BitAxe", + "miningPoolStats": "Habilitar la integración de estadísticas del grupo minero", + "nostrZapNotify": "Habilitar notificaciones de Nostr Zap", + "thirdPartySource": "Utilice mempool.space/coincap.io", + "dataSource": { + "nostr": "editorial nostr", + "custom": "Punto final personalizado" + }, + "fontName": "Fuente", + "timeBasedDnd": "Habilitar el horario de No molestar", + "dndStartHour": "Hora de inicio", + "dndStartMinute": "Minuto de inicio", + "dndEndHour": "Hora final", + "dndEndMinute": "Minuto final" + }, + "control": { + "turnOff": "Apagar", + "setColor": "Establecer el color", + "version": "Versión", + "ledColor": "color del LED", + "systemInfo": "Info del sistema", + "showText": "Mostrar texto", + "text": "Texto", + "title": "Control", + "buildTime": "Tiempo de compilación", + "hostname": "Nombre del host", + "turnOn": "Encender", + "frontlight": "Luz de la pantalla", + "flashFrontlight": "Luz intermitente" + }, + "status": { + "memoryFree": "Memoria RAM libre", + "wsPriceConnection": "Conexión WebSocket Precio", + "wsMempoolConnection": "Conexión WebSocket {instance}", + "screenCycle": "Ciclo de pantalla", + "uptime": "Tiempo de funcionamiento", + "fetchEuroNote": "Si utiliza \"Obtener precio en €\", la conexión de Precio WS mostrará ❌ ya que utiliza otra fuente de datos.", + "title": "Estado", + "wifiSignalStrength": "Fuerza de la señal WiFi", + "wsDataConnection": "Conexión de fuente de datos BTClock", + "lightSensor": "Sensor de luz", + "nostrConnection": "Conexión de relé Nostr", + "doNotDisturb": "No molestar", + "timeBasedDnd": "Horario basado en el tiempo" + }, + "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.", + "swUpToDate": "Tienes la ultima version.", + "swUpdateAvailable": "¡Una nueva versión está disponible!", + "latestVersion": "Ultima versión", + "releaseDate": "Fecha de lanzamiento", + "viewRelease": "Ver lanzamiento", + "autoUpdate": "Instalar actualización (experimental)", + "autoUpdateInProgress": "Actualización automática en progreso, espere..." + } + }, + "button": { + "save": "Guardar", + "reset": "Restaurar", + "restart": "Reiniciar", + "forceFullRefresh": "Forzar refresco" + }, + "colors": { + "black": "Negro", + "white": "Blanco" + }, + "restartRequired": "reinicio requerido", + "time": { + "minutes": "minutos", + "seconds": "segundos" + }, + "timer": { + "running": "funcionando", + "stopped": "detenido" + }, + "sections": { + "control": { + "keepSameColor": "Mantén el mismo color" + } + }, + "rssiBar": { + "tooltip": "Se consideran buenos valores > -67 dBm. > -30 dBm es increíble" + }, + "warning": "Aviso", + "auto-detect": "Detección automática" +} diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 82b4c7f..ebad607 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -139,4 +139,4 @@ }, "warning": "Waarschuwing", "auto-detect": "Automatische detectie" -} \ No newline at end of file +} diff --git a/src/app.css b/src/app.css index b6cb659..64d0745 100644 --- a/src/app.css +++ b/src/app.css @@ -1,26 +1,26 @@ @import 'tailwindcss'; @plugin "daisyui" { - } :root { - --primary: #3b82f6; - --secondary: #6b7280; - --accent: #f59e0b; + --primary: #3b82f6; + --secondary: #6b7280; + --accent: #f59e0b; } html { - scroll-behavior: smooth; + scroll-behavior: smooth; } -html, body { - @apply h-full; +html, +body { + @apply h-full; } html { - @apply bg-base-200; + @apply bg-base-200; } body { - @apply bg-base-200 pt-16; -} \ No newline at end of file + @apply bg-base-200 pt-16; +} diff --git a/src/app.html b/src/app.html index 9175965..50bd0b5 100644 --- a/src/app.html +++ b/src/app.html @@ -2,11 +2,10 @@ - %sveltekit.head% -
%sveltekit.body%
+
%sveltekit.body%
diff --git a/src/lib/clockControl.ts b/src/lib/clockControl.ts index eb940c2..29a965e 100644 --- a/src/lib/clockControl.ts +++ b/src/lib/clockControl.ts @@ -1,112 +1,111 @@ -import { PUBLIC_BASE_URL } from '$env/static/public'; import { baseUrl } from './env'; /** * Sets custom text to display on the clock */ export const setCustomText = (newText: string) => { - return fetch(`${baseUrl}/api/show/text/${newText}`).catch(() => {}); + return fetch(`${baseUrl}/api/show/text/${newText}`).catch(() => {}); }; /** * Updates the LED colors */ export const setLEDcolor = (ledStatus: { hex: string }[]) => { - return fetch(`${baseUrl}/api/lights/set`, { - headers: { - 'Content-Type': 'application/json' - }, - method: 'PATCH', - body: JSON.stringify(ledStatus) - }).catch(() => {}); + return fetch(`${baseUrl}/api/lights/set`, { + headers: { + 'Content-Type': 'application/json' + }, + method: 'PATCH', + body: JSON.stringify(ledStatus) + }).catch(() => {}); }; /** * Turns off all LEDs */ export const turnOffLeds = () => { - return fetch(`${baseUrl}/api/lights/off`).catch(() => {}); + return fetch(`${baseUrl}/api/lights/off`).catch(() => {}); }; /** * Restarts the clock */ export const restartClock = () => { - return fetch(`${baseUrl}/api/restart`).catch(() => {}); + return fetch(`${baseUrl}/api/restart`).catch(() => {}); }; /** * Forces a full refresh of the clock */ export const forceFullRefresh = () => { - return fetch(`${baseUrl}/api/full_refresh`).catch(() => {}); + return fetch(`${baseUrl}/api/full_refresh`).catch(() => {}); }; /** * Generates a random color hex code */ export const generateRandomColor = () => { - return `#${Math.floor(Math.random() * 16777215) - .toString(16) - .padStart(6, '0')}`; -}; + return `#${Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, '0')}`; +}; /** * Sets the active screen */ export const setActiveScreen = async (screenId: string) => { - return fetch(`${baseUrl}/api/show/screen/${screenId}`); -} + return fetch(`${baseUrl}/api/show/screen/${screenId}`); +}; /** * Sets the active currency */ export const setActiveCurrency = async (currency: string) => { - return fetch(`${baseUrl}/api/show/currency/${currency}`); -} + return fetch(`${baseUrl}/api/show/currency/${currency}`); +}; /** * Turns on the frontlight */ export const turnOnFrontlight = () => { - return fetch(`${baseUrl}/api/frontlight/on`).catch(() => {}); + return fetch(`${baseUrl}/api/frontlight/on`).catch(() => {}); }; /** * Flashes the frontlight */ export const flashFrontlight = () => { - return fetch(`${baseUrl}/api/frontlight/flash`).catch(() => {}); + return fetch(`${baseUrl}/api/frontlight/flash`).catch(() => {}); }; /** * Turns off the frontlight */ export const turnOffFrontlight = () => { - return fetch(`${baseUrl}/api/frontlight/off`).catch(() => {}); + return fetch(`${baseUrl}/api/frontlight/off`).catch(() => {}); }; /** * Toggles the timer - */ + */ export const toggleTimer = (currentStatus: boolean) => (e: Event) => { - e.preventDefault(); - if (currentStatus) { - fetch(`${baseUrl}/api/action/pause`); - } else { - fetch(`${baseUrl}/api/action/timer_restart`); - } + e.preventDefault(); + if (currentStatus) { + fetch(`${baseUrl}/api/action/pause`); + } else { + fetch(`${baseUrl}/api/action/timer_restart`); + } }; /** * Toggles the do not disturb mode */ export const toggleDoNotDisturb = (currentStatus: boolean) => (e: Event) => { - e.preventDefault(); - console.log(currentStatus); - if (!currentStatus) { - fetch(`${baseUrl}/api/dnd/enable`); - } else { - fetch(`${baseUrl}/api/dnd/disable`); - } -}; \ No newline at end of file + e.preventDefault(); + console.log(currentStatus); + if (!currentStatus) { + fetch(`${baseUrl}/api/dnd/enable`); + } else { + fetch(`${baseUrl}/api/dnd/disable`); + } +}; diff --git a/src/lib/components/BTClock.svelte b/src/lib/components/BTClock.svelte index 87a3fc4..2a25bb8 100644 --- a/src/lib/components/BTClock.svelte +++ b/src/lib/components/BTClock.svelte @@ -60,10 +60,6 @@ // $: if (containerWidth > 0) { // containerHeight = containerWidth * deviceRatio; // } - - const fontSizeSingle = '4.5rem'; - const fontSizeMedium = '2.0rem'; - const fontSizeSplit = '1.0rem';
- let { - currency, - active = false, - onClick, - ...restProps - } = $props(); + let { currency, active = false, onClick, ...restProps } = $props(); - \ No newline at end of file + {currency} + diff --git a/src/lib/components/form/InputField.svelte b/src/lib/components/form/InputField.svelte index dde5b1c..57dd300 100644 --- a/src/lib/components/form/InputField.svelte +++ b/src/lib/components/form/InputField.svelte @@ -1,26 +1,19 @@
- {#if label} - - {/if} - -
\ No newline at end of file + {#if label} + + {/if} + +
diff --git a/src/lib/components/form/Toggle.svelte b/src/lib/components/form/Toggle.svelte index 6c31c91..187377c 100644 --- a/src/lib/components/form/Toggle.svelte +++ b/src/lib/components/form/Toggle.svelte @@ -1,21 +1,10 @@ - \ No newline at end of file + diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index b8e130f..8f190e4 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -18,4 +18,4 @@ export { default as CollapsibleSection } from './layout/CollapsibleSection.svelt export { default as ControlSection } from './sections/ControlSection.svelte'; export { default as StatusSection } from './sections/StatusSection.svelte'; export { default as SettingsSection } from './sections/SettingsSection.svelte'; -export { default as SystemSection } from './sections/SystemSection.svelte'; \ No newline at end of file +export { default as SystemSection } from './sections/SystemSection.svelte'; diff --git a/src/lib/components/layout/CollapsibleSection.svelte b/src/lib/components/layout/CollapsibleSection.svelte index 3b1d98a..eb3cf8d 100644 --- a/src/lib/components/layout/CollapsibleSection.svelte +++ b/src/lib/components/layout/CollapsibleSection.svelte @@ -1,17 +1,13 @@ -
- -
- {title} -
-
- -
-
\ No newline at end of file +
+ +
+ {title} +
+
+ +
+
diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte index 15c0c94..2a16113 100644 --- a/src/lib/components/layout/Navbar.svelte +++ b/src/lib/components/layout/Navbar.svelte @@ -1,87 +1,105 @@ - \ No newline at end of file + diff --git a/src/lib/components/sections/ControlSection.svelte b/src/lib/components/sections/ControlSection.svelte index ae34fe5..5198395 100644 --- a/src/lib/components/sections/ControlSection.svelte +++ b/src/lib/components/sections/ControlSection.svelte @@ -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([ - {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 @@
{#if ledStatus.length > 0} - {#each ledStatus as led} + {#each ledStatus as led (led)} - {/each} + /> + {/each} {/if}
- + @@ -102,19 +104,24 @@
{#if $settings.hasFrontlight && !$settings.flDisable} -
-

{m['section.control.frontlight']()}

-
- - - +
+

{m['section.control.frontlight']()}

+
+ + + +
-
{/if}
-

{m['section.control.title']()}

-
+
diff --git a/src/lib/components/sections/SettingsSection.svelte b/src/lib/components/sections/SettingsSection.svelte index 2ad57b9..a923da1 100644 --- a/src/lib/components/sections/SettingsSection.svelte +++ b/src/lib/components/sections/SettingsSection.svelte @@ -1,341 +1,361 @@ - -
- - -
+ +
+ + +
-
+
+ +
+
+ +

+ When a new block is mined, it will switch focus from the current screen. +

+
- -
-
- -

When a new block is mined, it will switch focus from the current screen.

-
- -
- -

Use big characters for the market cap screen instead of using a suffix.

-
- -
- -

When enabled it count down blocks instead of years/monts/days/hours/minutes.

-
- -
- -

Prefix satoshi amounts with the sats symbol.

-
- -
- -

Always use a suffix for the ticker screen.

-
- -
- -

Rotate the description of the screen 90 degrees.

-
-
-
+
+ +

+ Use big characters for the market cap screen instead of using a suffix. +

+
- -
- {#each $settings.screens as screen} -
- -
- {/each} -
-
+
+ +

+ When enabled it count down blocks instead of years/monts/days/hours/minutes. +

+
- -
- - restart required -
+
+ +

Prefix satoshi amounts with the sats symbol.

+
-
- - {#each $settings.actCurrencies as currency} -
- -
- {/each} - -
-
+
+ +

Always use a suffix for the ticker screen.

+
- -
-
- - -
- -
- - -
- -
- -
- - {m["time.minutes"]()} -
-
- -
- -
- - {m["time.minutes"]()} -
-
- -
- -
- - {m["time.seconds"]()} -
-
- -
- - -
- -
- -
- -
- -
- -
- -
-
-
+
+ +

Rotate the description of the screen 90 degrees.

+
+
+
- {#if $settings.hasFrontlight} - -
-
- -
- -
- -
- -
- -
- -
- -
- - {#if $settings.hasLightLevel} -
- -
- -
- - -
- {/if} - -
- - -
- -
- - -
-
-
- {/if} + +
+ {#each $settings.screens as screen (screen.id)} +
+ +
+ {/each} +
+
- -
-
- - -
- -
- - -
- -
- - -
-
-
+ +
+ + restart required +
- -
-
- -
-
-
+
+ {#each $settings.actCurrencies as currency (currency)} +
+ +
+ {/each} +
+
- -
-
- -
- - -
-

{m["section.settings.tzOffsetHelpText"]()}

-
- -
- - -
- -
- -
- - {m["time.seconds"]()} -
-
-
-
+ +
+
+ + +
-
-
- - -
- \ No newline at end of file +
+ + +
+ +
+ +
+ + {m['time.minutes']()} +
+
+ +
+ +
+ + {m['time.minutes']()} +
+
+ +
+ +
+ + {m['time.seconds']()} +
+
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + + {#if $settings.hasFrontlight} + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + {#if $settings.hasLightLevel} +
+ +
+ +
+ + +
+ {/if} + +
+ + +
+ +
+ + +
+
+
+ {/if} + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+
+ +
+
+
+ + +
+
+ +
+ + +
+

{m['section.settings.tzOffsetHelpText']()}

+
+ +
+ + +
+ +
+ +
+ + {m['time.seconds']()} +
+
+
+
+
+
+ + +
+
diff --git a/src/lib/components/sections/StatusSection.svelte b/src/lib/components/sections/StatusSection.svelte index 5211c6f..2cdaef0 100644 --- a/src/lib/components/sections/StatusSection.svelte +++ b/src/lib/components/sections/StatusSection.svelte @@ -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 + })); -
+
- {#each screens as screen} - setActiveScreen(screen.id)}> + {#each screens as screen (screen.id)} + setActiveScreen(screen.id)} + > {screen.label} {/each}
- {#each $settings.actCurrencies as currency} - setActiveCurrency(currency)} - /> - {/each} + {#each $settings.actCurrencies as currency (currency)} + setActiveCurrency(currency)} + /> + {/each}
- - {$settings.verticalDesc}
{m['section.status.screenCycle']()}: is {$status.timerRunning ? 'running' : 'stopped'}
- {m['section.status.doNotDisturb']()}: {$status.dnd.enabled ? m['on']() : m['off']()} + {m['section.status.doNotDisturb']()}: {$status.dnd.enabled ? m['on']() : m['off']()} {#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}
@@ -58,18 +59,28 @@ {/if} {#if $settings.dataSource === DataSourceType.THIRD_PARTY_SOURCE} - - + + {:else} - + {/if}
- -
-
- - - -
+
+ + + +
diff --git a/src/lib/components/sections/SystemSection.svelte b/src/lib/components/sections/SystemSection.svelte index 24715f6..0e19a85 100644 --- a/src/lib/components/sections/SystemSection.svelte +++ b/src/lib/components/sections/SystemSection.svelte @@ -1,102 +1,99 @@ -
-
- - - - - - +
+
+
System info
+ + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - -
System info
{m['section.control.version']()}{$settings.gitTag}
{m['section.control.buildTime']()}{$settings.lastBuildTime}
IP{$settings.ip}
HW revision{$settings.hwRev}
{m['section.control.fwCommit']()}{$settings.gitRev}
{m['section.control.hostname']()}{$settings.hostname}
-
-
- - -
-
+ + + {m['section.control.version']()} + {$settings.gitTag} + + + {m['section.control.buildTime']()} + {$settings.lastBuildTime} + + + IP + {$settings.ip} + + + HW revision + {$settings.hwRev} + + + {m['section.control.fwCommit']()} + {$settings.gitRev} + + + {m['section.control.hostname']()} + {$settings.hostname} + + + +
+
+ + +
+
-
-

- Latest Version: 3.3.5 - Release Date: 5/2/2025, 12:37:14 AM - {m['section.firmwareUpdater.viewRelease']()} -

-

{m['section.firmwareUpdater.swUpToDate']()}

+
+

+ Latest Version: 3.3.5 - Release Date: 5/2/2025, 12:37:14 AM - {m['section.firmwareUpdater.viewRelease']()} +

+

{m['section.firmwareUpdater.swUpToDate']()}

-
- -
- - -
-
+
+ +
+ + +
+
-
- -
- - -
-
+
+ +
+ + +
+
-
- - {m['section.firmwareUpdater.firmwareUpdateText']()} -
-
- \ No newline at end of file +
+ + {m['section.firmwareUpdater.firmwareUpdateText']()} +
+
+
diff --git a/src/lib/components/ui/CardContainer.svelte b/src/lib/components/ui/CardContainer.svelte index 3b2021e..541b545 100644 --- a/src/lib/components/ui/CardContainer.svelte +++ b/src/lib/components/ui/CardContainer.svelte @@ -1,16 +1,12 @@ -
-
- {#if title} -

{title}

- {/if} - -
-
\ No newline at end of file +
+
+ {#if title} +

{title}

+ {/if} + +
+
diff --git a/src/lib/components/ui/Stat.svelte b/src/lib/components/ui/Stat.svelte index cca40b2..3cd52ce 100644 --- a/src/lib/components/ui/Stat.svelte +++ b/src/lib/components/ui/Stat.svelte @@ -1,23 +1,16 @@
- {#if icon} -
- {@html icon} -
- {/if} -
{title}
-
{value}
- {#if desc} -
{desc}
- {/if} -
\ No newline at end of file + {#if icon} +
+ {@html icon} +
+ {/if} +
{title}
+
{value}
+ {#if desc} +
{desc}
+ {/if} +
diff --git a/src/lib/components/ui/Status.svelte b/src/lib/components/ui/Status.svelte index 9662cd1..7107b0c 100644 --- a/src/lib/components/ui/Status.svelte +++ b/src/lib/components/ui/Status.svelte @@ -1,33 +1,35 @@
-
-
- {#if status === "online"} -
- {/if} -
- {text} -
\ No newline at end of file +
+
+ {#if status === 'online'} +
+ {/if} +
+ {text} +
diff --git a/src/lib/components/ui/TabButton.svelte b/src/lib/components/ui/TabButton.svelte index 357f74a..4dcba9b 100644 --- a/src/lib/components/ui/TabButton.svelte +++ b/src/lib/components/ui/TabButton.svelte @@ -1,15 +1,11 @@ - \ No newline at end of file + + diff --git a/src/lib/components/ui/Toast.svelte b/src/lib/components/ui/Toast.svelte index f8b507f..f66d9c4 100644 --- a/src/lib/components/ui/Toast.svelte +++ b/src/lib/components/ui/Toast.svelte @@ -1,71 +1,86 @@ {#if visible} -
-
- {message} - {#if showClose} - - {/if} -
-
-{/if} \ No newline at end of file +
+
+ {message} + {#if showClose} + + {/if} +
+
+{/if} diff --git a/src/lib/env.ts b/src/lib/env.ts index dae34b7..52eaa41 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -1,3 +1,3 @@ import { PUBLIC_BASE_URL } from '$env/static/public'; -export const baseUrl = PUBLIC_BASE_URL; \ No newline at end of file +export const baseUrl = PUBLIC_BASE_URL; diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts index 6c24162..339f521 100644 --- a/src/lib/stores/index.ts +++ b/src/lib/stores/index.ts @@ -1,2 +1,2 @@ export { settings, type Settings } from './settings'; -export { status, type Status } from './status'; \ No newline at end of file +export { status, type Status } from './status'; diff --git a/src/lib/stores/settings.ts b/src/lib/stores/settings.ts index 166d952..62486ee 100644 --- a/src/lib/stores/settings.ts +++ b/src/lib/stores/settings.ts @@ -4,158 +4,158 @@ import type { Settings } from '$lib/types'; // Create a default settings object const defaultSettings: Settings = { - numScreens: 7, - invertedColor: false, - timerSeconds: 60, - timerRunning: false, - minSecPriceUpd: 30, - fullRefreshMin: 60, - wpTimeout: 600, - tzString: "UTC", - dataSource: 0, - mempoolInstance: "mempool.space", - mempoolSecure: true, - localPoolEndpoint: "localhost:2019", - nostrPubKey: "", - nostrRelay: "wss://relay.damus.io", - nostrZapNotify: false, - nostrZapPubkey: "", - ledFlashOnZap: true, - fontName: "oswald", - availableFonts: ["antonio", "oswald"], - customEndpoint: "ws-staging.btclock.dev", - customEndpointDisableSSL: false, - ledTestOnPower: true, - ledFlashOnUpd: true, - ledBrightness: 255, - stealFocus: false, - mcapBigChar: true, - mdnsEnabled: true, - otaEnabled: true, - useSatsSymbol: true, - useBlkCountdown: true, - suffixPrice: false, - disableLeds: false, - mowMode: false, - verticalDesc: true, - suffixShareDot: false, - enableDebugLog: false, - hostnamePrefix: "btclock", - hostname: "btclock", - ip: "", - txPower: 80, - gitReleaseUrl: "https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/latest", - bitaxeEnabled: false, - bitaxeHostname: "bitaxe1", - miningPoolStats: false, - miningPoolName: "noderunners", - miningPoolUser: "", - availablePools: [ - "ocean", - "noderunners", - "satoshi_radio", - "braiins", - "public_pool", - "local_public_pool", - "gobrrr_pool", - "ckpool", - "eu_ckpool" - ], - httpAuthEnabled: false, - httpAuthUser: "btclock", - httpAuthPass: "satoshi", - hasFrontlight: false, - // Default frontlight settings - flDisable: false, - flMaxBrightness: 2684, - flAlwaysOn: false, - flEffectDelay: 50, - flFlashOnUpd: true, - flFlashOnZap: true, - // Default light sensor settings - hasLightLevel: false, - luxLightToggle: 128, - flOffWhenDark: false, - hwRev: "", - fsRev: "", - gitRev: "", - gitTag: "", - lastBuildTime: "", - screens: [ - {id: 0, name: "Block Height", enabled: true}, - {id: 3, name: "Time", enabled: false}, - {id: 4, name: "Halving countdown", enabled: false}, - {id: 6, name: "Block Fee Rate", enabled: false}, - {id: 10, name: "Sats per dollar", enabled: true}, - {id: 20, name: "Ticker", enabled: true}, - {id: 30, name: "Market Cap", enabled: false} - ], - actCurrencies: ["USD"], - availableCurrencies: ["USD", "EUR", "GBP", "JPY", "AUD", "CAD"], - poolLogosUrl: "https://git.btclock.dev/btclock/mining-pool-logos/raw/branch/main", - ceEndpoint: "ws-staging.btclock.dev", - ceDisableSSL: false, - dnd: { - enabled: false, - timeBasedEnabled: false, - startHour: 23, - startMinute: 0, - endHour: 7, - endMinute: 0 - } + numScreens: 7, + invertedColor: false, + timerSeconds: 60, + timerRunning: false, + minSecPriceUpd: 30, + fullRefreshMin: 60, + wpTimeout: 600, + tzString: 'UTC', + dataSource: 0, + mempoolInstance: 'mempool.space', + mempoolSecure: true, + localPoolEndpoint: 'localhost:2019', + nostrPubKey: '', + nostrRelay: 'wss://relay.damus.io', + nostrZapNotify: false, + nostrZapPubkey: '', + ledFlashOnZap: true, + fontName: 'oswald', + availableFonts: ['antonio', 'oswald'], + customEndpoint: 'ws-staging.btclock.dev', + customEndpointDisableSSL: false, + ledTestOnPower: true, + ledFlashOnUpd: true, + ledBrightness: 255, + stealFocus: false, + mcapBigChar: true, + mdnsEnabled: true, + otaEnabled: true, + useSatsSymbol: true, + useBlkCountdown: true, + suffixPrice: false, + disableLeds: false, + mowMode: false, + verticalDesc: true, + suffixShareDot: false, + enableDebugLog: false, + hostnamePrefix: 'btclock', + hostname: 'btclock', + ip: '', + txPower: 80, + gitReleaseUrl: 'https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/latest', + bitaxeEnabled: false, + bitaxeHostname: 'bitaxe1', + miningPoolStats: false, + miningPoolName: 'noderunners', + miningPoolUser: '', + availablePools: [ + 'ocean', + 'noderunners', + 'satoshi_radio', + 'braiins', + 'public_pool', + 'local_public_pool', + 'gobrrr_pool', + 'ckpool', + 'eu_ckpool' + ], + httpAuthEnabled: false, + httpAuthUser: 'btclock', + httpAuthPass: 'satoshi', + hasFrontlight: false, + // Default frontlight settings + flDisable: false, + flMaxBrightness: 2684, + flAlwaysOn: false, + flEffectDelay: 50, + flFlashOnUpd: true, + flFlashOnZap: true, + // Default light sensor settings + hasLightLevel: false, + luxLightToggle: 128, + flOffWhenDark: false, + hwRev: '', + fsRev: '', + gitRev: '', + gitTag: '', + lastBuildTime: '', + screens: [ + { id: 0, name: 'Block Height', enabled: true }, + { id: 3, name: 'Time', enabled: false }, + { id: 4, name: 'Halving countdown', enabled: false }, + { id: 6, name: 'Block Fee Rate', enabled: false }, + { id: 10, name: 'Sats per dollar', enabled: true }, + { id: 20, name: 'Ticker', enabled: true }, + { id: 30, name: 'Market Cap', enabled: false } + ], + actCurrencies: ['USD'], + availableCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD'], + poolLogosUrl: 'https://git.btclock.dev/btclock/mining-pool-logos/raw/branch/main', + ceEndpoint: 'ws-staging.btclock.dev', + ceDisableSSL: false, + dnd: { + enabled: false, + timeBasedEnabled: false, + startHour: 23, + startMinute: 0, + endHour: 7, + endMinute: 0 + } }; // Create the Svelte store function createSettingsStore() { - const { subscribe, set, update } = writable(defaultSettings); + const { subscribe, set, update } = writable(defaultSettings); - return { - subscribe, - fetch: async () => { - try { - const response = await fetch(`${baseUrl}/api/settings`); - if (!response.ok) { - throw new Error(`Error fetching settings: ${response.statusText}`); - } - const data = await response.json(); - set(data); - return data; - } catch (error) { - console.error('Failed to fetch settings:', error); - return defaultSettings; - } - }, - update: async (newSettings: Partial) => { - try { - const response = await fetch(`${baseUrl}/api/settings`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(newSettings), - }); - - if (!response.ok) { - throw new Error(`Error updating settings: ${response.statusText}`); - } - - // Update the local store with the new settings - update(currentSettings => ({ ...currentSettings, ...newSettings })); - - return true; - } catch (error) { - console.error('Failed to update settings:', error); - return false; - } - }, - set: (newSettings: Settings) => set(newSettings), - reset: () => set(defaultSettings) - }; + return { + subscribe, + fetch: async () => { + try { + const response = await fetch(`${baseUrl}/api/settings`); + if (!response.ok) { + throw new Error(`Error fetching settings: ${response.statusText}`); + } + const data = await response.json(); + set(data); + return data; + } catch (error) { + console.error('Failed to fetch settings:', error); + return defaultSettings; + } + }, + update: async (newSettings: Partial) => { + try { + const response = await fetch(`${baseUrl}/api/settings`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(newSettings) + }); + + if (!response.ok) { + throw new Error(`Error updating settings: ${response.statusText}`); + } + + // Update the local store with the new settings + update((currentSettings) => ({ ...currentSettings, ...newSettings })); + + return true; + } catch (error) { + console.error('Failed to update settings:', error); + return false; + } + }, + set: (newSettings: Settings) => set(newSettings), + reset: () => set(defaultSettings) + }; } export const settings = createSettingsStore(); // Initialize the store by fetching settings when this module is first imported if (typeof window !== 'undefined') { - settings.fetch(); -} \ No newline at end of file + settings.fetch(); +} diff --git a/src/lib/stores/status.ts b/src/lib/stores/status.ts index f3cd5f9..68415af 100644 --- a/src/lib/stores/status.ts +++ b/src/lib/stores/status.ts @@ -3,178 +3,178 @@ import { baseUrl } from '$lib/env'; import type { Status } from '$lib/types'; // Create a default status object const defaultStatus: Status = { - currentScreen: 0, - numScreens: 0, - timerRunning: false, - isOTAUpdating: false, - espUptime: 0, - espFreeHeap: 0, - espHeapSize: 0, - connectionStatus: { - price: false, - blocks: false, - V2: false, - nostr: false - }, - rssi: 0, - currency: "USD", - dnd: { - enabled: false, - timeBasedEnabled: false, - startTime: "00:00", - endTime: "00:00", - active: false - }, - data: [], - leds: [ - {red: 0, green: 0, blue: 0, hex: "#000000"}, - {red: 0, green: 0, blue: 0, hex: "#000000"}, - {red: 0, green: 0, blue: 0, hex: "#000000"}, - {red: 0, green: 0, blue: 0, hex: "#000000"} - ] + currentScreen: 0, + numScreens: 0, + timerRunning: false, + isOTAUpdating: false, + espUptime: 0, + espFreeHeap: 0, + espHeapSize: 0, + connectionStatus: { + price: false, + blocks: false, + V2: false, + nostr: false + }, + rssi: 0, + currency: 'USD', + dnd: { + enabled: false, + timeBasedEnabled: false, + startTime: '00:00', + endTime: '00:00', + active: false + }, + data: [], + leds: [ + { red: 0, green: 0, blue: 0, hex: '#000000' }, + { red: 0, green: 0, blue: 0, hex: '#000000' }, + { red: 0, green: 0, blue: 0, hex: '#000000' }, + { red: 0, green: 0, blue: 0, hex: '#000000' } + ] }; // Create the Svelte store function createStatusStore() { - const { subscribe, set, update } = writable(defaultStatus); - let eventSource: EventSource | null = null; + const { subscribe, set, update } = writable(defaultStatus); + let eventSource: EventSource | null = null; - // Clean up function to close SSE connection - const cleanup = () => { - if (eventSource) { - eventSource.close(); - eventSource = null; - } - }; + // Clean up function to close SSE connection + const cleanup = () => { + if (eventSource) { + eventSource.close(); + eventSource = null; + } + }; - // Create the store object with methods - const store = { - subscribe, - fetch: async () => { - try { - const response = await fetch(`${baseUrl}/api/status`); - if (!response.ok) { - throw new Error(`Error fetching status: ${response.statusText}`); - } - const data = await response.json(); - set(data); - return data; - } catch (error) { - console.error('Failed to fetch status:', error); - return defaultStatus; - } - }, - startListening: () => { - // Clean up any existing connections first - cleanup(); - - // Only run in the browser, not during SSR - if (typeof window === 'undefined') return; + // Create the store object with methods + const store = { + subscribe, + fetch: async () => { + try { + const response = await fetch(`${baseUrl}/api/status`); + if (!response.ok) { + throw new Error(`Error fetching status: ${response.statusText}`); + } + const data = await response.json(); + set(data); + return data; + } catch (error) { + console.error('Failed to fetch status:', error); + return defaultStatus; + } + }, + startListening: () => { + // Clean up any existing connections first + cleanup(); - try { - // Create a new EventSource connection - eventSource = new EventSource(`${baseUrl}/events`); + // Only run in the browser, not during SSR + if (typeof window === 'undefined') return; - // Handle status updates - eventSource.addEventListener('status', (event) => { - try { - const data = JSON.parse(event.data); - update(currentStatus => ({ ...currentStatus, ...data })); - } catch (error) { - console.error('Error processing status event:', error); - } - }); + try { + // Create a new EventSource connection + eventSource = new EventSource(`${baseUrl}/events`); - // Handle connection status updates - eventSource.addEventListener('connection', (event) => { - try { - const data = JSON.parse(event.data); - update(currentStatus => ({ - ...currentStatus, - connectionStatus: { - ...currentStatus.connectionStatus, - ...data - } - })); - } catch (error) { - console.error('Error processing connection event:', error); - } - }); + // Handle status updates + eventSource.addEventListener('status', (event) => { + try { + const data = JSON.parse(event.data); + update((currentStatus) => ({ ...currentStatus, ...data })); + } catch (error) { + console.error('Error processing status event:', error); + } + }); - // Handle screen updates - eventSource.addEventListener('screen', (event) => { - try { - const data = JSON.parse(event.data); - update(currentStatus => ({ - ...currentStatus, - currentScreen: data.screen || currentStatus.currentScreen - })); - } catch (error) { - console.error('Error processing screen event:', error); - } - }); + // Handle connection status updates + eventSource.addEventListener('connection', (event) => { + try { + const data = JSON.parse(event.data); + update((currentStatus) => ({ + ...currentStatus, + connectionStatus: { + ...currentStatus.connectionStatus, + ...data + } + })); + } catch (error) { + console.error('Error processing connection event:', error); + } + }); - // Handle generic messages - eventSource.onmessage = (event) => { - if (event.type === 'message') { - return; - } - try { - const data = JSON.parse(event.data); - update(currentStatus => ({ ...currentStatus, ...data })); - } catch (error) { - console.error('Error processing message event:', error); - } - }; + // Handle screen updates + eventSource.addEventListener('screen', (event) => { + try { + const data = JSON.parse(event.data); + update((currentStatus) => ({ + ...currentStatus, + currentScreen: data.screen || currentStatus.currentScreen + })); + } catch (error) { + console.error('Error processing screen event:', error); + } + }); - // Handle errors - eventSource.onerror = (error) => { - console.error('EventSource failed:', error); - cleanup(); - // Attempt to reconnect after a delay - setTimeout(() => store.startListening(), 5000); - }; - } catch (error) { - console.error('Failed to setup event source:', error); - } - }, - stopListening: cleanup, - - // Function to set the current screen - setScreen: async (id: number): Promise => { - try { - // Make the GET request to change the screen - const response = await fetch(`${baseUrl}/api/show/screen/${id}`); - - if (!response.ok) { - throw new Error(`Error setting screen: ${response.statusText}`); - } - - // Update the store with the new screen ID - update(currentStatus => ({ - ...currentStatus, - currentScreen: id - })); - - return true; - } catch (error) { - console.error('Failed to set screen:', error); - return false; - } - } - }; - - return store; + // Handle generic messages + eventSource.onmessage = (event) => { + if (event.type === 'message') { + return; + } + try { + const data = JSON.parse(event.data); + update((currentStatus) => ({ ...currentStatus, ...data })); + } catch (error) { + console.error('Error processing message event:', error); + } + }; + + // Handle errors + eventSource.onerror = (error) => { + console.error('EventSource failed:', error); + cleanup(); + // Attempt to reconnect after a delay + setTimeout(() => store.startListening(), 5000); + }; + } catch (error) { + console.error('Failed to setup event source:', error); + } + }, + stopListening: cleanup, + + // Function to set the current screen + setScreen: async (id: number): Promise => { + try { + // Make the GET request to change the screen + const response = await fetch(`${baseUrl}/api/show/screen/${id}`); + + if (!response.ok) { + throw new Error(`Error setting screen: ${response.statusText}`); + } + + // Update the store with the new screen ID + update((currentStatus) => ({ + ...currentStatus, + currentScreen: id + })); + + return true; + } catch (error) { + console.error('Failed to set screen:', error); + return false; + } + } + }; + + return store; } export const status = createStatusStore(); // Initialize the store by fetching initial status and starting to listen for updates if (typeof window !== 'undefined') { - status.fetch().then(() => status.startListening()); - - // Clean up the EventSource when the window is unloaded - window.addEventListener('beforeunload', () => { - status.stopListening(); - }); -} \ No newline at end of file + status.fetch().then(() => status.startListening()); + + // Clean up the EventSource when the window is unloaded + window.addEventListener('beforeunload', () => { + status.stopListening(); + }); +} diff --git a/src/lib/types.ts b/src/lib/types.ts index ef0564f..1604add 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -3,11 +3,11 @@ */ export interface LedStatus { - hex: string; -} + hex: string; +} /** - * Data source types + * Data source types */ export enum DataSourceType { BTCLOCK_SOURCE = 0, @@ -26,31 +26,31 @@ export interface Status { espFreeHeap: number; espHeapSize: number; connectionStatus: { - price: boolean; - blocks: boolean; - V2: boolean; - nostr: boolean; + price: boolean; + blocks: boolean; + V2: boolean; + nostr: boolean; }; rssi: number; currency: string; dnd: { - enabled: boolean; - timeBasedEnabled: boolean; - startTime: string; - endTime: string; - active: boolean; + enabled: boolean; + timeBasedEnabled: boolean; + startTime: string; + endTime: string; + active: boolean; }; data: string[]; leds: Array<{ - red: number; - green: number; - blue: number; - hex: string; + red: number; + green: number; + blue: number; + hex: string; }>; [key: string]: unknown; - } +} - // Define the Settings interface based on the API response structure +// Define the Settings interface based on the API response structure export interface Settings { numScreens: number; invertedColor: boolean; @@ -120,9 +120,9 @@ export interface Settings { gitTag: string; lastBuildTime: string; screens: Array<{ - id: number; - name: string; - enabled: boolean; + id: number; + name: string; + enabled: boolean; }>; actCurrencies: string[]; availableCurrencies: string[]; @@ -130,13 +130,12 @@ export interface Settings { ceEndpoint: string; ceDisableSSL: boolean; dnd: { - enabled: boolean; - timeBasedEnabled: boolean; - startHour: number; - startMinute: number; - endHour: number; - endMinute: number; + enabled: boolean; + timeBasedEnabled: boolean; + startHour: number; + startMinute: number; + endHour: number; + endMinute: number; }; [key: string]: unknown; - } - \ No newline at end of file +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 1b550fd..2d5249b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,22 +1,22 @@ export const toTime = (secs: number) => { - const hours = Math.floor(secs / (60 * 60)); + const hours = Math.floor(secs / (60 * 60)); - const divisor_for_minutes = secs % (60 * 60); - const minutes = Math.floor(divisor_for_minutes / 60); + const divisor_for_minutes = secs % (60 * 60); + const minutes = Math.floor(divisor_for_minutes / 60); - const divisor_for_seconds = divisor_for_minutes % 60; - const seconds = Math.ceil(divisor_for_seconds); + const divisor_for_seconds = divisor_for_minutes % 60; + const seconds = Math.ceil(divisor_for_seconds); - const obj = { - h: hours, - m: minutes, - s: seconds - }; - return obj; + const obj = { + h: hours, + m: minutes, + s: seconds + }; + return obj; }; export const toUptimestring = (secs: number): string => { - const time = toTime(secs); + const time = toTime(secs); - return `${time.h}h ${time.m}m ${time.s}s`; + return `${time.h}h ${time.m}m ${time.s}s`; }; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1708864..aa85d10 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -25,7 +25,6 @@ status.stopListening(); } }); - export const prerender = true; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..d2c0be2 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,2 @@ +export const prerender = true; +export const ssr = false; \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a0a7977..29a0014 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,21 +1,15 @@
-
-
- -
- -
- -
+
+
+ +
-
+
+ +
+
diff --git a/src/routes/apidoc/+page.svelte b/src/routes/apidoc/+page.svelte index e03787a..e6bddab 100644 --- a/src/routes/apidoc/+page.svelte +++ b/src/routes/apidoc/+page.svelte @@ -3,17 +3,17 @@ import { onMount, onDestroy } from 'svelte'; let isLoaded = $state(false); - let scalarApiReference; + let scalarApiReference; function initializeScalar() { - // @ts-ignore - Scalar is loaded dynamically + // @ts-expect-error - Scalar is loaded dynamically if (window.Scalar) { - // @ts-ignore - Scalar is loaded dynamically + // @ts-expect-error - Scalar is loaded dynamically scalarApiReference = window.Scalar.createApiReference('#app', { url: '/swagger.json', - hideDarkModeToggle: true, - hideClientButton: true, - baseServerURL: baseUrl + hideDarkModeToggle: true, + hideClientButton: true, + baseServerURL: baseUrl }); isLoaded = true; } else { @@ -34,33 +34,33 @@ } let darkMode = $state(false); - let handler: (e: MediaQueryListEvent) => void; - let mediaQuery: MediaQueryList; + let handler: (e: MediaQueryListEvent) => void; + let mediaQuery: MediaQueryList; onMount(() => { loadScalarScript(); - darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; + darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; - mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - handler = (e: MediaQueryListEvent) => { - darkMode = e.matches; - }; - mediaQuery.addEventListener('change', handler); + mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + handler = (e: MediaQueryListEvent) => { + darkMode = e.matches; + }; + mediaQuery.addEventListener('change', handler); return () => { isLoaded = false; }; }); - onDestroy(() => { - if (mediaQuery) { - mediaQuery.removeEventListener('change', handler); - } - if (isLoaded) { - document.querySelectorAll('style[data-scalar]').forEach(el => el.remove()); + onDestroy(() => { + if (mediaQuery) { + mediaQuery.removeEventListener('change', handler); + } + if (isLoaded) { + document.querySelectorAll('style[data-scalar]').forEach((el) => el.remove()); - scalarApiReference.destroy(); - } - }); + scalarApiReference.destroy(); + } + });
diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index ad4ad7e..d21bd01 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -1,8 +1,8 @@
-

Settings

- -
\ No newline at end of file +

Settings

+ +
diff --git a/src/routes/system/+page.svelte b/src/routes/system/+page.svelte index b609b7f..3007a65 100644 --- a/src/routes/system/+page.svelte +++ b/src/routes/system/+page.svelte @@ -1,8 +1,8 @@
-

System Management

- -
\ No newline at end of file +

System Management

+ + diff --git a/svelte.config.js b/svelte.config.js index d96b82d..52492a7 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -3,11 +3,16 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; const config = { preprocess: vitePreprocess(), - kit: { adapter: adapter({ - fallback: 'index.html', - precompress: false, - strict: true - }) } + kit: { + adapter: adapter({ + pages: 'build', + assets: 'build', + fallback: 'bundle.html', + precompress: false, + strict: true + }), + appDir: 'build' + } }; export default config;