v1.8.6: branding — colors apply instantly, logo upload with size control, fix tracking width
This commit is contained in:
+67
-12
@@ -484,12 +484,23 @@ a[role=button]:active {
|
||||
<input id="b-subtitle" class="w-full bg-surface-high border border-outline-variant rounded-xl px-4 py-3 text-sm text-on-surface outline-none focus:border-primary transition-colors"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-on-surface-variant mb-2 block">Logo URL</label>
|
||||
<input id="b-logo" placeholder="https://example.com/logo.png" class="w-full bg-surface-high border border-outline-variant rounded-xl px-4 py-3 text-sm text-on-surface outline-none focus:border-primary transition-colors"/>
|
||||
</div>
|
||||
<div id="logo-preview" class="hidden">
|
||||
<label class="text-xs text-on-surface-variant mb-2 block">Preview</label>
|
||||
<img id="logo-img" class="h-12 rounded-xl" />
|
||||
<label class="text-xs text-on-surface-variant mb-2 block">Logo</label>
|
||||
<div class="flex gap-2 mb-2">
|
||||
<input id="b-logo" placeholder="https://example.com/logo.png"
|
||||
class="flex-1 bg-surface-high border border-outline-variant rounded-xl px-4 py-3 text-sm text-on-surface outline-none focus:border-primary transition-colors"
|
||||
oninput="updateLogoPreview()"/>
|
||||
<label class="glass glass-hover px-3 py-2 rounded-xl text-xs text-primary font-bold cursor-pointer flex items-center gap-1 shrink-0">
|
||||
<span class="msym text-sm">upload</span> Upload
|
||||
<input type="file" accept="image/*" class="hidden" onchange="uploadLogo(this)"/>
|
||||
</label>
|
||||
</div>
|
||||
<img id="b-logo-preview" class="hidden rounded-xl mb-2 border border-outline-variant" style="max-width:200px"/>
|
||||
<div class="flex items-center gap-3">
|
||||
<label class="text-xs text-on-surface-variant shrink-0">Size</label>
|
||||
<input type="range" id="b-logo-size" min="20" max="120" value="40" step="4"
|
||||
class="flex-1 accent-primary" oninput="updateLogoPreview()"/>
|
||||
<span id="b-logo-size-val" class="text-xs text-on-surface-variant w-10 text-right">40px</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass rounded-2xl p-6 space-y-5">
|
||||
@@ -515,7 +526,7 @@ a[role=button]:active {
|
||||
<div id="preview-secondary" class="flex-1 h-10 rounded-lg"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-on-surface-variant">Changes apply on next page load after saving.</p>
|
||||
<p class="text-xs text-on-surface-variant">Colors apply instantly after saving.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -589,7 +600,6 @@ curl -X POST https://rag.afaqzaid.com/ingest-url \
|
||||
<div id="settings-llm" class="glass rounded-2xl p-6 mt-6"></div>
|
||||
<div id="settings-r2" class="glass rounded-2xl p-6 mt-6"></div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CLAUDE -->
|
||||
@@ -1300,13 +1310,13 @@ async function loadSettings() {
|
||||
const d = await api('/admin/settings');
|
||||
document.getElementById('settings-ai').innerHTML = `
|
||||
<h3 class="text-primary text-xs font-bold uppercase tracking-widest mb-5 flex items-center gap-2"><span class="msym text-base">smart_toy</span>AI Configuration</h3>
|
||||
${[['Local LLM',d.ollama_model,''],['Claude API',d.claude_api_configured?'✓ Configured':'✗ Not set',d.claude_api_configured?'text-emerald-400':'text-red-400'],['Complexity Threshold',d.complexity_threshold_words+' words',''],['Confidence Threshold',d.confidence_threshold,''],['Cache TTL',d.cache_ttl_hours+'h','']].map(([l,v,cls])=>`
|
||||
${[['Local LLM',d.ollama_model,''],['Claude API',d.claude_api_configured?'✓ Configured':'✗ Not set',d.claude_api_configured?'text-emerald-400':'text-red-400'],['Confidence Threshold',d.confidence_threshold,''],['Cache TTL',d.cache_ttl_hours+'h','']].map(([l,v,cls])=>`
|
||||
<div class="flex justify-between items-center py-3 border-b border-outline-variant/20 last:border-0">
|
||||
<span class="text-sm text-on-surface-variant">${l}</span><span class="text-xs font-mono glass px-2 py-1 rounded-lg ${cls||'text-primary'}">${v}</span>
|
||||
</div>`).join('')}`;
|
||||
document.getElementById('settings-system').innerHTML = `
|
||||
<h3 class="text-primary text-xs font-bold uppercase tracking-widest mb-5 flex items-center gap-2"><span class="msym text-base">build</span>Ingestion Settings</h3>
|
||||
${[['Chunk Size',d.chunk_size+' words'],['Chunk Overlap',d.chunk_overlap+' words'],['Qdrant URL',d.qdrant_url],['Ollama URL',d.ollama_url]].map(([l,v])=>`
|
||||
${[['Chunk Size',d.chunk_size+' words'],['Chunk Overlap',d.chunk_overlap+' sentences'],['Qdrant URL',d.qdrant_url],['Ollama URL',d.ollama_url]].map(([l,v])=>`
|
||||
<div class="flex justify-between items-center py-3 border-b border-outline-variant/20 last:border-0">
|
||||
<span class="text-sm text-on-surface-variant">${l}</span><span class="text-xs font-mono glass px-2 py-1 rounded-lg text-primary truncate max-w-[140px]">${v}</span>
|
||||
</div>`).join('')}`;
|
||||
@@ -1431,6 +1441,39 @@ async function testR2Connection() {
|
||||
} catch(e) { status.textContent = '❌ '+e.message; status.style.color = '#f87171'; }
|
||||
}
|
||||
|
||||
function updateLogoPreview() {
|
||||
const url = document.getElementById('b-logo')?.value;
|
||||
const size = document.getElementById('b-logo-size')?.value || 40;
|
||||
document.getElementById('b-logo-size-val').textContent = size + 'px';
|
||||
const img = document.getElementById('b-logo-preview');
|
||||
if (url) { img.src = url; img.style.height = size + 'px'; img.classList.remove('hidden'); }
|
||||
else img.classList.add('hidden');
|
||||
}
|
||||
|
||||
async function uploadLogo(input) {
|
||||
const file = input.files[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = async (e) => {
|
||||
try {
|
||||
const base64 = e.target.result.split(',')[1];
|
||||
const mime = file.type;
|
||||
const r = await fetch('/admin/upload-logo', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/json','Authorization':'Bearer '+JWT_TOKEN},
|
||||
body: JSON.stringify({data: base64, mime_type: mime, filename: file.name})
|
||||
});
|
||||
const d = await r.json();
|
||||
if (d.url) {
|
||||
document.getElementById('b-logo').value = d.url;
|
||||
updateLogoPreview();
|
||||
toast('Logo uploaded!', 'success');
|
||||
}
|
||||
} catch(e) { toast('Upload failed: '+e.message); }
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
async function loadBranding() {
|
||||
try {
|
||||
const d = await api('/admin/branding');
|
||||
@@ -1443,6 +1486,13 @@ async function loadBranding() {
|
||||
document.getElementById('b-secondary-hex').value = d.secondary_color||'#95d3ba';
|
||||
document.getElementById('preview-primary').style.background = d.primary_color||'#4edea3';
|
||||
document.getElementById('preview-secondary').style.background = d.secondary_color||'#95d3ba';
|
||||
// Apply colors live to admin CSS variables
|
||||
if(d.primary_color) document.documentElement.style.setProperty('--primary', d.primary_color);
|
||||
if(d.secondary_color) document.documentElement.style.setProperty('--secondary', d.secondary_color);
|
||||
// Logo
|
||||
if(d.logo_url) document.getElementById('b-logo').value = d.logo_url;
|
||||
if(d.logo_size) document.getElementById('b-logo-size').value = d.logo_size;
|
||||
if(d.logo_url) { const img=document.getElementById('b-logo-preview'); img.src=d.logo_url; img.style.height=(d.logo_size||40)+'px'; img.classList.remove('hidden'); }
|
||||
if (d.logo_url) { document.getElementById('logo-img').src=d.logo_url; document.getElementById('logo-preview').classList.remove('hidden'); }
|
||||
document.getElementById('b-primary').oninput = function(){ document.getElementById('b-primary-hex').value=this.value; document.getElementById('preview-primary').style.background=this.value; };
|
||||
document.getElementById('b-secondary').oninput = function(){ document.getElementById('b-secondary-hex').value=this.value; document.getElementById('preview-secondary').style.background=this.value; };
|
||||
@@ -1454,8 +1504,13 @@ async function loadBranding() {
|
||||
|
||||
async function saveBranding() {
|
||||
try {
|
||||
const r = await fetch('/admin/branding', {method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+JWT_TOKEN}, body:JSON.stringify({site_name:document.getElementById('b-site-name').value, site_subtitle:document.getElementById('b-subtitle').value, logo_url:document.getElementById('b-logo').value, primary_color:document.getElementById('b-primary-hex').value, secondary_color:document.getElementById('b-secondary-hex').value})});
|
||||
if (r.ok) toast('Branding saved!','success'); else alert('Save failed');
|
||||
const r = await fetch('/admin/branding', {method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+JWT_TOKEN}, body:JSON.stringify({site_name:document.getElementById('b-site-name').value, site_subtitle:document.getElementById('b-subtitle').value, logo_url:document.getElementById('b-logo').value, logo_size:document.getElementById('b-logo-size')?.value||'40', primary_color:document.getElementById('b-primary-hex').value, secondary_color:document.getElementById('b-secondary-hex').value})});
|
||||
if (r.ok) toast('Branding saved!','success');
|
||||
const p=document.getElementById('b-primary-hex')?.value;
|
||||
const s=document.getElementById('b-secondary-hex')?.value;
|
||||
if(p) document.documentElement.style.setProperty('--primary',p);
|
||||
if(s) document.documentElement.style.setProperty('--secondary',s);
|
||||
} else alert('Save failed');
|
||||
} catch(e) { alert('Error: '+e.message); }
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -371,8 +371,14 @@ let currentUser = null;
|
||||
|
||||
// ── Branding ──────────────────────────────────────────────────────────────────
|
||||
fetch('/branding').then(r=>r.json()).then(b=>{
|
||||
if(b.site_name){document.title=b.site_name;document.getElementById('brand-name').textContent=b.site_name;}
|
||||
if(b.site_subtitle) document.getElementById('brand-sub').textContent=b.site_subtitle;
|
||||
if(b.site_name){document.title=b.site_name;const bn=document.getElementById('brand-name');if(bn)bn.textContent=b.site_name;}
|
||||
if(b.site_subtitle){const bs=document.getElementById('brand-sub');if(bs)bs.textContent=b.site_subtitle;}
|
||||
if(b.primary_color) document.documentElement.style.setProperty('--primary', b.primary_color);
|
||||
if(b.secondary_color) document.documentElement.style.setProperty('--secondary', b.secondary_color);
|
||||
if(b.logo_url){
|
||||
const logo=document.getElementById('brand-logo');
|
||||
if(logo){logo.src=b.logo_url;logo.style.height=(b.logo_size||'40')+'px';logo.classList.remove('hidden');}
|
||||
}
|
||||
}).catch(()=>{});
|
||||
|
||||
// ── Counter animation ─────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user