diff --git a/frontend/public/img/wallpapers/sky-phone-demo-gradient.png b/frontend/public/img/wallpapers/sky-phone-demo-gradient.png
new file mode 100644
index 0000000..2c7f53a
Binary files /dev/null and b/frontend/public/img/wallpapers/sky-phone-demo-gradient.png differ
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index dc8cd20..5289d74 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -268,8 +268,13 @@ const PHONE_BASE_SCALE = 0.69 * 1.2
const PHONE_PORTRAIT_WIDTH = 390
const PHONE_PORTRAIT_HEIGHT = 844
const MIN_PRODUCTION_PHONE_ZOOM = 260 / PHONE_PORTRAIT_WIDTH
-const isDevelopment = import.meta.env.DEV
const developmentParameters = new URLSearchParams(window.location.search)
+const isBrowserPreview =
+ developmentParameters.has('browserPreview') &&
+ developmentParameters.get('apiBase')?.startsWith('/') === true
+const isDevelopment =
+ import.meta.env.DEV ||
+ developmentParameters.get('apiBase')?.startsWith('/') === true
const developmentLockScreenPreview =
isDevelopment && developmentParameters.has('lockScreenPreview')
@@ -433,6 +438,14 @@ let simPickerClosePending = false
function getViewportScale(): number {
const heightScale = window.innerHeight / REFERENCE_VIEWPORT_HEIGHT
+ if (isBrowserPreview) {
+ const availableScale = Math.min(
+ window.innerWidth / PHONE_PORTRAIT_WIDTH,
+ window.innerHeight / PHONE_PORTRAIT_HEIGHT,
+ )
+
+ return (availableScale * 0.94) / PHONE_BASE_SCALE
+ }
if (isDevelopment) return heightScale
return Math.min(window.innerWidth / REFERENCE_VIEWPORT_WIDTH, heightScale)
@@ -1583,6 +1596,7 @@ onBeforeUnmount(() => {
"
class="phone-stage"
:class="{
+ 'phone-stage--browser-preview': isBrowserPreview,
'phone-stage--landscape': phone.cameraLandscape,
'phone-stage--peek': notifications.isPeeking,
}"
diff --git a/frontend/src/AppDevelopmentPreview.contract.test.ts b/frontend/src/AppDevelopmentPreview.contract.test.ts
index af0d7eb..3246879 100644
--- a/frontend/src/AppDevelopmentPreview.contract.test.ts
+++ b/frontend/src/AppDevelopmentPreview.contract.test.ts
@@ -84,4 +84,16 @@ describe('browser development preview contract', () => {
)
expect(mainCss).not.toContain('.phone-stage--dev')
})
+
+ it('fills the dedicated browser embed without clipping the hardware controls', () => {
+ expect(source).toContain("developmentParameters.has('browserPreview')")
+ expect(source).toContain("'phone-stage--browser-preview': isBrowserPreview")
+ expect(source).toContain('return (availableScale * 0.94) / PHONE_BASE_SCALE')
+ expect(mainCss).toMatch(
+ /\.phone-stage--browser-preview\s*\{[^}]*place-items:\s*center;[^}]*padding:\s*0;/s,
+ )
+ expect(mainCss).toMatch(
+ /\.phone-stage--browser-preview \.phone-device\s*\{[^}]*filter:\s*none;/s,
+ )
+ })
})
diff --git a/frontend/src/assets/img/app-icons/billing.svg b/frontend/src/assets/img/app-icons/billing.svg
deleted file mode 100644
index 5cc30ba..0000000
--- a/frontend/src/assets/img/app-icons/billing.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/billing.webp b/frontend/src/assets/img/app-icons/billing.webp
new file mode 100644
index 0000000..9ee5e84
Binary files /dev/null and b/frontend/src/assets/img/app-icons/billing.webp differ
diff --git a/frontend/src/assets/img/app-icons/calendar.svg b/frontend/src/assets/img/app-icons/calendar.svg
deleted file mode 100644
index 6c34aa8..0000000
--- a/frontend/src/assets/img/app-icons/calendar.svg
+++ /dev/null
@@ -1,26 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/calendar.webp b/frontend/src/assets/img/app-icons/calendar.webp
new file mode 100644
index 0000000..af607ad
Binary files /dev/null and b/frontend/src/assets/img/app-icons/calendar.webp differ
diff --git a/frontend/src/assets/img/app-icons/citywarn.webp b/frontend/src/assets/img/app-icons/citywarn.webp
index d15f7c6..083957e 100644
Binary files a/frontend/src/assets/img/app-icons/citywarn.webp and b/frontend/src/assets/img/app-icons/citywarn.webp differ
diff --git a/frontend/src/assets/img/app-icons/companies.svg b/frontend/src/assets/img/app-icons/companies.svg
deleted file mode 100644
index b52f7ce..0000000
--- a/frontend/src/assets/img/app-icons/companies.svg
+++ /dev/null
@@ -1,29 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/companies.webp b/frontend/src/assets/img/app-icons/companies.webp
new file mode 100644
index 0000000..5f5ee8e
Binary files /dev/null and b/frontend/src/assets/img/app-icons/companies.webp differ
diff --git a/frontend/src/assets/img/app-icons/crewlink.svg b/frontend/src/assets/img/app-icons/crewlink.svg
deleted file mode 100644
index 27e2f21..0000000
--- a/frontend/src/assets/img/app-icons/crewlink.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/crewlink.webp b/frontend/src/assets/img/app-icons/crewlink.webp
new file mode 100644
index 0000000..7e35b75
Binary files /dev/null and b/frontend/src/assets/img/app-icons/crewlink.webp differ
diff --git a/frontend/src/assets/img/app-icons/crypto-header-logo.png b/frontend/src/assets/img/app-icons/crypto-header-logo.png
deleted file mode 100644
index 734bf52..0000000
Binary files a/frontend/src/assets/img/app-icons/crypto-header-logo.png and /dev/null differ
diff --git a/frontend/src/assets/img/app-icons/crypto-header-logo.webp b/frontend/src/assets/img/app-icons/crypto-header-logo.webp
new file mode 100644
index 0000000..1fe2223
Binary files /dev/null and b/frontend/src/assets/img/app-icons/crypto-header-logo.webp differ
diff --git a/frontend/src/assets/img/app-icons/feather.svg b/frontend/src/assets/img/app-icons/feather.svg
deleted file mode 100644
index 6f3a090..0000000
--- a/frontend/src/assets/img/app-icons/feather.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/feather.webp b/frontend/src/assets/img/app-icons/feather.webp
new file mode 100644
index 0000000..fb96e3b
Binary files /dev/null and b/frontend/src/assets/img/app-icons/feather.webp differ
diff --git a/frontend/src/assets/img/app-icons/flare.svg b/frontend/src/assets/img/app-icons/flare.svg
deleted file mode 100644
index 3cad880..0000000
--- a/frontend/src/assets/img/app-icons/flare.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/flare.webp b/frontend/src/assets/img/app-icons/flare.webp
new file mode 100644
index 0000000..4999692
Binary files /dev/null and b/frontend/src/assets/img/app-icons/flare.webp differ
diff --git a/frontend/src/assets/img/app-icons/house.svg b/frontend/src/assets/img/app-icons/house.svg
deleted file mode 100644
index 050488a..0000000
--- a/frontend/src/assets/img/app-icons/house.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/house.webp b/frontend/src/assets/img/app-icons/house.webp
new file mode 100644
index 0000000..575b2d2
Binary files /dev/null and b/frontend/src/assets/img/app-icons/house.webp differ
diff --git a/frontend/src/assets/img/app-icons/local-pages.webp b/frontend/src/assets/img/app-icons/local-pages.webp
index 91bd3b1..94d92fa 100644
Binary files a/frontend/src/assets/img/app-icons/local-pages.webp and b/frontend/src/assets/img/app-icons/local-pages.webp differ
diff --git a/frontend/src/assets/img/app-icons/memos.webp b/frontend/src/assets/img/app-icons/memos.webp
index 589e615..8bd9bd5 100644
Binary files a/frontend/src/assets/img/app-icons/memos.webp and b/frontend/src/assets/img/app-icons/memos.webp differ
diff --git a/frontend/src/assets/img/app-icons/music.svg b/frontend/src/assets/img/app-icons/music.svg
deleted file mode 100644
index 308c035..0000000
--- a/frontend/src/assets/img/app-icons/music.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/music.webp b/frontend/src/assets/img/app-icons/music.webp
new file mode 100644
index 0000000..db954d0
Binary files /dev/null and b/frontend/src/assets/img/app-icons/music.webp differ
diff --git a/frontend/src/assets/img/app-icons/neon-drop-source.png b/frontend/src/assets/img/app-icons/neon-drop-source.png
deleted file mode 100644
index fb2efc7..0000000
Binary files a/frontend/src/assets/img/app-icons/neon-drop-source.png and /dev/null differ
diff --git a/frontend/src/assets/img/app-icons/picstagram.webp b/frontend/src/assets/img/app-icons/picstagram.webp
index 6512bb2..68dcd58 100644
Binary files a/frontend/src/assets/img/app-icons/picstagram.webp and b/frontend/src/assets/img/app-icons/picstagram.webp differ
diff --git a/frontend/src/assets/img/app-icons/radio.webp b/frontend/src/assets/img/app-icons/radio.webp
index bbce3fa..bc8fa54 100644
Binary files a/frontend/src/assets/img/app-icons/radio.webp and b/frontend/src/assets/img/app-icons/radio.webp differ
diff --git a/frontend/src/assets/img/app-icons/sky-flappy.svg b/frontend/src/assets/img/app-icons/sky-flappy.svg
deleted file mode 100644
index 2588494..0000000
--- a/frontend/src/assets/img/app-icons/sky-flappy.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/skyride.svg b/frontend/src/assets/img/app-icons/skyride.svg
deleted file mode 100644
index 00e99ca..0000000
--- a/frontend/src/assets/img/app-icons/skyride.svg
+++ /dev/null
@@ -1,34 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/skyride.webp b/frontend/src/assets/img/app-icons/skyride.webp
new file mode 100644
index 0000000..84eec27
Binary files /dev/null and b/frontend/src/assets/img/app-icons/skyride.webp differ
diff --git a/frontend/src/assets/img/app-icons/weazel-news.svg b/frontend/src/assets/img/app-icons/weazel-news.svg
deleted file mode 100644
index c98d991..0000000
--- a/frontend/src/assets/img/app-icons/weazel-news.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
diff --git a/frontend/src/assets/img/app-icons/weazel-news.webp b/frontend/src/assets/img/app-icons/weazel-news.webp
new file mode 100644
index 0000000..17d140d
Binary files /dev/null and b/frontend/src/assets/img/app-icons/weazel-news.webp differ
diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css
index c490e5f..816d110 100644
--- a/frontend/src/assets/main.css
+++ b/frontend/src/assets/main.css
@@ -248,6 +248,13 @@ button {
padding: 0 var(--phone-edge-gap, 24px) var(--phone-edge-gap, 24px) 0;
pointer-events: none;
}
+.phone-stage--browser-preview {
+ place-items: center;
+ padding: 0;
+}
+.phone-stage--browser-preview .phone-device {
+ filter: none;
+}
.phone-stage--peek .phone-resolution-wrapper--primary .phone-device {
transform: translateY(calc(100% - 190px));
transform-origin: right bottom;
diff --git a/frontend/src/config/apps.ts b/frontend/src/config/apps.ts
index bca3955..0cda34d 100644
--- a/frontend/src/config/apps.ts
+++ b/frontend/src/config/apps.ts
@@ -45,7 +45,7 @@ import appStoreIcon from '@/assets/img/app-icons/apps.webp'
import calculatorIcon from '@/assets/img/app-icons/calculator.webp'
import cameraIcon from '@/assets/img/app-icons/camera.webp'
import clockIcon from '@/assets/img/app-icons/clock.webp'
-import calendarIcon from '@/assets/img/app-icons/calendar.svg'
+import calendarIcon from '@/assets/img/app-icons/calendar.webp'
import mailIcon from '@/assets/img/app-icons/mail.webp'
import mapIcon from '@/assets/img/app-icons/map.webp'
import messagesIcon from '@/assets/img/app-icons/sms.webp'
@@ -67,20 +67,20 @@ import weatherIcon from '@/assets/img/app-icons/weather.webp'
import healthIcon from '@/assets/img/app-icons/health.webp'
import bankingIcon from '@/assets/img/app-icons/banking.webp'
import cryptoIcon from '@/assets/img/app-icons/crypto.webp'
-import billingIcon from '@/assets/img/app-icons/billing.svg'
+import billingIcon from '@/assets/img/app-icons/billing.webp'
import garageIcon from '@/assets/img/app-icons/garage.webp'
-import houseIcon from '@/assets/img/app-icons/house.svg'
+import houseIcon from '@/assets/img/app-icons/house.webp'
import citymarktIcon from '@/assets/img/app-icons/citymarkt.webp'
import localPagesIcon from '@/assets/img/app-icons/local-pages.webp'
-import flareIcon from '@/assets/img/app-icons/flare.svg'
+import flareIcon from '@/assets/img/app-icons/flare.webp'
import flipTokIcon from '@/assets/img/app-icons/fliptok.webp'
import picstagramIcon from '@/assets/img/app-icons/picstagram.webp'
-import skyRideIcon from '@/assets/img/app-icons/skyride.svg'
-import musicIcon from '@/assets/img/app-icons/music.svg'
-import featherIcon from '@/assets/img/app-icons/feather.svg'
-import crewLinkIcon from '@/assets/img/app-icons/crewlink.svg'
-import companiesIcon from '@/assets/img/app-icons/companies.svg'
-import weazelNewsIcon from '@/assets/img/app-icons/weazel-news.svg'
+import skyRideIcon from '@/assets/img/app-icons/skyride.webp'
+import musicIcon from '@/assets/img/app-icons/music.webp'
+import featherIcon from '@/assets/img/app-icons/feather.webp'
+import crewLinkIcon from '@/assets/img/app-icons/crewlink.webp'
+import companiesIcon from '@/assets/img/app-icons/companies.webp'
+import weazelNewsIcon from '@/assets/img/app-icons/weazel-news.webp'
import cityWarnIcon from '@/assets/img/app-icons/citywarn.webp'
import type {
BuiltinPhoneAppDefinition,
diff --git a/frontend/src/utils/nui.ts b/frontend/src/utils/nui.ts
index eecee7e..02a40e4 100644
--- a/frontend/src/utils/nui.ts
+++ b/frontend/src/utils/nui.ts
@@ -11,18 +11,21 @@ export async function nuiCall(
endpoint: string,
data: Record = {},
): Promise> {
- const developmentPort = new URLSearchParams(window.location.search).get(
- 'apiPort',
- )
- const baseUrl = import.meta.env.DEV
- ? `http://localhost:${developmentPort ?? '3002'}/api`
- : `https://${resourceName}`
- const requestData = import.meta.env.DEV
+ const developmentParameters = new URLSearchParams(window.location.search)
+ const developmentPort = developmentParameters.get('apiPort')
+ const configuredApiBase = developmentParameters.get('apiBase')
+ const browserApiBase = configuredApiBase?.startsWith('/')
+ ? configuredApiBase.replace(/\/$/, '')
+ : null
+ const baseUrl = browserApiBase
+ ?? (import.meta.env.DEV
+ ? `http://localhost:${developmentPort ?? '3002'}/api`
+ : `https://${resourceName}`)
+ const requestData = browserApiBase || import.meta.env.DEV
? {
...data,
_testScenario:
- new URLSearchParams(window.location.search).get('testScenario') ??
- undefined,
+ developmentParameters.get('testScenario') ?? undefined,
}
: data
const controller = new AbortController()
diff --git a/frontend/src/views/apps/CryptoApp.vue b/frontend/src/views/apps/CryptoApp.vue
index e99d5a8..1cd357f 100644
--- a/frontend/src/views/apps/CryptoApp.vue
+++ b/frontend/src/views/apps/CryptoApp.vue
@@ -25,7 +25,7 @@ import {
WalletCards,
} from 'lucide-vue-next'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
-import cryptoHeaderLogo from '@/assets/img/app-icons/crypto-header-logo.png'
+import cryptoHeaderLogo from '@/assets/img/app-icons/crypto-header-logo.webp'
import CryptoLogo from '@/components/crypto/CryptoLogo.vue'
import { useAccountStore } from '@/stores/account'
import { useCryptoStore } from '@/stores/crypto'
diff --git a/frontend/testserver/index.cjs b/frontend/testserver/index.cjs
index c1b6eda..c70b2ab 100644
--- a/frontend/testserver/index.cjs
+++ b/frontend/testserver/index.cjs
@@ -2906,6 +2906,50 @@ let calendarEvents = [
title: 'Beach cruise',
},
]
+const demoInstalledAppIds = [
+ 'citywarn',
+ 'crypto',
+ 'health',
+ 'weazel-news',
+ 'companies',
+ 'music',
+ 'picstagram',
+ 'feather',
+ 'fliptok',
+ 'flare',
+ 'calendar',
+ 'radio',
+ 'local-pages',
+ 'crewlink',
+ 'phone',
+ 'messages',
+ 'darkchat',
+ 'garage',
+ 'house',
+ 'map',
+ 'skyride',
+ 'banking',
+ 'billing',
+ 'mail',
+ 'notes',
+ 'memos',
+ 'calculator',
+ 'camera',
+ 'clock',
+ 'weather',
+ 'photos',
+ 'app-store',
+ 'settings',
+ 'snake',
+ 'memory',
+ 'number-merge',
+ 'minesweeper',
+ 'tower-stack',
+ 'sky-flappy',
+ 'citymarkt',
+ 'neon-drop',
+]
+const demoWallpaperUrl = '/img/wallpapers/sky-phone-demo-gradient.png'
const deviceData = {
appAuth: {
payload: {
@@ -2949,7 +2993,7 @@ const deviceData = {
},
apps: {
payload: {
- claimedApps: [],
+ claimedApps: [...demoInstalledAppIds],
},
revision: 2,
},
@@ -3042,6 +3086,8 @@ const deviceData = {
airplaneMode: false,
appearanceMode: 'automatic',
frame: 'black',
+ lockWallpaper: 'custom',
+ lockWallpaperImageUrl: demoWallpaperUrl,
notificationDurationSeconds: 10,
notificationSound: 'chime',
notificationVolume: 70,
@@ -3049,7 +3095,11 @@ const deviceData = {
ringtone: 'skyline',
ringtoneVolume: 80,
streamerMode: false,
- wallpaper: 'midnight',
+ wallpaper: 'custom',
+ wallpaperHistory: [
+ { imageUrl: demoWallpaperUrl, wallpaper: 'custom' },
+ ],
+ wallpaperImageUrl: demoWallpaperUrl,
},
version: 1,
},
@@ -9716,7 +9766,33 @@ app.post('/api/:endpoint', (request, response) => {
},
},
}
- : deviceData
+ : testScenario === ''
+ ? {
+ ...deviceData,
+ apps: {
+ ...deviceData.apps,
+ payload: {
+ claimedApps: [...demoInstalledAppIds],
+ },
+ },
+ settings: {
+ ...deviceData.settings,
+ payload: {
+ ...deviceData.settings.payload,
+ settings: {
+ ...deviceData.settings.payload.settings,
+ lockWallpaper: 'custom',
+ lockWallpaperImageUrl: demoWallpaperUrl,
+ wallpaper: 'custom',
+ wallpaperHistory: [
+ { imageUrl: demoWallpaperUrl, wallpaper: 'custom' },
+ ],
+ wallpaperImageUrl: demoWallpaperUrl,
+ },
+ },
+ },
+ }
+ : deviceData
response.json({
success: true,
data: {
diff --git a/frontend/testserver/smoke.cjs b/frontend/testserver/smoke.cjs
index 73a8129..fb00c9e 100644
--- a/frontend/testserver/smoke.cjs
+++ b/frontend/testserver/smoke.cjs
@@ -109,6 +109,31 @@ function verifyBrowserTestData(dataByEndpoint) {
'camera captures',
2,
)
+ expectItems(
+ development.device.data.apps.payload.claimedApps,
+ 'installed demo apps',
+ 41,
+ )
+ assert.equal(
+ new Set(development.device.data.apps.payload.claimedApps).size,
+ 41,
+ 'browser demo apps were not uniquely installed',
+ )
+ assert.equal(
+ development.device.data.settings.payload.settings.wallpaper,
+ 'custom',
+ 'browser demo home wallpaper was not configured',
+ )
+ assert.equal(
+ development.device.data.settings.payload.settings.lockWallpaper,
+ 'custom',
+ 'browser demo Lock Screen wallpaper was not configured',
+ )
+ assert.match(
+ development.device.data.settings.payload.settings.wallpaperImageUrl,
+ /sky-phone-demo-gradient\.png$/,
+ 'browser demo wallpaper image was not configured',
+ )
expectItems(development.memos, 'memos', 3)
expectItems(development.notes, 'notes', 4)
assert(