Merge pull request #1130 from zeedif/feat/setup-wizard-ux

feat(setup-wizard): improve E2EE dialog UX and password field
This commit is contained in:
vorotamoroz
2026-09-02 23:54:25 +09:00
committed by GitHub
10 changed files with 238 additions and 79 deletions
@@ -2550,6 +2550,10 @@ export const allMessages: Readonly<Record<string, Readonly<Record<string, string
ko: "해당 없는 항목 숨기기",
"zh-tw": "隱藏不適用的項目",
},
"Hide password": {
def: "Hide password",
es: "Ocultar contraseña",
},
"Higher (${local} > ${remote})": {
def: "Higher (${local} > ${remote})",
es: "Superior (${local} > ${remote})",
@@ -9443,6 +9447,10 @@ export const allMessages: Readonly<Record<string, Readonly<Record<string, string
zh: "仅显示通知",
"zh-tw": "僅顯示通知",
},
"Show password": {
def: "Show password",
es: "Mostrar contraseña",
},
"Show status as icons only": {
def: "Show status as icons only",
es: "Mostrar estado solo con íconos",
+2
View File
@@ -308,6 +308,7 @@
"Hidden Files": "Hidden Files",
"Hide completely": "Hide completely",
"Hide not applicable items": "Hide not applicable items",
"Hide password": "Hide password",
"Higher (${local} > ${remote})": "Higher (${local} > ${remote})",
"Highlight diff": "Highlight diff",
"How to display network errors when the sync server is unreachable.": "How to display network errors when the sync server is unreachable.",
@@ -1026,6 +1027,7 @@
"Show history": "Show history",
"Show icon only": "Show icon only",
"Show only notifications": "Show only notifications",
"Show password": "Show password",
"Show status as icons only": "Show status as icons only",
"Show status icon instead of file warnings banner": "Show status icon instead of file warnings banner",
"Show status inside the editor": "Show status inside the editor",
+2
View File
@@ -309,6 +309,7 @@
"Hidden Files": "Archivos ocultos",
"Hide completely": "Ocultar por completo",
"Hide not applicable items": "Ocultar elementos no aplicables",
"Hide password": "Ocultar contraseña",
"Higher (${local} > ${remote})": "Superior (${local} > ${remote})",
"Highlight diff": "Resaltar las diferencias",
"How to display network errors when the sync server is unreachable.": "Cómo mostrar los errores de red cuando el servidor de sincronización no está disponible.",
@@ -1060,6 +1061,7 @@
"Show history": "Mostrar el historial",
"Show icon only": "Mostrar solo el icono",
"Show only notifications": "Mostrar solo notificaciones",
"Show password": "Mostrar contraseña",
"Show status as icons only": "Mostrar estado solo con íconos",
"Show status icon instead of file warnings banner": "Mostrar icono de estado en lugar del banner de advertencia de archivos",
"Show status inside the editor": "Mostrar estado dentro del editor",
+2
View File
@@ -409,6 +409,7 @@ Hidden file synchronization have been temporarily disabled. Please enable them a
Hidden Files: Hidden Files
Hide completely: Hide completely
Hide not applicable items: Hide not applicable items
Hide password: Hide password
Higher (${local} > ${remote}): Higher (${local} > ${remote})
Highlight diff: Highlight diff
How to display network errors when the sync server is unreachable.: How to display network errors when the sync server is unreachable.
@@ -1812,6 +1813,7 @@ Show full banner: Show full banner
Show history: Show history
Show icon only: Show icon only
Show only notifications: Show only notifications
Show password: Show password
Show status as icons only: Show status as icons only
Show status icon instead of file warnings banner: Show status icon instead of file warnings banner
Show status inside the editor: Show status inside the editor
+2
View File
@@ -432,6 +432,7 @@ Hidden file synchronization have been temporarily disabled. Please enable them a
Hidden Files: Archivos ocultos
Hide completely: Ocultar por completo
Hide not applicable items: Ocultar elementos no aplicables
Hide password: Ocultar contraseña
Higher (${local} > ${remote}): Superior (${local} > ${remote})
Highlight diff: Resaltar las diferencias
How to display network errors when the sync server is unreachable.:
@@ -1921,6 +1922,7 @@ Show full banner: Mostrar banner completo
Show history: Mostrar el historial
Show icon only: Mostrar solo el icono
Show only notifications: Mostrar solo notificaciones
Show password: Mostrar contraseña
Show status as icons only: Mostrar estado solo con íconos
Show status icon instead of file warnings banner: Mostrar icono de estado en lugar del banner de advertencia de archivos
Show status inside the editor: Mostrar estado dentro del editor
@@ -48,80 +48,94 @@
}
</script>
<DialogHeader title={translateMessage("End-to-End Encryption")} />
<Guidance>{translateMessage("Please configure your end-to-end encryption settings.")}</Guidance>
<InputRow label={translateMessage("End-to-End Encryption")}>
<input type="checkbox" bind:checked={encryptionSettings.encrypt} />
<Password
name="e2ee-passphrase"
placeholder={translateMessage("Enter your passphrase")}
bind:value={encryptionSettings.passphrase}
disabled={!encryptionSettings.encrypt}
required={encryptionSettings.encrypt}
/>
</InputRow>
<InfoNote title={translateMessage("Strongly Recommended")}>
{translateMessage(
"Enabling end-to-end encryption ensures that your data is encrypted on your device before being sent to the remote server. This means that even if someone gains access to the server, they won't be able to read your data without the passphrase. Make sure to remember your passphrase, as it will be required to decrypt your data on other devices."
)}
<br />
{translateMessage(
"Also, please note that if you are using Peer-to-Peer synchronization, this configuration will be used when you switch to other methods and connect to a remote server in the future."
)}
</InfoNote>
<InfoNote warning>
{translateMessage("This setting must be the same even when connecting to multiple synchronisation destinations.")}
</InfoNote>
<InputRow label={translateMessage("Obfuscate Properties")}>
<input
type="checkbox"
bind:checked={encryptionSettings.usePathObfuscation}
disabled={!encryptionSettings.encrypt}
/>
</InputRow>
<InfoNote>
{translateMessage(
"Obfuscating properties (e.g., path of file, size, creation and modification dates) adds an additional layer of security by making it harder to identify the structure and names of your files and folders on the remote server. This helps protect your privacy and makes it more difficult for unauthorized users to infer information about your data."
)}
</InfoNote>
<ExtraItems title={translateMessage("Advanced")}>
<InputRow label={translateMessage("Encryption Algorithm")}>
<select bind:value={encryptionSettings.E2EEAlgorithm} disabled={!encryptionSettings.encrypt}>
{#each Object.values(E2EEAlgorithms) as alg}
<option value={alg}>{E2EEAlgorithmNames[alg] ?? alg}</option>
{/each}
</select>
<div class="sls-e2ee-dialog">
<DialogHeader title={translateMessage("End-to-End Encryption")} />
<Guidance>{translateMessage("Please configure your end-to-end encryption settings.")}</Guidance>
<InputRow label={translateMessage("End-to-End Encryption")}>
<input type="checkbox" bind:checked={encryptionSettings.encrypt} />
</InputRow>
<InfoNote>
<InfoNote title={translateMessage("Strongly Recommended")}>
{translateMessage(
"In most cases, you should stick with the default algorithm (${algorithm}), This setting is only required if you have an existing Vault encrypted in a different format.",
{ algorithm: E2EEAlgorithmNames[DEFAULT_SETTINGS.E2EEAlgorithm] }
"Enabling end-to-end encryption ensures that your data is encrypted on your device before being sent to the remote server. This means that even if someone gains access to the server, they won't be able to read your data without the passphrase. Make sure to remember your passphrase, as it will be required to decrypt your data on other devices."
)}
<br />
{translateMessage(
"Also, please note that if you are using Peer-to-Peer synchronization, this configuration will be used when you switch to other methods and connect to a remote server in the future."
)}
</InfoNote>
{#if encryptionSettings.encrypt}
<InputRow label={translateMessage("Passphrase")}>
<Password
name="e2ee-passphrase"
placeholder={translateMessage("Enter your passphrase")}
bind:value={encryptionSettings.passphrase}
required
/>
</InputRow>
<InfoNote warning>
{translateMessage(
"This setting must be the same even when connecting to multiple synchronisation destinations."
)}
</InfoNote>
<InputRow label={translateMessage("Obfuscate Properties")}>
<input type="checkbox" bind:checked={encryptionSettings.usePathObfuscation} />
</InputRow>
<InfoNote>
{translateMessage(
"Obfuscating properties (e.g., path of file, size, creation and modification dates) adds an additional layer of security by making it harder to identify the structure and names of your files and folders on the remote server. This helps protect your privacy and makes it more difficult for unauthorized users to infer information about your data."
)}
</InfoNote>
{/if}
<ExtraItems title={translateMessage("Advanced")}>
<InputRow label={translateMessage("Encryption Algorithm")}>
<select bind:value={encryptionSettings.E2EEAlgorithm} disabled={!encryptionSettings.encrypt}>
{#each Object.values(E2EEAlgorithms) as alg}
<option value={alg}>{E2EEAlgorithmNames[alg] ?? alg}</option>
{/each}
</select>
</InputRow>
<InfoNote>
{translateMessage(
"In most cases, you should stick with the default algorithm (${algorithm}), This setting is only required if you have an existing Vault encrypted in a different format.",
{ algorithm: E2EEAlgorithmNames[DEFAULT_SETTINGS.E2EEAlgorithm] }
)}
</InfoNote>
<InfoNote warning>
{translateMessage(
"Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data."
)}
</InfoNote>
</ExtraItems>
<InfoNote warning>
{translateMessage(
"Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data."
)}
<p>
{translateMessage(
"Please be aware that the End-to-End Encryption passphrase is not validated until the synchronisation process actually commences. This is a security measure designed to protect your data."
)}
</p>
<p>
{translateMessage(
"Therefore, we ask that you exercise extreme caution when configuring server information manually. If an incorrect passphrase is entered, the data on the server will become corrupted."
)} <br /><br />
{translateMessage("Please understand that this is intended behaviour.")}
</p>
</InfoNote>
</ExtraItems>
<InfoNote warning>
<p>
{translateMessage(
"Please be aware that the End-to-End Encryption passphrase is not validated until the synchronisation process actually commences. This is a security measure designed to protect your data."
)}
</p>
<p>
{translateMessage(
"Therefore, we ask that you exercise extreme caution when configuring server information manually. If an incorrect passphrase is entered, the data on the server will become corrupted."
)} <br /><br />
{translateMessage("Please understand that this is intended behaviour.")}
</p>
</InfoNote>
<UserDecisions>
<Decision title={translateMessage("Proceed")} important disabled={!e2eeValid} commit={() => commit()} />
<Decision title={translateMessage("Cancel")} commit={() => setResult(TYPE_CANCELLED)} />
</UserDecisions>
</div>
<UserDecisions>
<Decision title={translateMessage("Proceed")} important disabled={!e2eeValid} commit={() => commit()} />
<Decision title={translateMessage("Cancel")} commit={() => setResult(TYPE_CANCELLED)} />
</UserDecisions>
<style>
.sls-e2ee-dialog {
display: flex;
flex-direction: column;
gap: 0.5em;
}
:global(.dialog-host .sls-e2ee-dialog label > span) {
width: auto;
min-width: 8em;
}
</style>
@@ -18,6 +18,7 @@
let showPassword = $state(false);
const type = $derived.by(() => (showPassword ? "text" : "password"));
const translatedPlaceholder = $derived.by(() => translate(placeholder));
const toggleLabel = $derived.by(() => translate(showPassword ? "Hide password" : "Show password"));
</script>
<input
@@ -31,4 +32,36 @@
autocorrect="off"
autocapitalize="off"
/>
<input type="checkbox" bind:checked={showPassword} />
<button
type="button"
class="sls-password-toggle"
aria-label={toggleLabel}
title={toggleLabel}
aria-pressed={showPassword}
{disabled}
onclick={() => (showPassword = !showPassword)}
>
👁️
</button>
<style>
.sls-password-toggle {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
padding: 4px;
margin-left: 4px;
background: transparent;
border: none;
box-shadow: none;
line-height: 1;
cursor: pointer;
}
.sls-password-toggle:hover {
background: var(--background-modifier-hover);
}
:global(body.is-mobile) .sls-password-toggle {
min-width: 44px;
}
</style>