feat: custom domains for OpenWebUI + fix README issues
- Add custom domain support in Options page so users can add self-hosted AI services (e.g. https://ai.myserver.com) - Background worker dynamically injects content scripts on custom domains using scripting.executeScript - Add optional_host_permissions so Chrome can grant per-domain access - Rewrite README: add clone step to Firefox instructions, clarify what "credentials" means in step 3, add Windows commands alongside Mac/Linux for every terminal step - Bump version to 0.2.0 https://claude.ai/code/session_01Dvgwe7XMoSxnWXkih8p1Cw
This commit is contained in:
@@ -79,6 +79,19 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>Custom Domains</h2>
|
||||
<p class="section-desc">Add domains for self-hosted AI services (like OpenWebUI). The extension will activate on these domains in addition to the built-in ones.</p>
|
||||
<div class="domain-list" id="domainList"></div>
|
||||
<div class="add-row">
|
||||
<input type="text" id="newDomain" placeholder="https://ai.myserver.com" class="input" style="flex:2">
|
||||
<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.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>Activity Log</h2>
|
||||
<div class="log-actions">
|
||||
@@ -89,7 +102,7 @@
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>Silent Send v0.1.0</p>
|
||||
<p>Silent Send v0.2.0</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,8 +14,15 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
$('#maxLogEntries').value = settings.maxLogEntries || 200;
|
||||
|
||||
renderMappings();
|
||||
renderDomains();
|
||||
renderLog();
|
||||
|
||||
// Custom domains
|
||||
$('#btnAddDomain').addEventListener('click', addDomain);
|
||||
$('#newDomain').addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') addDomain();
|
||||
});
|
||||
|
||||
// Settings listeners
|
||||
$('#showHighlights').addEventListener('change', async (e) => {
|
||||
await Storage.saveSettings({ showHighlights: e.target.checked });
|
||||
@@ -176,6 +183,61 @@ async function renderLog() {
|
||||
.join('');
|
||||
}
|
||||
|
||||
// --- Custom Domains ---
|
||||
async function addDomain() {
|
||||
let domain = $('#newDomain').value.trim();
|
||||
if (!domain) return;
|
||||
|
||||
// Normalize: ensure it has a protocol
|
||||
if (!domain.startsWith('http://') && !domain.startsWith('https://')) {
|
||||
domain = 'https://' + domain;
|
||||
}
|
||||
// Strip trailing slashes
|
||||
domain = domain.replace(/\/+$/, '');
|
||||
|
||||
const domains = settings.customDomains || [];
|
||||
if (domains.includes(domain)) {
|
||||
alert('Domain already added.');
|
||||
return;
|
||||
}
|
||||
|
||||
domains.push(domain);
|
||||
settings.customDomains = domains;
|
||||
await Storage.saveSettings({ customDomains: domains });
|
||||
renderDomains();
|
||||
$('#newDomain').value = '';
|
||||
}
|
||||
|
||||
function renderDomains() {
|
||||
const list = $('#domainList');
|
||||
const domains = settings.customDomains || [];
|
||||
|
||||
if (domains.length === 0) {
|
||||
list.innerHTML = '<div style="text-align:center;color:#9ca3af;padding:12px;font-size:13px">No custom domains. Built-in sites (Claude, ChatGPT, Grok, Gemini, localhost) are always active.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
list.innerHTML = domains
|
||||
.map((d, i) => `
|
||||
<div class="domain-item" style="display:flex;align-items:center;justify-content:space-between;padding:8px;background:#f9fafb;border-radius:6px;margin-bottom:4px">
|
||||
<span style="font-size:13px;font-family:monospace">${escapeHtml(d)}</span>
|
||||
<button class="btn btn-sm btn-danger btn-remove-domain" data-index="${i}">×</button>
|
||||
</div>
|
||||
`)
|
||||
.join('');
|
||||
|
||||
list.querySelectorAll('.btn-remove-domain').forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const idx = parseInt(btn.dataset.index, 10);
|
||||
const domains = settings.customDomains || [];
|
||||
domains.splice(idx, 1);
|
||||
settings.customDomains = domains;
|
||||
await Storage.saveSettings({ customDomains: domains });
|
||||
renderDomains();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = str;
|
||||
|
||||
Reference in New Issue
Block a user