diff --git a/client_v3/src/common/ui/Dialog/Dialog.pcss b/client_v3/src/common/ui/Dialog/Dialog.pcss
index 3f603e9c7..60472a1a3 100644
--- a/client_v3/src/common/ui/Dialog/Dialog.pcss
+++ b/client_v3/src/common/ui/Dialog/Dialog.pcss
@@ -47,12 +47,12 @@
border: none;
border-radius: 16px;
background-clip: border-box;
- padding: 8px 0;
+ padding: 16px 0;
color: var(--default-main-text);
text-align: left;
overflow: auto;
- @media (min-width: 1024px) {
+ @media (min-width: 768px) {
border-radius: 16px;
padding: 24px 16px;
}
diff --git a/client_v3/src/components/Encryption/Encryption.tsx b/client_v3/src/components/Encryption/Encryption.tsx
index 35fff512a..36280d781 100644
--- a/client_v3/src/components/Encryption/Encryption.tsx
+++ b/client_v3/src/components/Encryption/Encryption.tsx
@@ -34,6 +34,22 @@ export const Encryption = () => {
const [tlsStatusLoaded, setTlsStatusLoaded] = createSignal(false);
+ /**
+ * Shadows encryptionState.enabled with {@code equals: false} so we can
+ * force a DOM re-sync even when the value is unchanged (e.g. reverting
+ * after a modal opens without saving). Synced from store only when
+ * {@code processingConfig} is false to avoid flashing during async save.
+ */
+ const [encryptionEnabled, setEncryptionEnabled] = createSignal(false, {
+ equals: false,
+ });
+
+ createEffect(() => {
+ if (!encryptionState.processingConfig) {
+ setEncryptionEnabled(encryptionState.enabled);
+ }
+ });
+
const [validateConfig, cancelValidation] = createDebouncedValidator();
onMount(async () => {
@@ -49,9 +65,9 @@ export const Encryption = () => {
!!(encryptionState.certificate_chain || encryptionState.certificate_path);
const handleEncryptedDnsChange = (checked: boolean) => {
- resetValidationStatus();
-
if (!checked) {
+ setEncryptionEnabled(false);
+ resetValidationStatus();
setTlsConfig(
{
enabled: false,
@@ -72,6 +88,8 @@ export const Encryption = () => {
const hasServerName = !!encryptionState.server_name;
// Everything is set up — save the change.
+ // Native input already shows ON from the click; sync effect
+ // confirms on success or reverts on failure.
if (hasCert && hasKey && hasServerName) {
setTlsConfig(
{
@@ -82,6 +100,10 @@ export const Encryption = () => {
return;
}
+ // Not saving — force DOM back to unchecked so the switch
+ // doesn't appear ON while encryption is actually OFF.
+ setEncryptionEnabled(false);
+
// Certificate or key is missing — open the TLS cert wizard (don't save yet).
if (!hasCert || !hasKey) {
setAddCertOpen(true);
@@ -174,7 +196,7 @@ export const Encryption = () => {
}
>
@@ -195,7 +217,7 @@ export const Encryption = () => {
variant="switch"
title={intl.getMessage('encryption_encrypted_dns')}
description={intl.getMessage('encryption_encrypted_dns_desc')}
- checked={encryptionState.enabled}
+ checked={encryptionEnabled()}
disabled={encryptionState.processingConfig}
onChange={handleEncryptedDnsChange}
/>
diff --git a/client_v3/src/components/Encryption/blocks/PlainDnsToggle.tsx b/client_v3/src/components/Encryption/blocks/PlainDnsToggle.tsx
index 8d5a1039b..468aba895 100644
--- a/client_v3/src/components/Encryption/blocks/PlainDnsToggle.tsx
+++ b/client_v3/src/components/Encryption/blocks/PlainDnsToggle.tsx
@@ -6,7 +6,9 @@ import { encryptionState, setTlsConfig } from 'panel/stores/encryption';
export const PlainDnsToggle = () => {
const [confirmDisable, setConfirmDisable] = createSignal(false);
- const [localChecked, setLocalChecked] = createSignal(encryptionState.serve_plain_dns);
+ const [localChecked, setLocalChecked] = createSignal(encryptionState.serve_plain_dns, {
+ equals: false,
+ });
const enc = () => encryptionState;
// Sync localChecked from store when the dialog is not open,
@@ -31,8 +33,9 @@ export const PlainDnsToggle = () => {
setTlsConfig({ serve_plain_dns: true });
return;
}
- // Don't update localChecked — the switch stays visually on
- // until the user confirms in the modal.
+ // Force DOM re-sync before opening the modal. With equals:false
+ // this re-applies checked={true} to the native input.
+ setLocalChecked((v) => v);
setConfirmDisable(true);
};
diff --git a/client_v3/src/components/Encryption/blocks/TlsCertSection.tsx b/client_v3/src/components/Encryption/blocks/TlsCertSection.tsx
index 84f2aa2cf..f32b47af8 100644
--- a/client_v3/src/components/Encryption/blocks/TlsCertSection.tsx
+++ b/client_v3/src/components/Encryption/blocks/TlsCertSection.tsx
@@ -1,7 +1,7 @@
import { Show } from 'solid-js';
import { Icon } from 'panel/common/ui/Icon';
import intl from 'panel/common/intl';
-import { encryptionState, setTlsConfig, resetValidationStatus } from 'panel/stores/encryption';
+import { encryptionState, setTlsConfig, resetValidationStatus, clearCertOptimistically } from 'panel/stores/encryption';
import { CertificateStatus, KeyStatus, ValidationStatus } from '../Status';
import s from '../styles.module.pcss';
import theme from 'panel/lib/theme';
@@ -10,6 +10,8 @@ export const TlsCertSection = () => {
const enc = () => encryptionState;
const removeCert = () => {
+ clearCertOptimistically();
+ resetValidationStatus();
setTlsConfig({
enabled: false,
serve_plain_dns: true,
@@ -19,7 +21,6 @@ export const TlsCertSection = () => {
private_key_path: '',
private_key_saved: false,
});
- resetValidationStatus();
};
const renderStatus = () => {
diff --git a/client_v3/src/install/Setup/Auth.tsx b/client_v3/src/install/Setup/Auth.tsx
index 2bc0313fc..a2fe4d978 100644
--- a/client_v3/src/install/Setup/Auth.tsx
+++ b/client_v3/src/install/Setup/Auth.tsx
@@ -114,6 +114,7 @@ export const Auth = (props: Props) => {
autocomplete="username"
value={(field.value as string) ?? ''}
errorMessage={field.error as string}
+ size="large"
/>
)}
@@ -146,6 +147,7 @@ export const Auth = (props: Props) => {
})
}
errorMessage={field.error as string}
+ size="large"
/>
)}
@@ -175,6 +177,7 @@ export const Auth = (props: Props) => {
})
}
errorMessage={field.error as string}
+ size="large"
/>
)}
diff --git a/client_v3/src/install/Setup/blocks/Banner/DnsBanner.tsx b/client_v3/src/install/Setup/blocks/Banner/DnsBanner.tsx
index dcb548f96..88f7c980d 100644
--- a/client_v3/src/install/Setup/blocks/Banner/DnsBanner.tsx
+++ b/client_v3/src/install/Setup/blocks/Banner/DnsBanner.tsx
@@ -86,6 +86,7 @@ export const DnsBanner = (props: Props) => {
const { value } = e.target as HTMLInputElement;
props.setDnsPort(toNumber(value));
}}
+ size="large"
/>
diff --git a/client_v3/src/install/Setup/blocks/Banner/WebBanner.tsx b/client_v3/src/install/Setup/blocks/Banner/WebBanner.tsx
index add8a25a1..ec617a6e5 100644
--- a/client_v3/src/install/Setup/blocks/Banner/WebBanner.tsx
+++ b/client_v3/src/install/Setup/blocks/Banner/WebBanner.tsx
@@ -47,7 +47,9 @@ export const WebBanner = (props: Props) => {
{intl.getMessage('setup_ui_title_banner')}