diff --git a/playwright.screenshot.config.ts b/playwright.screenshot.config.ts index 2eadd5f..81264a4 100644 --- a/playwright.screenshot.config.ts +++ b/playwright.screenshot.config.ts @@ -1,6 +1,7 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ + reporter: 'html', use: { locale: 'en-GB', timezoneId: 'Europe/Amsterdam' @@ -31,6 +32,20 @@ export default defineConfig({ use: { viewport: { width: 1512, height: 982 } } + }, + { + name: 'MacBook Pro 14 inch', + use: { + viewport: { width: 1512, height: 982 } + } + }, + { + name: 'MacBook Pro 14 inch Firefox HiDPI', + use: { ...devices['Desktop Firefox HiDPI'], viewport: { width: 1512, height: 982 } } + }, + { + name: 'MacBook Pro 14 inch Safari', + use: { ...devices['Desktop Safari'], viewport: { width: 1512, height: 982 } } } ] }); diff --git a/src/lib/style/app.scss b/src/lib/style/app.scss index 002e5db..0703d06 100644 --- a/src/lib/style/app.scss +++ b/src/lib/style/app.scss @@ -1,6 +1,4 @@ @import '../node_modules/bootstrap/scss/functions'; -@import '../node_modules/bootstrap/scss/variables'; -@import '../node_modules/bootstrap/scss/variables-dark'; //@import "@fontsource/antonio/latin-400.css"; @import '@fontsource/ubuntu/latin-400.css'; @@ -14,6 +12,8 @@ $font-family-base: 'Ubuntu'; $font-size-base: 0.9rem; $input-font-size-sm: $font-size-base * 0.875; +@import '../node_modules/bootstrap/scss/variables'; +@import '../node_modules/bootstrap/scss/variables-dark'; // $border-radius: .675rem; @import '../node_modules/bootstrap/scss/mixins'; @@ -43,6 +43,40 @@ $input-font-size-sm: $font-size-base * 0.875; @import '../node_modules/bootstrap/scss/helpers'; @import '../node_modules/bootstrap/scss/utilities/api'; +/* Default state (xs) - sticky */ +.sticky-xs-top { + position: sticky; + top: 0; + z-index: 1020; +} + +@media (max-width: 576px) { + main { + margin-top: 25px; + } +} + +/* Remove sticky behavior for larger screens */ +@media (min-width: 576px) { + .sticky-xs-top { + position: relative; + } +} + +@include color-mode(dark) { + .navbar { + --bs-navbar-color: $light; + background-color: $dark; + } +} + +@include color-mode(light) { + .navbar { + --bs-navbar-color: $dark; + background-color: $light; + } +} + nav { margin-bottom: 15px; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 0faad53..e421f5a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -12,6 +12,7 @@ NavbarBrand, NavbarToggler } from '@sveltestrap/sveltestrap'; + import { _ } from 'svelte-i18n'; import { page } from '$app/stores'; import { locale, locales, isLoading } from 'svelte-i18n'; @@ -38,7 +39,7 @@ return flagMap[lowercaseCode]; } else { // Return null for unsupported language codes - return null; + return flagMap['en']; } }; @@ -61,8 +62,23 @@ }; - - ₿TClock + + ₿TClock + + @@ -94,4 +110,6 @@ - +
+ +
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index b37ab13..c2e4e3b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -3,6 +3,7 @@ import { screenSize, updateScreenSize } from '$lib/screen'; import { Container, Row, Toast, ToastBody } from '@sveltestrap/sveltestrap'; + import { replaceState } from '$app/navigation'; import { onMount } from 'svelte'; import { writable } from 'svelte/store'; @@ -16,12 +17,6 @@ bgColor: '0' }); - // let uiSettings = writable({ - // inputSize: 'sm', - // selectClass: '', - // btnSize: 'lg' - // }); - let status = writable({ data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'], espFreeHeap: 0, @@ -60,7 +55,43 @@ }); }; + let sections: (HTMLElement | null)[]; + let observer: IntersectionObserver; + const SM_BREAKPOINT = 576; + + const setupObserver = () => { + if (window.innerWidth < SM_BREAKPOINT) { + observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const id = entry.target.id; + replaceState(`#${id}`); + + // Update nav pills + document.querySelectorAll('.nav-link').forEach((link) => { + link.classList.remove('active'); + if (link.getAttribute('href') === `#${id}`) { + link.classList.add('active'); + } + }); + } + }); + }, + { + threshold: 0.25 // Trigger when section is 50% visible + } + ); + + sections = ['control', 'status', 'settings'].map((id) => document.getElementById(id)); + + sections.forEach((section) => observer.observe(section!)); + } + }; + onMount(() => { + setupObserver(); + fetchSettingsData(); fetchStatusData(); @@ -72,6 +103,11 @@ }); function handleResize() { + if (observer) { + observer.disconnect(); + } + setupObserver(); + updateScreenSize(); } @@ -125,7 +161,9 @@ + + diff --git a/src/routes/Control.svelte b/src/routes/Control.svelte index d89ee10..25f5a44 100644 --- a/src/routes/Control.svelte +++ b/src/routes/Control.svelte @@ -105,8 +105,8 @@ export let xxl = xl; - - + + {$_('section.control.title', { default: 'Control' })} diff --git a/src/routes/Settings.svelte b/src/routes/Settings.svelte index 141c512..77c8a3d 100644 --- a/src/routes/Settings.svelte +++ b/src/routes/Settings.svelte @@ -221,8 +221,8 @@ systemIsOpen: boolean; - - + +
- - + + {$_('section.status.title', { default: 'Status' })} diff --git a/tests/screenshots/viewport-screenshots.spec.ts b/tests/screenshots/viewport-screenshots.spec.ts index 9628342..573b059 100644 --- a/tests/screenshots/viewport-screenshots.spec.ts +++ b/tests/screenshots/viewport-screenshots.spec.ts @@ -4,19 +4,23 @@ import { initMock, settingsJson } from '../shared'; test.beforeEach(initMock); -test('capture screenshots across devices', async ({ page }) => { +test('capture screenshots across devices', async ({ page }, testInfo) => { await page.goto('/'); await expect(page.getByRole('heading', { name: 'Control' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible(); - await page.screenshot({ - path: `./test-results/screenshots/default-${test.info().project.name}.png`, - fullPage: true + const screenshot = await page.screenshot({ + path: `./test-results/screenshots/default-${test.info().project.name.toLowerCase().replace(' ', '_')}.png` + }); + + await testInfo.attach(`default`, { + body: screenshot, + contentType: 'image/png' }); }); -test('capture screenshots across devices with bitaxe screens', async ({ page }) => { +test('capture screenshots across devices with bitaxe screens', async ({ page }, testInfo) => { settingsJson.screens = [ { id: 0, @@ -71,7 +75,11 @@ test('capture screenshots across devices with bitaxe screens', async ({ page }) await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible(); await page.screenshot({ - path: `./test-results/screenshots/bitaxe-${test.info().project.name}.png`, - fullPage: true + path: `./test-results/screenshots/bitaxe-${test.info().project.name.toLowerCase().replace(' ', '_')}.png` + }); + + await testInfo.attach(`bitaxe`, { + path: `./test-results/screenshots/bitaxe-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`, + contentType: 'image/png' }); }); diff --git a/vite.config.ts b/vite.config.ts index bb8e5b2..b2ed254 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -76,6 +76,14 @@ export default defineConfig({ } } }, + css: { + preprocessorOptions: { + scss: { + quietDeps: true, + silenceDeprecations: ['import'] + } + } + }, test: { include: ['src/**/*.{test,spec}.{js,ts}'], globals: true,