From 829f8da82a3ae8bd8eef6f32d9dccb593f7cc4de Mon Sep 17 00:00:00 2001 From: DerEchteAlec Date: Sun, 23 Aug 2026 22:12:22 +0200 Subject: [PATCH] FIX - polish camera controls and landscape layout (#37) Co-authored-by: Leon.Schmidt <159480018+leonw21342315@users.noreply.github.com> --- frontend/src/App.vue | 2 + frontend/src/assets/main.css | 10 ++ frontend/src/views/PhoneAppWindow.vue | 6 +- .../src/views/apps/CameraApp.contract.test.ts | 102 ++++++++++++++++++ frontend/src/views/apps/CameraApp.vue | 97 +++++++++++------ .../src/views/apps/SkyUiApps.contract.test.ts | 2 +- 6 files changed, 186 insertions(+), 33 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index f5ef846..b5a81b7 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1885,6 +1885,8 @@ onBeforeUnmount(() => { class="phone-screen" :class="{ 'phone-screen--app': isAppRoute || isDevelopmentRoute, + 'phone-screen--camera-landscape': + activeAppId === 'camera' && phone.cameraLandscape, 'phone-app--light': !displayedDarkMode, [`phone-app--${phone.preferences.settings.graphicsMode}`]: true, }" diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 6d7181a..48ff04e 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -460,6 +460,7 @@ button { } } .phone-screen { + --phone-screen-portrait-ratio: 2.30951; position: relative; container-type: size; margin: auto; @@ -470,6 +471,12 @@ button { background: #08080a; border-radius: 40px; } +.phone-screen--camera-landscape { + background: transparent; +} +.phone-screen--camera-landscape .springboard { + visibility: hidden; +} .phone-app { position: absolute !important; inset: 0 !important; @@ -3239,6 +3246,9 @@ button { backface-visibility: hidden; will-change: transform, border-radius, opacity; } +.app-window--camera-landscape { + background: transparent; +} .app-window--citywarn { backface-visibility: visible; filter: none; diff --git a/frontend/src/views/PhoneAppWindow.vue b/frontend/src/views/PhoneAppWindow.vue index 0dcc86e..19e4d3a 100644 --- a/frontend/src/views/PhoneAppWindow.vue +++ b/frontend/src/views/PhoneAppWindow.vue @@ -30,7 +30,11 @@ const launchStyle = computed(() => {
{ expect(cameraView).not.toContain('k-navbar') }) + it('stays dark when the global phone appearance is light', () => { + const cameraPageTag = cameraView.match(/]*>/s)?.[0] + + expect(cameraPageTag).toBeDefined() + expect(cameraPageTag).toMatch(/\bdark\b/) + expect(cameraView).toMatch( + /\.camera-page\s*\{[^}]*--sky-bg:\s*#000;[^}]*--sky-text:\s*#fff;[^}]*background:\s*var\(--sky-bg\);[^}]*color:\s*var\(--sky-text\);/s, + ) + }) + + it('fills the translucent landscape shell without letterbox gaps', () => { + const landscapeViewport = cameraView.match( + /\.camera-page--landscape \.camera-viewport\s*\{([^}]*)\}/s, + )?.[1] + + expect(cameraView).toMatch( + /\.camera-page--landscape\s*\{[^}]*--sky-bg:\s*rgba\(0, 0, 0, 0\.42\);/s, + ) + expect(landscapeViewport).toBeDefined() + expect(landscapeViewport).toContain('top: 50%') + expect(landscapeViewport).toContain( + 'width: calc(100% * var(--phone-screen-portrait-ratio))', + ) + expect(landscapeViewport).toContain( + 'aspect-ratio: var(--phone-screen-portrait-ratio)', + ) + expect(landscapeViewport).not.toContain('16 / 9') + expect(cameraView).toMatch( + /\.camera-page--landscape \.camera-shade\s*\{[^}]*linear-gradient\([^)]*90deg,[^)]*rgba\(0, 0, 0, 0\.42\) 0 18%,[^)]*transparent 18% 75%,[^)]*rgba\(0, 0, 0, 0\.42\) 75% 100%/s, + ) + expect(appShell).toMatch( + /'phone-screen--camera-landscape':\s*activeAppId === 'camera' && phone\.cameraLandscape/, + ) + expect(appWindow).toMatch( + /'app-window--camera-landscape':\s*app\.id === 'camera' && phone\.cameraLandscape/, + ) + expect(shellStyles).toMatch( + /\.phone-screen\s*\{[^}]*--phone-screen-portrait-ratio:\s*2\.30951;/s, + ) + expect(shellStyles).toMatch( + /\.phone-screen--camera-landscape\s*\{[^}]*background:\s*transparent;/s, + ) + expect(shellStyles).toMatch( + /\.phone-screen--camera-landscape \.springboard\s*\{[^}]*visibility:\s*hidden;/s, + ) + expect(shellStyles).toMatch( + /\.app-window--camera-landscape\s*\{[^}]*background:\s*transparent;/s, + ) + }) + + it('renders camera notices as plain text without a glass pill', () => { + const noticeMarkup = cameraView.slice( + cameraView.indexOf(' { + const zoomControl = cameraView.slice( + cameraView.indexOf('
'), + cameraView.indexOf('
'), + ) + + expect(zoomControl).toContain('variant="plain"') + expect(zoomControl).not.toContain('glass') + expect(zoomControl).toContain('{{ zoomPresetLabel(zoom) }}') + expect(cameraView).toContain("return zoom === 0.5 ? '.5' : `${zoom}`") + expect(cameraView).toMatch( + /\.camera-zoom-control\s*\{[^}]*bottom:\s*216px;/s, + ) + expect(cameraView).toMatch(/\.camera-zoom-row\s*\{[^}]*gap:\s*10px;/s) + expect(cameraView).toMatch( + /\.camera-zoom-pill\s*\{[^}]*width:\s*36px;[^}]*min-width:\s*36px;[^}]*height:\s*36px;[^}]*min-height:\s*36px;[^}]*padding:\s*0;/s, + ) + expect(cameraView).toMatch( + /\.camera-zoom-pill::before\s*\{[^}]*width:\s*var\(--sky-touch-target, 44px\);[^}]*inset-block:\s*-4px;/s, + ) + expect(cameraView).toMatch( + /\.camera-zoom-pill\.active\s*\{[^}]*color:\s*#ffd60a;[^}]*background:\s*rgba\(28, 28, 30, 0\.78\);/s, + ) + }) + it('keeps continuous wheel zoom without an extra slider bar', () => { expect(cameraView).not.toContain('camera-zoom-slider') expect(cameraView).not.toContain('type="range"') diff --git a/frontend/src/views/apps/CameraApp.vue b/frontend/src/views/apps/CameraApp.vue index 4823e35..72f9b80 100644 --- a/frontend/src/views/apps/CameraApp.vue +++ b/frontend/src/views/apps/CameraApp.vue @@ -77,6 +77,16 @@ const pendingCount = computed( () => captures.value.filter((capture) => capture.status === 'uploading').length, ) + +function zoomPresetIsActive(zoom: (typeof zoomLevels)[number]): boolean { + return Math.abs(selectedZoom.value - zoom) < 0.03 +} + +function zoomPresetLabel(zoom: (typeof zoomLevels)[number]): string { + if (zoomPresetIsActive(zoom)) return `${zoom}x` + return zoom === 0.5 ? '.5' : `${zoom}` +} + function correlationId(): string { return `${Date.now()}-${crypto.randomUUID()}` } @@ -453,6 +463,7 @@ onBeforeUnmount(() => { class="camera-page" :class="{ 'camera-page--landscape': phone.cameraLandscape }" :aria-label="phone.t('Apps.camera.name')" + dark >
{
- + {{ noticeText }} @@ -585,16 +593,16 @@ onBeforeUnmount(() => { - {{ zoom }}x + {{ zoomPresetLabel(zoom) }}
@@ -685,10 +693,15 @@ onBeforeUnmount(() => {