fix: corrections UI — indicateur voute, couleurs, statut IA, spinner éditeur
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:
@@ -368,6 +368,10 @@ export async function createAIToolbar(container, getEditorView) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showToast('⏳ AI: traitement en cours...', 'info');
|
showToast('⏳ AI: traitement en cours...', 'info');
|
||||||
|
|
||||||
|
// Show processing indicator in the editor header
|
||||||
|
_showProcessingIndicator(true);
|
||||||
|
|
||||||
// Dispatch AI processing start event
|
// Dispatch AI processing start event
|
||||||
document.dispatchEvent(new CustomEvent('ai-processing-start', { detail: { endpoint, mode } }));
|
document.dispatchEvent(new CustomEvent('ai-processing-start', { detail: { endpoint, mode } }));
|
||||||
try {
|
try {
|
||||||
@@ -384,10 +388,30 @@ export async function createAIToolbar(container, getEditorView) {
|
|||||||
showToast('AI: ' + msg, 'error');
|
showToast('AI: ' + msg, 'error');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
_showProcessingIndicator(false);
|
||||||
// Dispatch AI processing end event
|
// Dispatch AI processing end event
|
||||||
document.dispatchEvent(new CustomEvent('ai-processing-end'));
|
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 ──
|
// ── Keyboard shortcut Ctrl+J for inline completion ──
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.ctrlKey && e.key === 'j') {
|
if (e.ctrlKey && e.key === 'j') {
|
||||||
|
|||||||
@@ -72,6 +72,16 @@ function syncVaultSelectors() {
|
|||||||
if (contextText) {
|
if (contextText) {
|
||||||
contextText.textContent = state.selectedContextVault === "all" ? "All" : state.selectedContextVault;
|
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
@@ -339,15 +339,15 @@ function _renderSyncPanel(panel) {
|
|||||||
let _aiProcessing = false;
|
let _aiProcessing = false;
|
||||||
let _aiConfigured = null; // null = unknown, true/false
|
let _aiConfigured = null; // null = unknown, true/false
|
||||||
|
|
||||||
/** Check if AI is configured by calling the status endpoint. */
|
/** Check if AI is configured by looking for the AI toolbar in the DOM.
|
||||||
async function _checkAIStatus() {
|
* The toolbar is only rendered when the AI API responds successfully. */
|
||||||
try {
|
function _checkAIStatus() {
|
||||||
const resp = await api('/api/ai/status');
|
const container = document.getElementById('ai-toolbar-container');
|
||||||
console.log('[AI Status] API response:', resp);
|
if (container && container.children.length > 0) {
|
||||||
_aiConfigured = !!(resp && resp.configured);
|
_aiConfigured = true;
|
||||||
} catch (e) {
|
} else if (_aiConfigured === null) {
|
||||||
console.error('[AI Status] API call failed:', e);
|
// Keep unknown state until we have evidence
|
||||||
_aiConfigured = false;
|
_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) {
|
function _setBadgeAIProcessing(active) {
|
||||||
const badge = document.getElementById('sync-badge');
|
const badge = document.getElementById('sync-badge');
|
||||||
if (!badge) return;
|
if (!badge) return;
|
||||||
@@ -435,7 +438,8 @@ function _initAIStatus() {
|
|||||||
if (badge) {
|
if (badge) {
|
||||||
const origClick = badge.onclick;
|
const origClick = badge.onclick;
|
||||||
badge.addEventListener('click', () => {
|
badge.addEventListener('click', () => {
|
||||||
_checkAIStatus().then(() => _updateAIStatusInPanel());
|
_checkAIStatus();
|
||||||
|
_updateAIStatusInPanel();
|
||||||
}, { once: false });
|
}, { once: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-19
@@ -171,24 +171,48 @@ a:hover {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
border: 1px solid var(--border);
|
||||||
border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-family: "JetBrains Mono", monospace;
|
font-family: "JetBrains Mono", monospace;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--accent);
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
transition: all 200ms ease;
|
transition: all 200ms ease;
|
||||||
|
/* Default: neutral (overridden by classes below) */
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
.vault-context-indicator:hover {
|
.vault-context-indicator:hover {
|
||||||
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
filter: brightness(1.15);
|
||||||
border-color: var(--accent);
|
|
||||||
}
|
}
|
||||||
.vault-context-indicator i {
|
.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 {
|
.hamburger-btn {
|
||||||
display: none;
|
display: none;
|
||||||
background: none;
|
background: none;
|
||||||
@@ -2150,6 +2174,41 @@ select {
|
|||||||
top: 0;
|
top: 0;
|
||||||
z-index: 10;
|
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 {
|
.editor-title {
|
||||||
font-family: "JetBrains Mono", monospace;
|
font-family: "JetBrains Mono", monospace;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
@@ -6825,22 +6884,12 @@ body.popup-mode .content-area {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Vault context indicator */
|
/* Vault context indicator — hidden (now in header) */
|
||||||
.vault-context-bar {
|
.vault-context-bar {
|
||||||
display: flex;
|
display: none !important;
|
||||||
align-items: center;
|
|
||||||
padding: 6px 14px;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
}
|
||||||
.vault-context-pill {
|
.vault-context-pill {
|
||||||
font-size: 0.72rem;
|
display: none;
|
||||||
font-weight: 600;
|
|
||||||
color: var(--accent);
|
|
||||||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
||||||
padding: 3px 10px;
|
|
||||||
border-radius: 12px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-btn {
|
.mt-btn {
|
||||||
|
|||||||
Reference in New Issue
Block a user