fix: corrections UI — indicateur voute, couleurs, statut IA, spinner éditeur
CI / lint (push) Failing after 6s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 11s

1. Suppression du badge vault-context-pill doublon en mobile
2. Couleurs dynamiques indicateur header:
   - Bleu pour 'All' (.vault-ctx-all)
   - Mauve/violet pour les vaults (.vault-ctx-vault)
   - Jaune/ambre pour les répertoires (.vault-ctx-dir)
3. Statut IA: vérifie la présence du toolbar AI dans le DOM
   au lieu d'appeler /api/ai/status (qui ne fonctionnait pas)
4. Indicateur de traitement AI dans l'éditeur (spinner + badge
   'AI...' dans le header de l'editor modal) au lieu du flash
   blanc sur le badge sync
This commit is contained in:
2026-06-04 08:31:32 -04:00
parent 90dffdf26c
commit b077c24bf1
4 changed files with 117 additions and 30 deletions
+24
View File
@@ -368,6 +368,10 @@ export async function createAIToolbar(container, getEditorView) {
return;
}
showToast('⏳ AI: traitement en cours...', 'info');
// Show processing indicator in the editor header
_showProcessingIndicator(true);
// Dispatch AI processing start event
document.dispatchEvent(new CustomEvent('ai-processing-start', { detail: { endpoint, mode } }));
try {
@@ -384,10 +388,30 @@ export async function createAIToolbar(container, getEditorView) {
showToast('AI: ' + msg, 'error');
}
} finally {
_showProcessingIndicator(false);
// Dispatch AI processing end event
document.dispatchEvent(new CustomEvent('ai-processing-end'));
}
}
// ── In-editor processing indicator ──
function _showProcessingIndicator(show) {
let el = document.getElementById('ai-processing-indicator');
if (show) {
if (!el) {
el = document.createElement('span');
el.id = 'ai-processing-indicator';
el.className = 'ai-processing-indicator';
el.innerHTML = '<span class="ai-spinner"></span> AI...';
el.title = 'Traitement AI en cours';
const header = document.querySelector('#editor-modal .editor-header');
if (header) header.appendChild(el);
}
el.style.display = 'inline-flex';
} else if (el) {
el.style.display = 'none';
}
}
// ── Keyboard shortcut Ctrl+J for inline completion ──
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'j') {
+10
View File
@@ -72,6 +72,16 @@ function syncVaultSelectors() {
if (contextText) {
contextText.textContent = state.selectedContextVault === "all" ? "All" : state.selectedContextVault;
}
// Apply color class to the indicator
const indicator = document.getElementById("vault-context-indicator");
if (indicator) {
indicator.classList.remove("vault-ctx-all", "vault-ctx-vault", "vault-ctx-dir");
if (state.selectedContextVault === "all") {
indicator.classList.add("vault-ctx-all");
} else {
indicator.classList.add("vault-ctx-vault");
}
}
}
/**
+15 -11
View File
@@ -339,15 +339,15 @@ function _renderSyncPanel(panel) {
let _aiProcessing = false;
let _aiConfigured = null; // null = unknown, true/false
/** Check if AI is configured by calling the status endpoint. */
async function _checkAIStatus() {
try {
const resp = await api('/api/ai/status');
console.log('[AI Status] API response:', resp);
_aiConfigured = !!(resp && resp.configured);
} catch (e) {
console.error('[AI Status] API call failed:', e);
_aiConfigured = false;
/** Check if AI is configured by looking for the AI toolbar in the DOM.
* The toolbar is only rendered when the AI API responds successfully. */
function _checkAIStatus() {
const container = document.getElementById('ai-toolbar-container');
if (container && container.children.length > 0) {
_aiConfigured = true;
} else if (_aiConfigured === null) {
// Keep unknown state until we have evidence
_aiConfigured = null;
}
}
@@ -404,7 +404,10 @@ function _updateAIStatusInPanel() {
}
}
/** Flash the sync badge white during AI processing. */
/** Flash the sync badge white during AI processing.
* NOTE: now handled via CSS class on #sync-badge.
* The visual flash in the editor itself is handled by ai.js
* which adds a processing indicator inline. */
function _setBadgeAIProcessing(active) {
const badge = document.getElementById('sync-badge');
if (!badge) return;
@@ -435,7 +438,8 @@ function _initAIStatus() {
if (badge) {
const origClick = badge.onclick;
badge.addEventListener('click', () => {
_checkAIStatus().then(() => _updateAIStatusInPanel());
_checkAIStatus();
_updateAIStatusInPanel();
}, { once: false });
}
}
+68 -19
View File
@@ -171,24 +171,48 @@ a:hover {
align-items: center;
gap: 6px;
padding: 6px 12px;
background: color-mix(in srgb, var(--accent) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
border: 1px solid var(--border);
border-radius: 8px;
font-family: "JetBrains Mono", monospace;
font-size: 0.75rem;
font-weight: 600;
color: var(--accent);
cursor: default;
transition: all 200ms ease;
/* Default: neutral (overridden by classes below) */
background: var(--bg-tertiary);
color: var(--text-muted);
}
.vault-context-indicator:hover {
background: color-mix(in srgb, var(--accent) 18%, transparent);
border-color: var(--accent);
filter: brightness(1.15);
}
.vault-context-indicator i {
color: var(--accent);
opacity: 0.8;
}
/* All vaults — blue */
.vault-ctx-all {
background: color-mix(in srgb, #3b82f6 14%, transparent) !important;
border-color: color-mix(in srgb, #3b82f6 30%, var(--border)) !important;
color: #60a5fa !important;
}
.vault-ctx-all i { color: #60a5fa !important; }
/* Specific vault — mauve/violet */
.vault-ctx-vault {
background: color-mix(in srgb, #a855f7 14%, transparent) !important;
border-color: color-mix(in srgb, #a855f7 30%, var(--border)) !important;
color: #c084fc !important;
}
.vault-ctx-vault i { color: #c084fc !important; }
/* Directory — amber/yellow */
.vault-ctx-dir {
background: color-mix(in srgb, #f59e0b 14%, transparent) !important;
border-color: color-mix(in srgb, #f59e0b 30%, var(--border)) !important;
color: #fbbf24 !important;
}
.vault-ctx-dir i { color: #fbbf24 !important; }
.hamburger-btn {
display: none;
background: none;
@@ -2150,6 +2174,41 @@ select {
top: 0;
z-index: 10;
}
/* AI processing indicator — shown inside editor header during AI calls */
.ai-processing-indicator {
display: none;
align-items: center;
gap: 6px;
padding: 3px 10px;
border-radius: 12px;
font-size: 0.72rem;
font-weight: 500;
color: #ffffff;
background: rgba(59, 130, 246, 0.3);
border: 1px solid rgba(96, 165, 250, 0.4);
animation: ai-indicator-pulse 1s ease-in-out infinite;
margin-left: 12px;
}
.ai-spinner {
width: 10px;
height: 10px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: #ffffff;
border-radius: 50%;
animation: ai-spinner-spin 0.6s linear infinite;
}
@keyframes ai-spinner-spin {
to { transform: rotate(360deg); }
}
@keyframes ai-indicator-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
.editor-title {
font-family: "JetBrains Mono", monospace;
font-size: 0.9rem;
@@ -6825,22 +6884,12 @@ body.popup-mode .content-area {
display: none !important;
}
/* Vault context indicator */
/* Vault context indicator — hidden (now in header) */
.vault-context-bar {
display: flex;
align-items: center;
padding: 6px 14px;
background: var(--bg-primary);
border-bottom: 1px solid var(--border);
display: none !important;
}
.vault-context-pill {
font-size: 0.72rem;
font-weight: 600;
color: var(--accent);
background: color-mix(in srgb, var(--accent) 10%, transparent);
padding: 3px 10px;
border-radius: 12px;
white-space: nowrap;
display: none;
}
.mt-btn {