diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue
index d37b0b8225..97344d9451 100644
--- a/apps/app-frontend/src/App.vue
+++ b/apps/app-frontend/src/App.vue
@@ -157,6 +157,11 @@ provideModrinthClient(tauriApiClient)
providePageContext({
hierarchicalSidebarAvailable: ref(true),
showAds: ref(false),
+ featureFlags: {
+ serverRamAsBytesAlwaysOn: computed(() =>
+ themeStore.getFeatureFlag('server_ram_as_bytes_always_on'),
+ ),
+ },
openExternalUrl: (url) => openUrl(url),
})
provideModalBehavior({
diff --git a/apps/app-frontend/src/store/theme.ts b/apps/app-frontend/src/store/theme.ts
index dbdd5a1196..ccf143a78f 100644
--- a/apps/app-frontend/src/store/theme.ts
+++ b/apps/app-frontend/src/store/theme.ts
@@ -6,6 +6,7 @@ export const DEFAULT_FEATURE_FLAGS = {
worlds_tab: false,
worlds_in_home: true,
server_project_qa: false,
+ server_ram_as_bytes_always_on: false,
i18n_debug: false,
}
diff --git a/apps/frontend/src/composables/featureFlags.ts b/apps/frontend/src/composables/featureFlags.ts
index a1acb5878c..be15100be2 100644
--- a/apps/frontend/src/composables/featureFlags.ts
+++ b/apps/frontend/src/composables/featureFlags.ts
@@ -38,6 +38,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
showProjectPageQuickServerButton: false,
newProjectGeneralSettings: false,
newProjectEnvironmentSettings: true,
+ serverRamAsBytesAlwaysOn: false,
archonSentryCapture: false,
hideRussiaCensorshipBanner: false,
disablePrettyProjectUrlRedirects: false,
diff --git a/apps/frontend/src/providers/setup/page-context.ts b/apps/frontend/src/providers/setup/page-context.ts
index 919a146c62..4ace7cffa1 100644
--- a/apps/frontend/src/providers/setup/page-context.ts
+++ b/apps/frontend/src/providers/setup/page-context.ts
@@ -1,12 +1,18 @@
import { provideModalBehavior, providePageContext } from '@modrinth/ui'
import { computed, ref } from 'vue'
+import { useFeatureFlags } from '~/composables/featureFlags.ts'
+
export function setupPageContextProvider() {
const cosmetics = useCosmetics()
+ const featureFlags = useFeatureFlags()
providePageContext({
hierarchicalSidebarAvailable: ref(false),
showAds: ref(false),
+ featureFlags: {
+ serverRamAsBytesAlwaysOn: computed(() => featureFlags.value.serverRamAsBytesAlwaysOn),
+ },
openExternalUrl: (url) => window.open(url, '_blank'),
})
provideModalBehavior({
diff --git a/packages/app-lib/src/state/settings.rs b/packages/app-lib/src/state/settings.rs
index 4b555bdeb9..6345e4c6a1 100644
--- a/packages/app-lib/src/state/settings.rs
+++ b/packages/app-lib/src/state/settings.rs
@@ -55,6 +55,7 @@ pub enum FeatureFlag {
ProjectBackground,
WorldsTab,
WorldsInHome,
+ ServerRamAsBytesAlwaysOn,
ServersInApp,
ServerProjectQa,
I18nDebug,
diff --git a/packages/ui/src/layouts/shared/server-settings/pages/general.vue b/packages/ui/src/layouts/shared/server-settings/pages/general.vue
index 756da0fbe6..7bedf006c3 100644
--- a/packages/ui/src/layouts/shared/server-settings/pages/general.vue
+++ b/packages/ui/src/layouts/shared/server-settings/pages/general.vue
@@ -89,12 +89,15 @@
{{ prefConfig.description }}
-
+
+ setPreferenceValue(key, !!value)"
+ />
+
@@ -138,11 +141,13 @@ import {
injectModrinthClient,
injectModrinthServerContext,
injectNotificationManager,
+ injectPageContext,
} from '#ui/providers'
const { addNotification } = injectNotificationManager()
const client = injectModrinthClient()
const { server: data, serverId, busyReasons } = injectModrinthServerContext()
+const { featureFlags } = injectPageContext()
const queryClient = useQueryClient()
const serverName = ref(data.value?.name)
@@ -207,6 +212,28 @@ const userPreferences = useStorage(
const newUserPreferences = ref(JSON.parse(JSON.stringify(userPreferences.value)))
+const isRamAsBytesForcedByFeatureFlag = computed(
+ () => featureFlags?.serverRamAsBytesAlwaysOn?.value ?? false,
+)
+
+const isPreferenceForcedByFeatureFlag = (key: string) =>
+ key === 'ramAsNumber' && isRamAsBytesForcedByFeatureFlag.value
+
+const getPreferenceTooltip = (key: string) =>
+ isPreferenceForcedByFeatureFlag(key)
+ ? 'Feature flag enabled to always show RAM as bytes.'
+ : undefined
+
+const getPreferenceValue = (key: string) =>
+ isPreferenceForcedByFeatureFlag(key) ? true : newUserPreferences.value[key as PreferenceKeys]
+
+const setPreferenceValue = (key: string, value: boolean) => {
+ if (isPreferenceForcedByFeatureFlag(key)) {
+ return
+ }
+ newUserPreferences.value[key as PreferenceKeys] = value
+}
+
// Info properties
const infoProperties = [
{ name: 'Server ID', value: serverId ?? 'Unknown' },
diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue b/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue
index 97b57d73ac..d27f8aece4 100644
--- a/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue
+++ b/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue
@@ -60,11 +60,12 @@ import { useStorage } from '@vueuse/core'
import { computed, defineAsyncComponent, ref, shallowRef, watch } from 'vue'
import { RouterLink } from 'vue-router'
-import { injectModrinthServerContext } from '#ui/providers'
+import { injectModrinthServerContext, injectPageContext } from '#ui/providers'
const VueApexCharts = defineAsyncComponent(() => import('vue3-apexcharts'))
const { serverId } = injectModrinthServerContext()
+const { featureFlags } = injectPageContext()
const props = withDefaults(
defineProps<{
@@ -83,6 +84,9 @@ const chartsReady = ref(new Set())
const userPreferences = useStorage(`pyro-server-${serverId || 'unknown'}-preferences`, {
ramAsNumber: false,
})
+const isRamAsBytesForcedByFeatureFlag = computed(
+ () => featureFlags?.serverRamAsBytesAlwaysOn?.value ?? false,
+)
const stats = shallowRef(
props.data?.current || {
@@ -214,7 +218,9 @@ const metrics = computed(() => {
{
title: 'Memory',
value:
- props.showMemoryAsBytes || userPreferences.value.ramAsNumber
+ props.showMemoryAsBytes ||
+ isRamAsBytesForcedByFeatureFlag.value ||
+ userPreferences.value.ramAsNumber
? formatBytes(stats.value.ram_usage_bytes ?? 0)
: `${ramPercent.value.toFixed(2)}%`,
icon: DatabaseIcon,
diff --git a/packages/ui/src/providers/page-context.ts b/packages/ui/src/providers/page-context.ts
index ddac323294..6dfd9156d8 100644
--- a/packages/ui/src/providers/page-context.ts
+++ b/packages/ui/src/providers/page-context.ts
@@ -6,6 +6,9 @@ export interface PageContext {
// pages may render sidebar content in #sidebar-teleport-target instead of in the main layout when true
hierarchicalSidebarAvailable: Ref
showAds: Ref
+ featureFlags?: {
+ serverRamAsBytesAlwaysOn?: Ref
+ }
openExternalUrl: (url: string) => void
}