FIX - add Chrome 103 rendering fallbacks

This commit is contained in:
DerEchteAlec
2026-08-12 18:59:48 +02:00
parent b54839ee74
commit 87ae9902bf
13 changed files with 188 additions and 54 deletions
+29 -10
View File
@@ -1,5 +1,22 @@
# sky_phone
## Payphones
Payphone dialing is validated against server-owned booth positions. Vanilla GTA V positions live
in `sky_phone/config/payphones.lua`; the server compares its own player coordinates with this list
and does not trust coordinates or models sent by the NUI or client.
For a custom map or MLO, add each booth to `Config.Payphones.Locations` in that server-only file:
```lua
{ model = "prop_phonebox_01a", coords = { x = 123.45, y = 678.90, z = 21.0 } },
```
The model must also be present in `Config.Payphones.Props`. Coordinates must be finite Lua numbers
inside the supported world bounds. Restart `sky_phone` after changing the list. If payphones are
enabled but the list is empty or contains invalid entries, the server logs a visible warning and
rejects calls that cannot be matched to a valid configured location.
## Custom Apps
`sky_phone` erkennt Registrierungen gestarteter Fremd-App-Ressourcen über integrierte
@@ -167,23 +184,25 @@ and restart the resource after updating the configuration.
- When `Config.Sim.Enabled = true`, two unique, non-stackable inventory items named `sky_phone_sim_registered` and `sky_phone_sim_anonymous`. Their metadata is initialized automatically on first use, so shops and crafting recipes add plain items without supplying a number. These item definitions are not required when SIM cards are disabled.
- `oxmysql` with MySQL/MariaDB.
- `pma-voice` when `Config.Calls.VoiceProvider` is set to `"pma"`.
- A FiveManage V3 Media API token for Camera photo/video uploads and Gallery deletion. Set the
server-only `Config.Media.FiveManage.ApiKey` in `sky_phone/config/media.lua`; the token is never
sent to NUI because clients receive temporary presigned upload URLs instead.
- A FiveManage V3 Media API token for Camera photo/video uploads and Gallery deletion. Set it as a
server-only convar; the token is never sent to NUI because clients receive temporary presigned
upload URLs instead:
```cfg
set sky_phone_fivemanage_api_key "replace-with-your-media-token"
```
- `yaca-voice`, `pma-voice`, or `saltychat` when the Radio app is enabled. `Config.Radio.VoiceProvider = "auto"` selects the first running provider in that order.
## Messages GIF provider
Configure GIF search in `sky_phone/config/config.lua`:
Configure GIF search as a server-only convar:
```lua
Config.Media.GiphyApiKey = "YOUR_GIPHY_API_KEY"
```cfg
set sky_phone_giphy_api_key "replace-with-your-giphy-api-key"
```
GIPHY provides trending and searched GIFs through a paginated server-side proxy. The shared
`config.lua` is loaded by both FiveM runtimes, so its values are available to clients even though
only the server uses the GIPHY key. Photo and video actions in Messages are intentionally inactive
until their dedicated implementation is available.
GIPHY provides trending and searched GIFs through a paginated server-side proxy. Only the server
reads the key. Photo and video actions in Messages use media captured by the Camera app.
Database migrations run automatically. Existing `sky_phone_mail_accounts` installations are renamed to `sky_phone_accounts` while preserving account IDs and mail foreign keys. The migration also creates `sky_phone_character_devices` for persistent non-unique phone mappings and marks automatic SIMs through `sky_phone_sims.is_virtual`. iFruit passwords are intentional in-character credentials and remain plaintext `VARCHAR(64)` values; registration screens warn players never to reuse a real password.
+47 -21
View File
@@ -14,9 +14,13 @@
.sim-picker {
position: relative;
width: min(58vh, 90vw);
max-height: 46vh;
overflow: clip;
max-width: calc(100vw - 32px);
max-height: calc(100vh - 32px);
min-height: 0;
overflow: hidden;
padding: 2.2vh;
display: flex;
flex-direction: column;
border: 0.1vh solid rgb(255 255 255 / 12%);
border-radius: 0.9vh;
background: #050505;
@@ -56,11 +60,16 @@
.sim-picker__header {
display: flex;
flex: 0 0 auto;
justify-content: space-between;
gap: 2vh;
margin-bottom: 1.6vh;
}
.sim-picker__header > div {
min-width: 0;
}
.sim-picker__header h1,
.sim-picker__confirmation h2 {
margin: 0;
@@ -73,6 +82,7 @@
margin: 0.45vh 0 0;
color: #9ca3af;
font-size: 1.15vh;
overflow-wrap: anywhere;
}
.sim-picker__close {
@@ -80,6 +90,8 @@
place-items: center;
width: 2.8vh;
height: 2.8vh;
min-width: 32px;
min-height: 32px;
border: 0;
border-radius: 0.35vh;
background: #1dd1ce;
@@ -96,10 +108,12 @@
position: relative;
z-index: 1;
display: grid;
flex: 1 1 auto;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1vh;
max-height: 34vh;
overflow: auto;
min-height: 0;
max-height: 54vh;
overflow-y: auto;
}
.sim-picker__card {
@@ -140,30 +154,34 @@
}
.sim-picker__details strong {
overflow: hidden;
font-size: 1.2vh;
text-overflow: ellipsis;
white-space: nowrap;
overflow-wrap: anywhere;
}
.sim-picker__details small {
color: #9ca3af;
font-size: 0.95vh;
overflow-wrap: anywhere;
}
.sim-picker__confirmation {
min-height: 0;
overflow-y: auto;
padding: 2vh 0 0;
text-align: center;
}
.sim-picker__confirmation > div {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1vh;
margin-top: 2vh;
}
.sim-picker__confirmation button {
min-height: 40px;
overflow-wrap: anywhere;
padding: 0.8vh 1.6vh;
border: 0.1vh solid rgb(255 255 255 / 18%);
border-radius: 0.45vh;
@@ -184,6 +202,13 @@
color: #ff453a;
font-size: 1vh;
text-align: center;
overflow-wrap: anywhere;
}
@media (max-width: 640px) {
.sim-picker__cards {
grid-template-columns: 1fr;
}
}
:root {
font-family:
@@ -205,6 +230,7 @@ body,
margin: 0;
overflow: hidden;
background: transparent !important;
pointer-events: none;
user-select: none;
}
button,
@@ -885,13 +911,13 @@ button {
}
/* DarkChat is intentionally independent from the phone appearance setting. */
.phone-app:has(.darkchat-page),
.phone-app:has(.darkchat-page) .phone-app-window,
.phone-app:has(.darkchat-page) .phone-app-window__content {
.phone-app--darkchat,
.phone-app--darkchat .phone-app-window,
.phone-app--darkchat .phone-app-window__content {
background: #000 !important;
color-scheme: dark;
}
.phone-app:has(.darkchat-page) .phone-status-bar {
.phone-app--darkchat .phone-status-bar {
color: #fff !important;
--status-bar-color: #fff;
}
@@ -1592,7 +1618,7 @@ button {
font-size: 9px;
white-space: nowrap;
}
.darkchat-message:has(.darkchat-reactions) {
.darkchat-message {
margin-bottom: 12px;
}
.darkchat-replying {
@@ -3714,7 +3740,7 @@ button {
.clock-world-time {
margin-top: 8px;
font-variant-numeric: tabular-nums;
font-size: min(76px, 20cqw);
font-size: 74px;
font-weight: 200;
letter-spacing: -4px;
}
@@ -3861,7 +3887,7 @@ button {
.clock-digits {
margin: 70px 0 54px;
font-variant-numeric: tabular-nums;
font-size: min(52px, 14cqw);
font-size: 52px;
font-weight: 200;
}
.clock-sound-menu {
@@ -4242,11 +4268,11 @@ button {
background: #fff !important;
color: #111;
}
.phone-app:has(.messages-page) .phone-status-bar {
.phone-app--messages .phone-status-bar {
color: #080808;
text-shadow: none;
}
.phone-app:has(.messages-page)::before {
.phone-app--messages::before {
content: '';
position: absolute;
z-index: 60;
@@ -5458,7 +5484,7 @@ button {
}
}
/* iOS 26-style Liquid Glass refinements and complete media pickers. */
.phone-app:has(.messages-page) {
.messages-page {
--ios-glass: rgb(252 252 255 / 70%);
--ios-glass-strong: rgb(255 255 255 / 86%);
--ios-glass-border: rgb(255 255 255 / 72%);
@@ -5779,21 +5805,21 @@ button {
.messages-attachment--video small {
z-index: 2;
}
.messages-attachment--gif:has(img) {
.messages-attachment--gif {
background: #e9e9ed;
}
/* Konsta's k-app dark state drives the complete iOS Messages palette. */
.phone-app.dark:has(.messages-page) {
.phone-app.dark .messages-page {
--ios-glass: rgb(28 28 30 / 76%);
--ios-glass-strong: rgb(36 36 38 / 90%);
--ios-glass-border: rgb(255 255 255 / 12%);
color-scheme: dark;
}
.phone-app.dark:has(.messages-page)::before {
.phone-app--messages.dark::before {
background: #000;
}
.phone-app.dark:has(.messages-page) .phone-status-bar {
.phone-app--messages.dark .phone-status-bar {
color: #fff;
text-shadow: none;
}
@@ -447,6 +447,8 @@ watch(() => catalog.openRequests[props.app.id], flushOpenRequest, {
right: auto;
bottom: auto;
left: 50%;
width: 827px;
height: 368px;
width: 100cqh;
height: 100cqw;
transform: translate(-50%, -50%) rotate(90deg);
@@ -81,6 +81,7 @@ const initials = computed(() =>
<style scoped>
.message-contact-card {
width: min(258px, 100%);
width: min(258px, 72cqw);
overflow: hidden;
border: 1px solid rgb(255 255 255 / 48%);
@@ -93,6 +93,7 @@ watch(
<style scoped>
.shared-content-card {
width: min(262px, 100%);
width: min(262px, 73cqw);
overflow: hidden;
border: 1px solid rgb(60 60 67 / 13%);
@@ -367,4 +367,15 @@ function relativeTime(timestamp: number): string {
.feather-actions .is-bookmarked {
color: #438cf5;
}
@supports not (color: color-mix(in srgb, white, black)) {
.feather-post:active {
background: rgb(127 127 127 / 4%);
}
.feather-follow {
border-color: var(--feather-blue, #1d9bf0);
}
.feather-media {
border-color: rgb(127 127 127 / 18%);
}
}
</style>
+11
View File
@@ -85,6 +85,17 @@ describe('preferences', () => {
expect(value.settings.screenBrightness).toBe(10)
})
it('keeps the phone above the minimum usable scale', () => {
const value = parsePhonePreferences(
JSON.stringify({
version: 1,
settings: { phoneScale: 50 },
}),
)
expect(value.settings.phoneScale).toBe(75)
})
it('preserves safe notification preferences for custom apps', () => {
const appId = 'example-app' as LaunchablePhoneAppId
const value = parsePhonePreferences(
+12 -6
View File
@@ -23,7 +23,7 @@ export const PHONE_FRAME_IDS = [
export const RINGTONE_IDS = ['skyline', 'horizon', 'pulse'] as const
export const NOTIFICATION_SOUND_IDS = ['chime', 'signal', 'soft'] as const
export const WALLPAPER_IDS = ['midnight', 'aurora', 'ember'] as const
export const PHONE_SCALE_MIN = 50
export const PHONE_SCALE_MIN = 75
export const PHONE_SCALE_MAX = 150
export const PHONE_SCALE_STEP = 5
@@ -197,6 +197,10 @@ export function ensureAppNotificationPreferences(
}
}
export function clampPhoneScale(value: number): number {
return Math.min(PHONE_SCALE_MAX, Math.max(PHONE_SCALE_MIN, value))
}
export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
if (!raw) return cloneJsonData(DEFAULT_PHONE_PREFERENCES)
@@ -249,11 +253,13 @@ export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
100,
),
notifications: readNotifications(settings.notifications),
phoneScale: readNumber(
settings.phoneScale,
defaults.phoneScale,
PHONE_SCALE_MIN,
PHONE_SCALE_MAX,
phoneScale: clampPhoneScale(
readNumber(
settings.phoneScale,
defaults.phoneScale,
Number.MIN_SAFE_INTEGER,
Number.MAX_SAFE_INTEGER,
),
),
ringtone: readChoice(
settings.ringtone,
+25
View File
@@ -1404,6 +1404,31 @@ onBeforeUnmount(() => {
--k-button-bg-color: var(--billing-blue);
width: 100%;
}
@supports not (color: color-mix(in srgb, white, black)) {
.billing-navbar {
--k-navbar-bg-color: rgb(7 9 12 / 90%);
background: rgb(7 9 12 / 88%);
}
.billing-app--light .billing-navbar {
--k-navbar-bg-color: rgb(245 247 250 / 91%);
background: rgb(245 247 250 / 88%);
}
.billing-summary__item,
.billing-filter-panel,
.billing-invoice-card,
.billing-list-row,
.billing-search :deep(form),
.billing-detail__hero,
.billing-panel,
.billing-note {
background: var(--billing-panel);
}
.billing-detail__hero--paid {
background:
radial-gradient(circle at 50% 6%, rgb(72 199 111 / 18%), transparent 52%),
var(--billing-panel);
}
}
.billing-toast {
z-index: 50;
}
+2 -1
View File
@@ -50,7 +50,8 @@ const deleteActionColors = {
textMaterial: 'text-red-500',
}
const noteBodyStyle: CSSProperties = {
height: 'calc(100cqh - 210px)',
height: '617px',
maxHeight: 'calc(100% - 210px)',
resize: 'none',
}
const currentNote = computed(() =>
+40 -15
View File
@@ -348,6 +348,11 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.number-merge-header > div {
min-width: 0;
}
.number-merge-header span {
@@ -357,6 +362,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
font-weight: 800;
letter-spacing: 1.2px;
text-transform: uppercase;
overflow-wrap: anywhere;
}
.number-merge-header h1 {
@@ -364,6 +370,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
font-size: 28px;
line-height: 1;
letter-spacing: -1px;
overflow-wrap: anywhere;
}
.number-merge-header button,
@@ -383,11 +390,14 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
.number-merge-menu {
height: calc(100% - 55px);
min-height: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
justify-content: flex-start;
gap: 13px;
overflow-y: auto;
padding: 12px 0;
text-align: center;
}
@@ -401,6 +411,8 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
border-radius: 29px;
background: #7e5147;
box-shadow: 0 17px 30px rgb(93 48 36 / 20%);
flex: 0 0 auto;
margin-top: auto;
transform: rotate(-2deg);
}
@@ -418,8 +430,9 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
.number-merge-hero__tile--4 { color: #fff7e4; background: #e96c2c; }
.number-merge-hero__tile--8 { color: #fff7e4; background: #713c31; }
.number-merge-menu__intro h2 { margin: 0; font-size: 25px; letter-spacing: -0.5px; }
.number-merge-menu__intro p { max-width: 300px; margin: 6px 0 0; color: #8b675b; font-size: 14px; line-height: 1.45; }
.number-merge-menu__intro { width: 100%; }
.number-merge-menu__intro h2 { margin: 0; font-size: 25px; letter-spacing: -0.5px; overflow-wrap: anywhere; }
.number-merge-menu__intro p { max-width: 300px; margin: 6px auto 0; color: #8b675b; font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; }
.number-merge-records {
width: 100%;
@@ -429,6 +442,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
}
.number-merge-records div {
min-width: 0;
display: grid;
gap: 1px;
padding: 8px;
@@ -443,9 +457,10 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
overflow-wrap: anywhere;
}
.number-merge-records strong { font-size: 20px; }
.number-merge-records strong { min-width: 0; overflow-wrap: anywhere; font-size: 20px; }
.number-merge-menu__actions { width: 100%; display: grid; gap: 7px; }
@@ -453,11 +468,12 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
.number-merge-secondary,
.number-merge-danger {
min-height: 46px;
padding: 0 18px;
padding: 10px 18px;
border-radius: 14px;
font-size: 15px;
font-weight: 850;
cursor: pointer;
overflow-wrap: anywhere;
}
.number-merge-primary {
@@ -474,14 +490,17 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
}
.number-merge-how-to {
width: 100%;
flex: 0 0 auto;
margin-bottom: auto;
padding: 9px 15px;
border-radius: 14px;
color: #795549;
background: rgb(255 255 255 / 28%);
}
.number-merge-how-to strong { font-size: 13px; text-transform: uppercase; }
.number-merge-how-to p { margin: 5px 0 0; font-size: 12px; line-height: 1.4; }
.number-merge-how-to strong { font-size: 13px; text-transform: uppercase; overflow-wrap: anywhere; }
.number-merge-how-to p { margin: 5px 0 0; font-size: 12px; line-height: 1.4; overflow-wrap: anywhere; }
.number-merge-how-to div {
margin: 7px 0 4px;
color: #a7472d;
@@ -489,7 +508,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
font-weight: 900;
letter-spacing: -0.2px;
}
.number-merge-how-to small { display: block; color: #8b6559; font-size: 11px; line-height: 1.35; }
.number-merge-how-to small { display: block; color: #8b6559; font-size: 11px; line-height: 1.35; overflow-wrap: anywhere; }
.number-merge-game {
position: absolute;
@@ -629,7 +648,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
justify-content: flex-start;
gap: 8px;
padding: 22px;
border-radius: 20px;
@@ -637,12 +656,14 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
backdrop-filter: blur(5px);
color: #fff7ea;
text-align: center;
overflow-y: auto;
}
.number-merge-overlay > span { color: #ffc75b; font-size: 29px; font-weight: 900; }
.number-merge-overlay h2 { margin: 0; font-size: 25px; }
.number-merge-overlay p { margin: -3px 0 4px; color: #e7cabd; font-size: 14px; line-height: 1.4; }
.number-merge-overlay button { min-width: 160px; }
.number-merge-overlay > span { margin-top: auto; color: #ffc75b; font-size: 29px; font-weight: 900; }
.number-merge-overlay h2 { margin: 0; font-size: 25px; overflow-wrap: anywhere; }
.number-merge-overlay p { margin: -3px 0 4px; color: #e7cabd; font-size: 14px; line-height: 1.4; overflow-wrap: anywhere; }
.number-merge-overlay button { min-width: min(160px, 100%); }
.number-merge-overlay .number-merge-link { margin-bottom: auto; }
.number-merge-overlay .number-merge-secondary { color: #fff1df; background: rgb(255 255 255 / 8%); border-color: rgb(255 255 255 / 13%); }
.number-merge-link {
@@ -677,6 +698,8 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
inset: 0;
display: grid;
place-items: center;
min-height: 0;
overflow-y: auto;
padding: 30px;
background: rgb(54 29 25 / 54%);
backdrop-filter: blur(6px);
@@ -684,6 +707,8 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
.number-merge-confirm > div {
width: 100%;
max-height: calc(100% - 32px);
overflow-y: auto;
display: grid;
gap: 9px;
padding: 21px;
@@ -694,8 +719,8 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
text-align: center;
}
.number-merge-confirm h2 { margin: 0; font-size: 21px; }
.number-merge-confirm p { margin: 0 0 5px; color: #876359; font-size: 14px; line-height: 1.4; }
.number-merge-confirm h2 { margin: 0; font-size: 21px; overflow-wrap: anywhere; }
.number-merge-confirm p { margin: 0 0 5px; color: #876359; font-size: 14px; line-height: 1.4; overflow-wrap: anywhere; }
.number-merge-danger { border: 0; color: #fff; background: #b64f39; }
button:active { transform: scale(0.97); }
+1 -1
View File
@@ -3729,7 +3729,7 @@ onBeforeUnmount(() => {
box-shadow: inset 0 0 0 1px rgba(142, 142, 147, 0.18);
}
.phone-recent-row:has(button:hover),
.phone-recent-row:hover,
.phone-my-card:hover,
.phone-contact-row:hover {
border-radius: 14px;
@@ -2852,4 +2852,10 @@ onBeforeUnmount(() => {
transform: scale(1.28);
}
}
@supports not (color: color-mix(in srgb, white, black)) {
.ps-activity--unread {
background: rgb(10 132 255 / 10%);
}
}
</style>