diff --git a/docs/src/content/docs/rules.md b/docs/src/content/docs/rules.md index 773affe..2d00cff 100644 --- a/docs/src/content/docs/rules.md +++ b/docs/src/content/docs/rules.md @@ -3,16 +3,16 @@ title: Rules reference description: The defense rules shipped with agent-browser-shield, what each one does, and its default state. --- -The extension ships 35 rules, each independently toggleable from the extension -popup. Rules marked **default: on** are active on fresh install; **default: -off** rules must be enabled manually. +The extension ships 35 rules, each independently toggleable from the extension's +options page. Rules marked **default: on** are active on fresh install; +**default: off** rules must be enabled manually. Rules marked **top frame only** never run inside iframes — useful for page-wide targets (footers, cookie overlays, URL recipes) so they don't fire pointlessly in every embedded frame. -This page groups rules by the threat or pattern they defend against. The popup -itself shows a flat list; rule IDs here match the filenames in +This page groups rules by the threat or pattern they defend against, the same +grouping the options page uses; rule IDs here match the filenames in [`extension/src/rules/`](https://github.com/pixiebrix/agent-browser-shield/tree/main/extension/src/rules), which is the authoritative source for behavior. Initial enabled/disabled state for each rule lives in @@ -847,7 +847,7 @@ reaches — applied to SVG `` definitions at runtime. - **Scope:** top frame only - **Availability:** requires an OpenAI API key — either bundled at build time via `OPENAI_API_KEY`, or saved on the extension's options page. Until a key is - configured the rule shows as Unavailable in the popup and options. + configured the rule shows as Unavailable on the options page. Use a small LLM to identify engagement/exploration rails (related products, "you might also like", recommended articles, trending now, etc.) and replace them diff --git a/extension/src/lib/RuleList.tsx b/extension/src/lib/RuleList.tsx index e068d1f..df1b55c 100644 --- a/extension/src/lib/RuleList.tsx +++ b/extension/src/lib/RuleList.tsx @@ -21,34 +21,21 @@ function ruleById(id: RuleId): Rule { return rule; } -// Shared per-rule checkbox list rendered by both the popup and options page. -// Rules are grouped by the same top-level threat/pattern categories used on -// the docs Rules reference page; the catalog test enforces every rule belongs -// to exactly one group. -// -// Pass `disabledByEnforcement` when the surface should grey out every rule -// (popup, when global enforcement is off); the options page omits it because -// the user can still edit preferences while enforcement is paused. +// Per-rule checkbox list rendered on the options page. Rules are grouped by +// the same top-level threat/pattern categories used on the docs Rules reference +// page; the catalog test enforces every rule belongs to exactly one group. export function RuleList({ states, availability, - disabledByEnforcement = false, className, }: { states: RuleStates; availability: RuleAvailabilityStates; - disabledByEnforcement?: boolean; className?: string; }) { const rootClass = className ? `rule-groups ${className}` : "rule-groups"; return ( -
+
{RULE_GROUPS.map((group) => (

{group.label}

@@ -57,7 +44,6 @@ export function RuleList({ const rule = ruleById(ruleId); const snapshot = availability[rule.id]; const unavailable = !snapshot?.available; - const disabled = unavailable || disabledByEnforcement; return (
  • { void setRuleEnabled(rule.id, event.target.checked); }} diff --git a/extension/src/popup.html b/extension/src/popup.html index 291786a..2f3ef54 100644 --- a/extension/src/popup.html +++ b/extension/src/popup.html @@ -170,85 +170,6 @@ font-size: 10px; color: #888; } - .rule-groups { - display: flex; - flex-direction: column; - gap: 14px; - } - .rule-groups--enforcement-off { - opacity: 0.55; - } - .rule-group__heading { - margin: 0 0 6px; - padding-bottom: 4px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - color: #555; - border-bottom: 1px solid #e4e4e7; - } - .rules { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: 8px; - } - .rules label { - display: flex; - gap: 8px; - align-items: flex-start; - cursor: pointer; - padding: 6px; - border-radius: 4px; - } - .rules label:hover { - background: #f4f4f5; - } - .rules input { - margin-top: 2px; - } - .rules strong { - display: block; - font-weight: 600; - } - .rules p { - margin: 2px 0 0; - font-size: 11px; - color: #666; - line-height: 1.3; - } - .rules .rule--unavailable label { - cursor: not-allowed; - } - .rules .rule--unavailable strong, - .rules .rule--unavailable p { - color: #999; - } - .rules .rule--unavailable input { - cursor: not-allowed; - } - .rules .unavailable-reason { - margin: 2px 0 0; - font-style: italic; - color: #92400e; - } - .badge { - display: inline-block; - margin-left: 6px; - padding: 1px 6px; - font-size: 10px; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.04em; - color: #92400e; - background: #fef3c7; - border: 1px solid #fde68a; - border-radius: 3px; - vertical-align: middle; - } .options-button-toggle { display: flex; align-items: center; diff --git a/extension/src/popup/Popup.tsx b/extension/src/popup/Popup.tsx index 78f94d2..4edd235 100644 --- a/extension/src/popup/Popup.tsx +++ b/extension/src/popup/Popup.tsx @@ -1,27 +1,17 @@ // Copyright (c) 2026 PixieBrix, Inc. // Licensed under PolyForm Shield 1.0.0 — see LICENSE. -import { availabilitySource } from "../lib/availability"; import { enforcementStorage } from "../lib/enforcement"; import { HelpLinks } from "../lib/HelpLinks"; import { optionsButtonStorage } from "../lib/options-button-toggle"; -import { RuleList } from "../lib/RuleList"; -import { ruleStatesStorage } from "../lib/storage"; import { useChromeStorageValue } from "../lib/use-chrome-storage-value"; import { DetectionsSection } from "./DetectionsSection"; export function Popup() { - const states = useChromeStorageValue(ruleStatesStorage); const enforcementEnabled = useChromeStorageValue(enforcementStorage); - const availability = useChromeStorageValue(availabilitySource); const optionsButtonEnabled = useChromeStorageValue(optionsButtonStorage); - if ( - !states || - enforcementEnabled === null || - !availability || - optionsButtonEnabled === null - ) { + if (enforcementEnabled === null || optionsButtonEnabled === null) { return
    Loading…
    ; } @@ -56,17 +46,12 @@ export function Popup() { {!enforcementEnabled && (

    - All rules are paused for every tab. Your per-rule selection below is + All rules are paused for every tab. Your per-rule selection is preserved and restored when you turn enforcement back on.

    )}
  • -