Add autofill privacy settings controls

This commit is contained in:
Joe Julian
2026-04-01 17:58:37 -07:00
parent 07c277f425
commit 2e03db85a1
3 changed files with 368 additions and 2 deletions
+99 -2
View File
@@ -127,6 +127,7 @@ type statePaths struct {
DefaultSaveAsPath string
RecentVaultsPath string
RecentRemotesPath string
SettingsPath string
UIPreferencesPath string
AutofillCachePath string
}
@@ -150,6 +151,8 @@ type uiPreferences struct {
GroupControlsHidden bool `json:"groupControlsHidden"`
LifecycleAdvancedHidden bool `json:"lifecycleAdvancedHidden"`
HistoryHidden bool `json:"historyHidden"`
SyncSourceDefault string `json:"syncSourceDefault,omitempty"`
SyncDirectionDefault string `json:"syncDirectionDefault,omitempty"`
DenseLayout bool `json:"denseLayout"`
StatusBannerMillis int `json:"statusBannerMillis,omitempty"`
AutofillNoticeMode string `json:"autofillNoticeMode,omitempty"`
@@ -271,6 +274,10 @@ type ui struct {
settingsReducedMotionOn widget.Clickable
settingsKeyboardFocusStandard widget.Clickable
settingsKeyboardFocusProminent widget.Clickable
showSettingsSyncLocal widget.Clickable
showSettingsSyncRemote widget.Clickable
showSettingsSyncPull widget.Clickable
showSettingsSyncPush widget.Clickable
editEntry widget.Clickable
cancelEdit widget.Clickable
pickVaultPath widget.Clickable
@@ -402,10 +409,13 @@ type ui struct {
keyboardFocus focusID
defaultSaveAsPath string
recentVaultsPath string
settingsPath string
uiPreferencesPath string
recentRemotesPath string
autofillCachePath string
editingEntry bool
syncDefaultSourceMode syncSourceMode
syncDefaultDirection syncDirection
groupControlsHidden bool
lifecycleAdvancedHidden bool
historyHidden bool
@@ -547,6 +557,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
lifecycleMode: "local",
defaultSaveAsPath: paths.DefaultSaveAsPath,
recentVaultsPath: paths.RecentVaultsPath,
settingsPath: paths.SettingsPath,
uiPreferencesPath: paths.UIPreferencesPath,
recentRemotesPath: paths.RecentRemotesPath,
autofillCachePath: paths.AutofillCachePath,
@@ -560,6 +571,8 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
now: time.Now,
syncSourceMode: syncSourceLocal,
syncDirection: syncDirectionPull,
syncDefaultSourceMode: syncSourceLocal,
syncDefaultDirection: syncDirectionPull,
apiPolicyGroupScope: true,
autofillNoticePreference: autofillNoticeAll,
backgroundResults: make(chan backgroundActionResult, 8),
@@ -585,6 +598,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
u.loadRecentRemotes()
u.restoreStartupLifecycleTarget()
u.loadUIPreferences()
u.loadSettings()
u.loadSettingsFormFromPreferences()
u.loadSettingsDraft()
u.filter()
@@ -621,6 +635,7 @@ func defaultStatePaths(stateDir string) statePaths {
DefaultSaveAsPath: filepath.Join(baseDir, "vault.kdbx"),
RecentVaultsPath: filepath.Join(baseDir, "recent-vaults.json"),
RecentRemotesPath: filepath.Join(baseDir, "recent-remotes.json"),
SettingsPath: filepath.Join(baseDir, "settings.json"),
UIPreferencesPath: filepath.Join(baseDir, "ui-prefs.json"),
AutofillCachePath: filepath.Join(baseDir, "autofill-cache.json"),
}
@@ -1093,11 +1108,31 @@ func (u *ui) openAdvancedSyncDialog() {
u.syncDialogOpen = true
u.syncMenuOpen = false
u.showSyncPassword = false
u.syncSourceMode = u.syncDefaultSourceMode
u.syncDirection = u.syncDefaultDirection
if strings.TrimSpace(u.syncLocalPath.Text()) == "" {
u.syncLocalPath.SetText(strings.TrimSpace(u.vaultPath.Text()))
}
}
func sanitizeSyncSourceMode(mode syncSourceMode) syncSourceMode {
switch mode {
case syncSourceRemote:
return syncSourceRemote
default:
return syncSourceLocal
}
}
func sanitizeSyncDirection(direction syncDirection) syncDirection {
switch direction {
case syncDirectionPush:
return syncDirectionPush
default:
return syncDirectionPull
}
}
func (u *ui) advancedSyncAction() error {
switch u.syncDirection {
case syncDirectionPush:
@@ -2728,6 +2763,18 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
for u.showSyncPush.Clicked(gtx) {
u.syncDirection = syncDirectionPush
}
for u.showSettingsSyncLocal.Clicked(gtx) {
u.settingsDraft.Sync.SourceDefault = syncSourceLocal
}
for u.showSettingsSyncRemote.Clicked(gtx) {
u.settingsDraft.Sync.SourceDefault = syncSourceRemote
}
for u.showSettingsSyncPull.Clicked(gtx) {
u.settingsDraft.Sync.DirectionDefault = syncDirectionPull
}
for u.showSettingsSyncPush.Clicked(gtx) {
u.settingsDraft.Sync.DirectionDefault = syncDirectionPush
}
for u.showAutofillApprovalAsk.Clicked(gtx) {
u.autofillFirstFillApprovalMode = autofillFirstFillApprovalAsk
}
@@ -3210,7 +3257,7 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(14), "Choose how KeePassGO remembers UI layout behavior, tunes noncritical feedback, and sets the KDBX security defaults used for new or future saves.")
lbl := material.Label(u.theme, unit.Sp(14), "Choose how KeePassGO remembers UI layout behavior, sync defaults, and KDBX security defaults without crowding the main vault flow.")
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
@@ -3248,6 +3295,56 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(labeledEditorHelp(u.theme, "KDF", "Supported values: "+strings.Join([]string{vault.KDFAES, vault.KDFArgon2}, ", "), &u.securityKDF, false)),
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
layout.Rigid(syncDialogSectionLabel(u.theme, "Sync Defaults")),
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(13), "Advanced Sync starts from these defaults. You can still change the source or direction before a single run.")
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncPull, "Pull Into Current Vault", u.settingsDraft.Sync.DirectionDefault == syncDirectionPull)
}),
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncPush, "Push Current Vault Out", u.settingsDraft.Sync.DirectionDefault == syncDirectionPush)
}),
)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncLocal, "Local File", u.settingsDraft.Sync.SourceDefault == syncSourceLocal)
}),
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncRemote, "Remote WebDAV", u.settingsDraft.Sync.SourceDefault == syncSourceRemote)
}),
)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return syncDialogSummaryCard(gtx, u.theme, u.settingsDraft.Sync.SourceDefault, u.settingsDraft.Sync.DirectionDefault)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(12), "Conflict handling stays retry-safe: merged entry changes keep history, while remote save conflicts still require reopening the vault and retrying the save.")
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
layout.Rigid(syncDialogSectionLabel(u.theme, "Background Sync")),
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(12), "Future background sync controls belong here so source, direction, and unattended behavior stay in one settings surface.")
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
layout.Rigid(syncDialogSectionLabel(u.theme, "Feedback")),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
@@ -3451,7 +3548,7 @@ func (u *ui) syncDialogContent(gtx layout.Context) layout.Dimensions {
}),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(14), "Pick direction, choose the other vault, and then run the merge. The quick sync button keeps using the current source.")
lbl := material.Label(u.theme, unit.Sp(14), "Pick direction, choose the other vault, and then run the merge. Saved source and direction defaults now live in Settings.")
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),