diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index dab71302dea..74ca445b8b0 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -633,7 +633,31 @@ export function renderApp(state: AppViewState) { overviewLogLines: state.overviewLogLines, showGatewayToken: state.overviewShowGatewayToken, showGatewayPassword: state.overviewShowGatewayPassword, - onSettingsChange: (next) => state.applySettings(next), + vncConfigDirty: state.vncConfigDirty, + onSettingsChange: (next) => { + // Only mark dirty if VNC fields changed + const vncChanged = + next.vncWsUrl !== state.settings.vncWsUrl || + next.vncPassword !== state.settings.vncPassword || + next.vncTarget !== state.settings.vncTarget; + + if (vncChanged) { + state.vncConfigDirty = true; + } + + // For VNC fields, we update local state but don't persist immediately + // This allows the Save button to be the trigger for persistence + // For other fields, we persist immediately as before + if (vncChanged) { + state.settings = next; + } else { + state.applySettings(next); + } + }, + onSaveVncConfig: () => { + state.applySettings(state.settings); + state.vncConfigDirty = false; + }, onPasswordChange: (next) => (state.password = next), onSessionKeyChange: (next) => { state.sessionKey = next; diff --git a/ui/src/ui/app-view-state.ts b/ui/src/ui/app-view-state.ts index ea61387d21b..9cd16af56a5 100644 --- a/ui/src/ui/app-view-state.ts +++ b/ui/src/ui/app-view-state.ts @@ -298,6 +298,7 @@ export type AppViewState = { streamMode: boolean; overviewShowGatewayToken: boolean; overviewShowGatewayPassword: boolean; + vncConfigDirty: boolean; overviewLogLines: string[]; overviewLogCursor: number; client: GatewayBrowserClient | null; diff --git a/ui/src/ui/views/overview.ts b/ui/src/ui/views/overview.ts index 8bbca92f099..0108f438713 100644 --- a/ui/src/ui/views/overview.ts +++ b/ui/src/ui/views/overview.ts @@ -48,7 +48,9 @@ export type OverviewProps = { overviewLogLines: string[]; showGatewayToken: boolean; showGatewayPassword: boolean; + vncConfigDirty?: boolean; onSettingsChange: (next: UiSettings) => void; + onSaveVncConfig?: () => void; onPasswordChange: (next: string) => void; onSessionKeyChange: (next: string) => void; onToggleGatewayTokenVisibility: () => void; @@ -325,7 +327,7 @@ export function renderOverview(props: OverviewProps) {