refactor(operator): derive env badge from hostname, not from user choice

A toggle-able env badge is a sticker, not a safety signal. Move env to
useEnv() which reads window.location.hostname:
  *.local / localhost → 'dev'
  *staging* → 'staging'
  everything else → 'prod' (safest default)

- New composable: apps/operator/composables/useEnv.ts
- Topbar reads useEnv() instead of useTweaks().env
- useTweaks loses the env field; hydrate strips it from stale
  localStorage payloads so old entries don't break
- TweaksPanel: env section removed (theme + density remain)
- Settings: env section removed from Appearance; added a read-only
  Environment row to the Profile card showing the detected env +
  hostname source ("auto-detected from operator.dezky.local")
This commit is contained in:
Ronni Baslund
2026-05-24 16:52:07 +02:00
parent 702fe9e134
commit c93865e187
5 changed files with 76 additions and 35 deletions
+13 -7
View File
@@ -1,21 +1,23 @@
// Cosmetic tweaks for the operator shell — theme (dark/light), density
// (comfy/compact), env badge (prod/staging/dev). Persisted in localStorage so
// Cosmetic user-controllable preferences for the operator shell — theme
// (dark/light) and density (comfy/compact). Persisted in localStorage so
// the choices survive page reloads. The values are applied to <html> as
// data-* attributes; tokens.css picks them up via selector overrides.
//
// NOTE: `env` used to live here but is now derived from the hostname via
// `useEnv()` so it's a real environment signal, not a sticker the operator
// can flip.
export type ThemeMode = 'dark' | 'light'
export type Density = 'comfy' | 'compact'
export type Env = 'prod' | 'staging' | 'dev'
interface TweakState {
theme: ThemeMode
density: Density
env: Env
}
const STORAGE_KEY = 'dezky-operator-tweaks'
const DEFAULTS: TweakState = { theme: 'dark', density: 'comfy', env: 'dev' }
const DEFAULTS: TweakState = { theme: 'dark', density: 'comfy' }
const state = ref<TweakState>({ ...DEFAULTS })
const hydrated = ref(false)
@@ -41,8 +43,13 @@ function hydrate() {
try {
const raw = localStorage.getItem(STORAGE_KEY)
if (raw) {
// Old payloads carried an `env` field — pick fields we still care about
// and ignore the rest so a stale localStorage entry doesn't break.
const parsed = JSON.parse(raw) as Partial<TweakState>
state.value = { ...DEFAULTS, ...parsed }
state.value = {
theme: parsed.theme ?? DEFAULTS.theme,
density: parsed.density ?? DEFAULTS.density,
}
}
} catch {
// ignore corrupt JSON
@@ -64,6 +71,5 @@ export const useTweaks = () => {
state,
setTheme: (v: ThemeMode) => set('theme', v),
setDensity: (v: Density) => set('density', v),
setEnv: (v: Env) => set('env', v),
}
}