Merge pull request #32 from outis1one/claude/read-repo-YMu21

feat: frictionless custom domain management
This commit is contained in:
Outis
2026-03-27 17:38:15 -04:00
committed by GitHub
4 changed files with 336 additions and 25 deletions
+13
View File
@@ -244,6 +244,19 @@
<label class="toggle"><input type="checkbox" id="optProperNouns"><span class="toggle-slider"></span></label>
</div>
<div style="margin-top:12px;padding-top:10px;border-top:1px solid #e5e7eb">
<div class="setting-label" style="margin-bottom:6px">
<strong>Custom Domains</strong>
<span class="setting-desc">Add sites beyond the built-in list</span>
</div>
<div id="popupDomainList" style="max-height:120px;overflow-y:auto;margin-bottom:6px"></div>
<div style="display:flex;gap:4px">
<input type="text" id="popupNewDomain" placeholder="https://ai.example.com" style="flex:1;font-size:11px;padding:4px 6px;border:1px solid #d1d5db;border-radius:4px;min-width:0">
<button class="btn" id="btnPopupAddDomain" style="font-size:11px;padding:4px 8px;white-space:nowrap">Add</button>
</div>
<div id="popupDomainSuggestions" style="margin-top:6px;display:flex;flex-wrap:wrap;gap:3px"></div>
</div>
<div style="margin-top:12px;padding-top:10px;border-top:1px solid #e5e7eb">
<button class="btn" id="btnOpenFullOptions" style="width:100%;font-size:12px">Open Full Options Page</button>
<p class="help-text" style="margin-top:6px;text-align:center">Sync, encryption, org, version history, import/export, and more</p>
+117
View File
@@ -244,6 +244,15 @@ async function initUnlockedUI() {
});
}
// --- Popup domain management ---
renderPopupDomains();
renderPopupDomainSuggestions();
$('#btnPopupAddDomain').addEventListener('click', popupAddDomain);
$('#popupNewDomain').addEventListener('keydown', (e) => {
if (e.key === 'Enter') popupAddDomain();
});
// Update privacy note based on encryption state
const encEnabled = await Storage._isAtRestEncryptionEnabled();
const encNote = $('#privacyEncNote');
@@ -908,6 +917,114 @@ function updateStatusDot() {
dot.classList.toggle('disabled', !settings.enabled);
}
// --- Popup Domain Management ---
const POPUP_SUGGESTED_DOMAINS = [
{ label: 'Mistral', url: 'https://chat.mistral.ai' },
{ label: 'Cohere', url: 'https://coral.cohere.com' },
{ label: 'Phind', url: 'https://www.phind.com' },
{ label: 'You.com', url: 'https://you.com' },
{ label: 'Pi AI', url: 'https://pi.ai' },
{ label: 'Discord', url: 'https://discord.com' },
{ label: 'Slack', url: 'https://app.slack.com' },
{ label: 'Notion', url: 'https://www.notion.so' },
{ label: 'Linear', url: 'https://linear.app' },
{ label: 'Bitbucket', url: 'https://bitbucket.org' },
];
function normalizeDomain(raw) {
let d = raw.trim();
if (!d) return null;
if (!d.startsWith('http://') && !d.startsWith('https://')) d = 'https://' + d;
return d.replace(/\/+$/, '');
}
async function popupAddDomain() {
const domain = normalizeDomain($('#popupNewDomain').value);
if (!domain) return;
const domains = settings.customDomains || [];
if (domains.includes(domain)) return;
try {
const granted = await api.permissions.request({ origins: [domain + '/*'] });
if (!granted) return;
} catch (e) { /* non-fatal */ }
domains.push(domain);
settings.customDomains = domains;
await Storage.saveSettings({ customDomains: domains });
$('#popupNewDomain').value = '';
renderPopupDomains();
renderPopupDomainSuggestions();
}
function renderPopupDomains() {
const list = $('#popupDomainList');
if (!list) return;
const domains = settings.customDomains || [];
if (domains.length === 0) {
safeHTML(list, '<div style="font-size:11px;color:#9ca3af;padding:4px 0">No custom domains added</div>');
return;
}
safeHTML(list, domains.map((d, i) => `
<div style="display:flex;align-items:center;justify-content:space-between;padding:3px 6px;background:#f9fafb;border-radius:4px;margin-bottom:2px">
<span style="font-size:11px;font-family:monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1">${escapeHtml(d)}</span>
<button class="btn-popup-remove-domain" data-index="${i}" style="border:none;background:none;color:#9ca3af;cursor:pointer;font-size:14px;padding:0 2px;line-height:1" title="Remove">&times;</button>
</div>
`).join(''));
list.querySelectorAll('.btn-popup-remove-domain').forEach(btn => {
btn.addEventListener('click', async () => {
const idx = parseInt(btn.dataset.index, 10);
const domains = settings.customDomains || [];
const removed = domains.splice(idx, 1)[0];
settings.customDomains = domains;
await Storage.saveSettings({ customDomains: domains });
if (removed) {
try { await api.permissions.remove({ origins: [removed + '/*'] }); } catch (e) { /* non-fatal */ }
}
renderPopupDomains();
renderPopupDomainSuggestions();
});
});
}
function renderPopupDomainSuggestions() {
const container = $('#popupDomainSuggestions');
if (!container) return;
const domains = settings.customDomains || [];
const available = POPUP_SUGGESTED_DOMAINS.filter(s => !domains.includes(s.url));
if (available.length === 0) {
container.replaceChildren();
return;
}
safeHTML(container, available.slice(0, 6).map(s =>
`<button class="btn-popup-suggest" data-url="${escapeHtml(s.url)}" title="${escapeHtml(s.url)}" style="font-size:10px;padding:2px 6px;border:1px solid #e5e7eb;border-radius:10px;background:#fff;cursor:pointer;color:#6b7280;white-space:nowrap">+ ${escapeHtml(s.label)}</button>`
).join(''));
container.querySelectorAll('.btn-popup-suggest').forEach(btn => {
btn.addEventListener('click', async () => {
const domain = btn.dataset.url;
const domains = settings.customDomains || [];
if (domains.includes(domain)) return;
try {
const granted = await api.permissions.request({ origins: [domain + '/*'] });
if (!granted) return;
} catch (e) { /* non-fatal */ }
domains.push(domain);
settings.customDomains = domains;
await Storage.saveSettings({ customDomains: domains });
renderPopupDomains();
renderPopupDomainSuggestions();
});
});
}
// --- Util ---
function escapeHtml(str) {
const div = document.createElement('div');