From ee7b2283de4ad2231742444e973fc7aee94540fc Mon Sep 17 00:00:00 2001 From: perf3ct Date: Wed, 12 Mar 2025 21:13:49 +0000 Subject: [PATCH] correctly style the failed embeddings section --- .../type_widgets/options/ai_settings.ts | 115 +++++++++++++----- src/public/translations/en/translation.json | 6 +- src/services/llm/embeddings/queue.ts | 8 +- 3 files changed, 95 insertions(+), 34 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/ai_settings.ts b/src/public/app/widgets/type_widgets/options/ai_settings.ts index 92fdfa24c..a97e95a0e 100644 --- a/src/public/app/widgets/type_widgets/options/ai_settings.ts +++ b/src/public/app/widgets/type_widgets/options/ai_settings.ts @@ -42,6 +42,7 @@ interface FailedEmbeddingNotes { error: string; failureType: string; chunks: number; + isPermanent: boolean; }>; } @@ -847,34 +848,61 @@ export default class AiSettingsWidget extends OptionsWidget { return; } - const $failedHeader = $(` + // Create header with count and retry all button + const $header = $(`
-
Failed Embeddings (${failedResult.failedNotes.length})
- +
Failed Embeddings (${failedResult.failedNotes.length})
+
`); - const $failedList = $('
'); + // Create list container using the application's native note-list class + const $failedList = $('
'); for (const note of failedResult.failedNotes) { // Determine if this is a full note failure or just failed chunks const isFullFailure = note.failureType === 'full'; - const badgeClass = isFullFailure ? 'badge-danger' : 'badge-warning'; - const badgeText = isFullFailure ? 'Full Note' : `${note.chunks} Chunks`; + const isPermanentlyFailed = note.isPermanent === true; + // Use Bootstrap 4 badge classes + let badgeText = isFullFailure ? 'Full Note' : `Chunks Failed`; + let badgeClass = 'badge-warning'; + + if (isPermanentlyFailed) { + badgeClass = 'badge-danger'; + if (isFullFailure) { + badgeText = 'Permanently Failed'; + } else { + badgeText = 'Partially Failed'; + } + } + + // Use the application's note-list-item styling const $item = $(` -
-
-
-
${note.title || note.noteId}
- ${badgeText} +
+
+
+
+
+
${note.title || note.noteId}
+ ${badgeText} +
+

+ ${isPermanentlyFailed ? + `Status: Permanently failed` : + `Attempts: ${note.attempts}`} +
+ Last attempt: ${note.lastAttempt.substring(0, 19)} +
+ Error: ${(note.error || 'Unknown error').substring(0, 100)}${(note.error && note.error.length > 100) ? '...' : ''} +

+
+
+ +
- -
-
-
Attempts: ${note.attempts}
-
Last attempt: ${note.lastAttempt}
-
Error: ${note.error}
`); @@ -882,38 +910,67 @@ export default class AiSettingsWidget extends OptionsWidget { $failedList.append($item); } - this.$widget.find('.embedding-failed-notes-list').empty().append($failedHeader, $failedList); + // Add the header and list to the DOM (no card structure) + this.$widget.find('.embedding-failed-notes-list').empty().append($header, $failedList); // Add event handlers using local variables to avoid 'this' issues const self = this; - this.$widget.find('.retry-btn').on('click', async function() { - const noteId = $(this).data('note-id'); - $(this).prop('disabled', true).text('Retrying...'); + this.$widget.find('.retry-btn').on('click', async function(e) { + // Prevent default behavior + e.preventDefault(); + + const $button = $(this); + const noteId = $button.data('note-id'); + + // Show loading state + $button.prop('disabled', true) + .removeClass('btn-outline-secondary') + .addClass('btn-outline-secondary') + .html('Retrying'); const success = await self.retryFailedEmbedding(noteId); if (success) { - toastService.showMessage("Note queued for retry"); + toastService.showMessage(t("ai_llm.note_queued_for_retry")); await self.refreshEmbeddingStats(); } else { - toastService.showError("Failed to retry note"); - $(this).prop('disabled', false).text('Retry'); + toastService.showError(t("ai_llm.failed_to_retry_note")); + $button.prop('disabled', false) + .html(' Retry'); } }); - this.$widget.find('.retry-all-btn').on('click', async function() { - $(this).prop('disabled', true).text('Retrying All...'); + this.$widget.find('.retry-all-btn').on('click', async function(e) { + const $button = $(this); + + // Show loading state + $button.prop('disabled', true) + .removeClass('btn-primary') + .addClass('btn-secondary') + .html('Retrying All'); const success = await self.retryAllFailedEmbeddings(); if (success) { - toastService.showMessage("All failed notes queued for retry"); + toastService.showMessage(t("ai_llm.all_notes_queued_for_retry")); await self.refreshEmbeddingStats(); + + // Return button to original state after successful refresh + if (!$button.is(':disabled')) { // Check if button still exists + $button.prop('disabled', false) + .removeClass('btn-secondary') + .addClass('btn-primary') + .html('Retry All'); + } } else { - toastService.showError("Failed to retry notes"); - $(this).prop('disabled', false).text('Retry All Failed'); + toastService.showError(t("ai_llm.failed_to_retry_all")); + $button.prop('disabled', false) + .removeClass('btn-secondary') + .addClass('btn-primary') + .html('Retry All'); } }); } } + diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 3ba3ad2d4..7aa352a17 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1209,7 +1209,11 @@ "refresh_stats": "Refresh Stats", "refreshing": "Refreshing...", "stats_error": "Error fetching embedding statistics", - "auto_refresh_notice": "Auto-refreshes every {{seconds}} seconds" + "auto_refresh_notice": "Auto-refreshes every {{seconds}} seconds", + "note_queued_for_retry": "Note queued for retry", + "failed_to_retry_note": "Failed to retry note", + "all_notes_queued_for_retry": "All failed notes queued for retry", + "failed_to_retry_all": "Failed to retry notes" }, "zoom_factor": { "title": "Zoom Factor (desktop build only)", diff --git a/src/services/llm/embeddings/queue.ts b/src/services/llm/embeddings/queue.ts index 102b52ff8..893dd6d0d 100644 --- a/src/services/llm/embeddings/queue.ts +++ b/src/services/llm/embeddings/queue.ts @@ -286,10 +286,10 @@ export async function processEmbeddingQueue() { if (noteData.attempts + 1 >= 3) { log.error(`Marked note ${noteData.noteId} as permanently failed after multiple embedding attempts`); - // Set the failed flag and update the attempts + // Set the failed flag but keep the actual attempts count await sql.execute(` UPDATE embedding_queue - SET attempts = 999, failed = 1 + SET failed = 1 WHERE noteId = ? `, [noteData.noteId]); } @@ -313,10 +313,10 @@ export async function processEmbeddingQueue() { if (noteData.attempts + 1 >= 3) { log.error(`Marked note ${noteData.noteId} as permanently failed after multiple embedding attempts`); - // Set the failed flag and update the attempts + // Set the failed flag but keep the actual attempts count await sql.execute(` UPDATE embedding_queue - SET attempts = 999, failed = 1 + SET failed = 1 WHERE noteId = ? `, [noteData.noteId]); }