feat: disable until configured + auto permission request for custom domains

Interception is now completely inactive until the user configures
at least one identity field or explicit mapping. Before that:
- Icon shows gray (unconfigured)
- No fetch/XHR hooks fire
- First-run banner tells user to set up

Custom domains: clicking "Add Domain" in Options now triggers
the browser's native permission prompt via permissions.request().
No more manual chrome://extensions site access step.

Icon states are now:
- Gray = unconfigured (nothing will happen)
- Black = active and protecting
- Blue = reveal mode on
- Red = manually disabled

https://claude.ai/code/session_01Dvgwe7XMoSxnWXkih8p1Cw
This commit is contained in:
Claude
2026-03-26 02:49:03 +00:00
parent a844c94f13
commit eed885ed47
4 changed files with 40 additions and 6 deletions
+14 -3
View File
@@ -264,9 +264,20 @@ function generateIcon(color, size) {
}
async function updateIcon(settings) {
// Check if identity is configured
const identity = await Storage.getIdentity();
const mappings = await Storage.getMappings();
const configured = mappings.length > 0 ||
(identity.emails || []).length > 0 ||
(identity.names || []).length > 0 ||
(identity.usernames || []).length > 0 ||
!!identity.catchAllEmail;
let color;
if (!settings.enabled) {
color = '#dc2626'; // red — disabled
} else if (!configured) {
color = '#9ca3af'; // gray — not configured, effectively disabled
} else if (settings.revealMode) {
color = '#1d4ed8'; // blue — reveal mode
} else {
@@ -285,10 +296,10 @@ async function updateIcon(settings) {
}
}
// Update icon when settings change
// Update icon when settings, identity, or mappings change
api.storage.onChanged.addListener(async (changes) => {
if (changes.ss_settings) {
const settings = { ...(changes.ss_settings.newValue || {}) };
if (changes.ss_settings || changes.ss_identity || changes.ss_mappings) {
const settings = await Storage.getSettings();
await updateIcon(settings);
}
});
+10 -2
View File
@@ -446,12 +446,20 @@
// ============================================================
// Check if we have anything to substitute
// ============================================================
function hasSubstitutions() {
// Check if the user has configured anything at all.
// If not, the extension is effectively disabled — no interception.
function isConfigured() {
return mappings.length > 0 ||
(identity.emails || []).length > 0 ||
(identity.names || []).length > 0 ||
(identity.usernames || []).length > 0 ||
(identity.phones || []).length > 0;
(identity.hostnames || []).length > 0 ||
(identity.phones || []).length > 0 ||
!!identity.catchAllEmail;
}
function hasSubstitutions() {
return isConfigured();
}
// ============================================================
+1 -1
View File
@@ -121,7 +121,7 @@
<button class="btn btn-primary" id="btnAddDomain">Add Domain</button>
</div>
<p class="section-desc" style="margin-top:8px;margin-bottom:0">
After adding a domain in Chrome, you also need to grant permission: <code>chrome://extensions/</code> → Silent Send → Details → Site access → add the domain.
When you click "Add Domain", your browser will ask you to confirm access. No extra steps needed.
</p>
</section>
+15
View File
@@ -1,4 +1,5 @@
import Storage from '../lib/storage.js';
import api from '../lib/browser-polyfill.js';
let mappings = [];
let settings = {};
@@ -206,6 +207,20 @@ async function addDomain() {
return;
}
// Request browser permission for this domain
try {
const granted = await api.permissions.request({
origins: [domain + '/*'],
});
if (!granted) {
alert('Permission denied. The extension needs access to this domain to work.');
return;
}
} catch (e) {
// Firefox or older Chrome may not support optional permissions this way
console.warn('[Silent Send] Could not request permission:', e);
}
domains.push(domain);
settings.customDomains = domains;
await Storage.saveSettings({ customDomains: domains });