Skip to content

feat(settings): add privacy filter editor#830

Merged
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:bob/privacy-filter-settings-ui
May 22, 2026
Merged

feat(settings): add privacy filter editor#830
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:bob/privacy-filter-settings-ui

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Contributor

Summary

  • add a dedicated settings panel for editing privacy_filters
  • validate the JSON rule shape client-side before save and keep server-side regex validation authoritative
  • include default example rules and focused unit coverage for the parser/validator helpers

Why

aw-server-rust#600 shipped the server-side privacy filter engine, but users still had no discoverable way to configure privacy_filters from aw-webui.

Testing

  • npm test -- privacyFilters.test.node.ts --runInBand
  • npm run lint
  • npm run build

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

❌ Patch coverage is 84.84848% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.10%. Comparing base (475442e) to head (6ff6c26).

Files with missing lines Patch % Lines
src/util/privacyFilters.ts 84.84% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #830      +/-   ##
==========================================
+ Coverage   32.46%   34.10%   +1.64%     
==========================================
  Files          35       36       +1     
  Lines        2039     2105      +66     
  Branches      362      386      +24     
==========================================
+ Hits          662      718      +56     
- Misses       1356     1366      +10     
  Partials       21       21              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

This PR adds a dedicated Settings panel for editing privacy_filters — a JSON textarea editor that validates rule shape client-side before calling the existing settingsStore.update path, with server-side regex validation kept as the authority.

  • src/util/privacyFilters.ts introduces PrivacyFilterRule, validatePrivacyFiltersInput, and formatPrivacyFilters helpers; the validator reports per-field errors and rejects structurally invalid rules before they reach the server.
  • src/views/settings/PrivacyFilterSettings.vue wires the textarea to the store with unsaved-change tracking, discard/save buttons, and a live validation alert; src/stores/settings.ts adds the privacy_filters: PrivacyFilterRule[] state key; src/views/settings/Settings.vue registers the new component between ActivePatternSettings and CategorizationSettings.

Confidence Score: 5/5

The change is safe to merge; all writes go through the existing store update path and no data is silently mutated on load.

The store integration is straightforward and the validation logic is sound. The two findings are minor UX polish items — a duplicate error message and an unguarded textarea during save — neither of which causes incorrect data to be written or read.

src/util/privacyFilters.ts (duplicate error for redact + empty replacement) and src/views/settings/PrivacyFilterSettings.vue (textarea not disabled while saving).

Important Files Changed

Filename Overview
src/util/privacyFilters.ts New validator and formatter helpers; logic is correct but emits duplicate errors when a redact rule has an empty (rather than absent) replacement string.
src/views/settings/PrivacyFilterSettings.vue New settings component; save/discard/unsaved-changes flow is correct, but the textarea remains editable while a save is in-flight, so mid-save edits are silently discarded when syncFromStore(true) runs on success.
src/stores/settings.ts Adds privacy_filters: PrivacyFilterRule[] to state with a sane [] default; integrates cleanly with the existing load/save/update infrastructure.
src/views/settings/Settings.vue Adds PrivacyFilterSettings between ActivePatternSettings and CategorizationSettings with correct import and component registration.
test/unit/privacyFilters.test.node.ts Good coverage of the key paths (missing field, blank field, invalid action, empty pattern, redact-without-replacement); the redact-with-empty-replacement double-error case is not covered.

Sequence Diagram

sequenceDiagram
    participant User
    participant PrivacyFilterSettings
    participant validatePrivacyFiltersInput
    participant SettingsStore
    participant Server

    User->>PrivacyFilterSettings: types JSON in textarea
    PrivacyFilterSettings->>validatePrivacyFiltersInput: validate(editorText) [computed]
    validatePrivacyFiltersInput-->>PrivacyFilterSettings: "{ rules, errors }"
    Note over PrivacyFilterSettings: canSave = hasUnsavedChanges && errors.length===0 && !isSaving

    User->>PrivacyFilterSettings: clicks Save
    PrivacyFilterSettings->>SettingsStore: "update({ privacy_filters: rules })"
    SettingsStore->>SettingsStore: $patch(new_state)
    SettingsStore-->>PrivacyFilterSettings: watcher fires syncFromStore(false)
    SettingsStore->>Server: POST /0/settings/privacy_filters
    Server-->>SettingsStore: 200 OK
    SettingsStore->>Server: GET /0/settings (reload)
    Server-->>SettingsStore: updated settings
    SettingsStore-->>PrivacyFilterSettings: update() resolves
    PrivacyFilterSettings->>PrivacyFilterSettings: "syncFromStore(true) — reset editorText & savedText"

    User->>PrivacyFilterSettings: clicks Discard
    PrivacyFilterSettings->>PrivacyFilterSettings: syncFromStore(true) — revert to store value
Loading

Reviews (6): Last reviewed commit: "fix(settings): avoid duplicate field val..." | Re-trigger Greptile

Comment thread src/util/privacyFilters.ts Outdated
Comment on lines +6 to +9
field?: string;
pattern: string;
action: PrivacyFilterAction;
replacement?: string;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Type/validator mismatch blocks editing of existing server configs

field is declared optional (field?: string) in PrivacyFilterRule, meaning the server schema accepts rules without it. The validator, however, treats field as required and pushes an error on line 124 when it's absent. If a user already has server-side privacy-filter rules without a field (written via a different client or an older config), the editor will load and display them correctly, but clicking Save will fail with "field is required" — making those rules impossible to round-trip through this UI. Either change the type to field: string to signal the restriction everywhere, or drop the required check from the validator and let the server enforce its own rules.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0b6fb9c: field is now declared as a required field: string in the PrivacyFilterRule interface, matching the validator's required check. Server-side rules without a field would be schema-invalid, so making it required in the type is the right call.

Comment on lines +103 to +106
const pattern = candidate.pattern;
if (typeof pattern !== 'string') {
errors.push(`Rule ${ruleNumber}: \`pattern\` must be a string.`);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Empty pattern strings pass client-side validation (only the type is checked, not the value), so a user can save {"pattern": ""} — a rule that silently matches every event. Adding a non-empty check here catches the mistake before the server applies the rule.

Suggested change
const pattern = candidate.pattern;
if (typeof pattern !== 'string') {
errors.push(`Rule ${ruleNumber}: \`pattern\` must be a string.`);
}
const pattern = candidate.pattern;
if (typeof pattern !== 'string') {
errors.push(`Rule ${ruleNumber}: \`pattern\` must be a string.`);
} else if (pattern.trim() === '') {
errors.push(`Rule ${ruleNumber}: \`pattern\` cannot be an empty string.`);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0b6fb9c: added else if (pattern.trim() === '') check so an empty pattern string is rejected with a clear error instead of silently passing through.

Comment on lines +117 to +127
const action = candidate.action;
if (action !== 'drop' && action !== 'redact') {
errors.push(`Rule ${ruleNumber}: \`action\` must be either "drop" or "redact".`);
return;
}

if (field === undefined) {
errors.push(
`Rule ${ruleNumber}: \`field\` is required so the rule only matches the intended event data.`
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Invalid action early-return suppresses the field required error

When action fails validation the callback returns early (line 120), so the field === undefined check at line 123 never runs. A rule that has both an invalid action and a missing field will only surface one error to the user — they fix the action, then have to save again to discover the missing field. The early return is needed to avoid a redundant action === 'redact' check, but the field guard could be moved above it to ensure all errors for a rule are reported in one pass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c4a33c8: moved the early-return guard to the end of all per-field checks so both an invalid action and a missing field are reported in the same pass. No more needing two save attempts to see all errors.

@TimeToBuildBob
Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob
Copy link
Copy Markdown
Contributor Author

CI is green and the Greptile findings are addressed. I tried to merge this, but the current token does not have MergePullRequest permission, so this needs a maintainer merge.

@TimeToBuildBob TimeToBuildBob force-pushed the bob/privacy-filter-settings-ui branch from c4a33c8 to 6ff6c26 Compare May 22, 2026 10:07
@TimeToBuildBob
Copy link
Copy Markdown
Contributor Author

@greptileai review

@ErikBjare ErikBjare merged commit 6516230 into ActivityWatch:master May 22, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants