From 80830beae28a2b664feecc08e8e47959654f8dcc Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 13 Jun 2026 23:33:07 +0000 Subject: [PATCH] Fix scale-selection 500 error (missing BytesIO import) and compact GPU badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ai_tools.py: add 'from io import BytesIO' — scale_selection and paste endpoints used BytesIO directly but it was only imported locally in one unrelated function, causing NameError on every scale call. provider-badge.js: redesign badge to fit the narrow (~40px) left toolbar. Was rendering 'GPU · sdxl_offload · Quadro RTX 3000' inline which wrapped into multiple lines covering tool icons. Now shows a status dot + short label (SDXL / OAI / Rep…) with all details moved to the hover tooltip. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM --- backend/app/routers/ai_tools.py | 1 + .../src/js/core/components/provider-badge.js | 83 ++++++++++--------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/backend/app/routers/ai_tools.py b/backend/app/routers/ai_tools.py index dba991e..a5d8095 100644 --- a/backend/app/routers/ai_tools.py +++ b/backend/app/routers/ai_tools.py @@ -10,6 +10,7 @@ from typing import Optional import base64 import asyncio import json +from io import BytesIO from app.services.local_inpaint import ( lama_inpaint, opencv_inpaint, lama_available, gpu_available, rembg_available, diff --git a/frontend/src/js/core/components/provider-badge.js b/frontend/src/js/core/components/provider-badge.js index fc20790..35f0ca0 100644 --- a/frontend/src/js/core/components/provider-badge.js +++ b/frontend/src/js/core/components/provider-badge.js @@ -1,10 +1,6 @@ /** - * ProviderBadge — small DOM element showing the active AI provider. - * Inserted into the toolbar footer on app load. - * - * Green = remote provider healthy (or local_gpu active) - * Yellow = provider configured but unhealthy/unreachable - * Grey = local only (LaMa + OpenCV) + * ProviderBadge — compact status indicator in the left toolbar footer. + * Shows a dot + 3-5 char label; all details in the tooltip. */ import { getCapabilities } from '../../api/capabilities.js'; @@ -15,84 +11,74 @@ export async function mountProviderBadge(container) { var badge = document.createElement('div'); badge.id = 'provider-badge'; badge.style.cssText = [ - 'display:inline-flex', 'align-items:center', 'gap:5px', - 'padding:3px 8px', 'border-radius:10px', - 'font-size:11px', 'font-family:sans-serif', + 'display:flex', 'flex-direction:column', 'align-items:center', 'gap:2px', + 'padding:4px 2px 4px', + 'font-size:9px', 'font-family:sans-serif', 'line-height:1.2', 'cursor:default', 'user-select:none', - 'margin:4px', 'opacity:0.85', + 'width:100%', 'box-sizing:border-box', + 'text-align:center', 'word-break:break-word', ].join(';'); var dot = document.createElement('span'); - dot.style.cssText = 'width:7px;height:7px;border-radius:50%;display:inline-block;'; + dot.style.cssText = 'width:8px;height:8px;border-radius:50%;display:block;flex-shrink:0;'; var label = document.createElement('span'); + label.style.cssText = 'color:inherit;max-width:36px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;'; var remote = caps.remote || {}; var local = caps.local || {}; if (remote.provider === 'local_gpu') { - // Local GPU provider — show GPU name and tier from /api/config local fields var gpuName = _shortGpuName(local.gpu_device); var tier = local.gpu_tier || ''; if (remote.healthy) { dot.style.background = '#44cc44'; - badge.style.background = '#1a2a1a'; badge.style.color = '#aaffaa'; - label.textContent = 'GPU · ' + tier + ' · ' + gpuName; + label.textContent = _shortTier(tier); var flagList = [ local.gpu_fp16 && 'fp16', local.gpu_bf16 && 'bf16', local.gpu_fp8 && 'fp8', - local.gpu_tensor_cores && 'tensor-cores', + local.gpu_tensor_cores && 'TC', ].filter(Boolean).join(' '); badge.title = [ - local.gpu_device || gpuName, - 'VRAM: ' + local.gpu_vram_total + ' GB total ' + local.gpu_vram_free + ' GB free', - 'Compute: CC ' + local.gpu_cc + ' Eff: ' + local.gpu_eff_vram + ' GB', - flagList ? 'Features: ' + flagList : '', - 'Capabilities: ' + (local.local_gpu_capabilities || []).join(', '), + gpuName, + 'VRAM: ' + local.gpu_vram_total + ' GB total / ' + local.gpu_vram_free + ' GB free', + 'CC: ' + local.gpu_cc + ' Eff VRAM: ' + local.gpu_eff_vram + ' GB', + flagList ? 'Flags: ' + flagList : '', + tier ? 'Tier: ' + tier : '', (local.local_gpu_warnings || []).length - ? '\nWarnings:\n' + local.local_gpu_warnings.join('\n') + ? 'Warnings:\n' + local.local_gpu_warnings.join('\n') : '', ].filter(Boolean).join('\n'); } else { dot.style.background = '#ffaa00'; - badge.style.background = '#2a2000'; badge.style.color = '#ffdd88'; - label.textContent = 'Local GPU (not ready)'; - badge.title = 'local_gpu is configured but the diffusers library may not be installed.\nCheck container logs for details.'; + label.textContent = 'GPU?'; + badge.title = 'local_gpu configured but diffusers may not be installed.\nCheck container logs.'; } } else if (remote.provider && remote.healthy) { dot.style.background = '#44cc44'; - badge.style.background = '#1a2a1a'; badge.style.color = '#aaffaa'; - - var overrides = remote.overrides || {}; - var overrideEntries = Object.entries(overrides).filter(([, v]) => v); - var overrideStr = overrideEntries.length - ? ' · ' + overrideEntries.map(([k, v]) => k + '→' + v).join(', ') - : ''; - label.textContent = remote.provider + overrideStr + (local.gpu_detected ? ' · GPU' : ''); + label.textContent = _shortProvider(remote.provider); var opLines = Object.entries(remote.operations || {}) .map(([op, s]) => op + ': ' + (s.provider || remote.provider) + ' ' + (s.healthy ? '✓' : '✗')) .join('\n'); - badge.title = opLines || ('Provider: ' + remote.provider); + badge.title = ('Provider: ' + remote.provider) + (opLines ? '\n' + opLines : ''); } else if (remote.provider && !remote.healthy) { dot.style.background = '#ffaa00'; - badge.style.background = '#2a2000'; badge.style.color = '#ffdd88'; - label.textContent = remote.provider + ' (offline)'; - badge.title = remote.provider + ' is configured but not reachable. Check your .env URL.'; + label.textContent = _shortProvider(remote.provider) + '?'; + badge.title = remote.provider + ' configured but not reachable.\nCheck your .env URL.'; } else { dot.style.background = '#888888'; - badge.style.background = '#1a1a1a'; badge.style.color = '#aaaaaa'; - label.textContent = 'Local' + (local.lama ? ' · LaMa' : '') + (local.gpu_detected ? ' · GPU' : ''); - badge.title = 'Local only. Set AI_PROVIDER in .env to enable generative tools.'; + label.textContent = local.lama ? 'LaMa' : 'Local'; + badge.title = 'Local only (no generative AI).\nSet AI_PROVIDER in .env to enable.'; } badge.appendChild(dot); @@ -108,6 +94,25 @@ export async function mountProviderBadge(container) { function _shortGpuName(name) { return (name || 'GPU') .replace(/^NVIDIA GeForce\s+/i, '') + .replace(/^NVIDIA Quadro\s+/i, '') .replace(/^NVIDIA\s+/i, '') .replace(/^AMD Radeon\s+/i, ''); } + +function _shortTier(tier) { + if (!tier) return 'GPU'; + // sdxl_offload → SDXL, flux → FLUX, sd15 → SD15 + return tier + .replace(/_offload$/, '') + .replace(/_cpu$/, '') + .toUpperCase() + .slice(0, 6); +} + +function _shortProvider(p) { + var map = { + openai: 'OAI', replicate: 'Rep', stability: 'Stab', + invokeai: 'Inv', comfyui: 'CUI', local_gpu: 'GPU', + }; + return map[p] || (p || 'AI').slice(0, 4); +}