diff --git a/renderer/index.html b/renderer/index.html
index bed7faa..1fcc265 100644
--- a/renderer/index.html
+++ b/renderer/index.html
@@ -488,11 +488,16 @@
+
Retry-Queue: 0
+ Fehler mit Fix: 0
diff --git a/renderer/renderer.js b/renderer/renderer.js
index a06e04d..978b84b 100644
--- a/renderer/renderer.js
+++ b/renderer/renderer.js
@@ -1281,17 +1281,150 @@ function getActiveHeatmapMonths() {
return activityHeatmapRangeMonths;
}
-function logActivity(level, message) {
+function classifyActivityEntry(level, message, meta = null) {
+ const text = String(message || '').trim();
+ const raw = text.toLowerCase();
+ const safeMeta = meta && typeof meta === 'object' ? meta : null;
+
+ let category = 'Allgemein';
+ let fixAction = null;
+
+ const useRetryFix = () => ({
+ type: 'retry-queue-now',
+ label: '🩹 Queue jetzt retry',
+ title: 'Versucht fehlgeschlagene Netzwerk-Aktionen erneut'
+ });
+
+ if (raw.includes('authentifizierung fehlgeschlagen') || raw.includes('unauthorized') || raw.includes('401') || raw.includes('token')) {
+ category = 'Authentifizierung';
+ fixAction = {
+ type: 'open-settings-token',
+ label: '🩹 Token prüfen',
+ title: 'Öffnet Einstellungen und fokussiert den Token'
+ };
+ } else if (raw.includes('zugriff verweigert') || raw.includes('forbidden') || raw.includes('403')) {
+ category = 'Berechtigungen';
+ fixAction = {
+ type: 'open-settings-token',
+ label: '🩹 Rechte prüfen',
+ title: 'Öffnet Einstellungen zur Scope-Prüfung'
+ };
+ } else if (raw.includes('ungültige url') || raw.includes('ungueltige url') || raw.includes('server nicht erreichbar') || raw.includes('dns') || raw.includes('econnrefused') || raw.includes('enotfound') || raw.includes('eai_again') || raw.includes('zeitüberschreitung') || raw.includes('timeout')) {
+ category = 'Verbindung';
+ fixAction = {
+ type: 'open-settings-url',
+ label: '🩹 URL prüfen',
+ title: 'Öffnet Einstellungen und fokussiert die Gitea-URL'
+ };
+ } else if (raw.includes('retry-queue') || raw.includes('queue-r') || raw.includes('queue:') || raw.includes('netzwerkproblem erkannt')) {
+ category = 'Retry-Queue';
+ fixAction = useRetryFix();
+ } else if (raw.startsWith('batch ') || raw.includes('batch beendet')) {
+ category = 'Batch';
+ if (String(level || '').toLowerCase() === 'error' || raw.includes('fehlgeschlagen')) {
+ fixAction = {
+ type: 'open-batch-modal',
+ label: '🩹 Batch erneut starten',
+ title: 'Öffnet den Batch-Dialog'
+ };
+ }
+ }
+
+ if (!fixAction && safeMeta && safeMeta.suggestRetryQueue) {
+ category = safeMeta.category || category;
+ fixAction = useRetryFix();
+ }
+
+ if (!fixAction && safeMeta && safeMeta.fixAction && typeof safeMeta.fixAction === 'object') {
+ fixAction = safeMeta.fixAction;
+ }
+
+ return {
+ category,
+ fixAction,
+ meta: safeMeta
+ };
+}
+
+async function runActivityFixAction(entry) {
+ const action = entry?.fixAction;
+ if (!action || !action.type) return;
+
+ try {
+ if (action.type === 'retry-queue-now') {
+ const res = await window.electronAPI.processRetryQueueNow();
+ if (res?.ok) {
+ showSuccess(`Queue verarbeitet: ${res.succeeded || 0} erfolgreich, ${res.failed || 0} verworfen.`);
+ } else {
+ showWarning(res?.error || 'Queue konnte nicht verarbeitet werden');
+ }
+ return;
+ }
+
+ if (action.type === 'open-settings-token') {
+ openSettingsForFix(currentPlatformKey() === 'github' ? 'githubToken' : 'giteaToken');
+ return;
+ }
+
+ if (action.type === 'open-settings-url') {
+ openSettingsForFix('giteaURL');
+ return;
+ }
+
+ if (action.type === 'open-batch-modal') {
+ $('batchActionModal')?.classList.remove('hidden');
+ updateBatchActionFields();
+ scheduleBatchCloneValidation();
+ return;
+ }
+ } catch (error) {
+ showError(error && error.message ? error.message : String(error));
+ }
+}
+
+function openSettingsForFix(focusId = null) {
+ const modal = $('settingsModal');
+ if (!modal) return;
+ modal.classList.remove('hidden');
+ $('settingsWatermarkCard')?.classList.add('hidden');
+ renderSettingsHealth();
+ requestAnimationFrame(() => {
+ syncSettingsPanelHeights();
+ if (focusId && $(focusId)) {
+ $(focusId).focus();
+ try { $(focusId).select?.(); } catch (_) {}
+ }
+ });
+}
+
+function countFixableErrors(entries) {
+ return (Array.isArray(entries) ? entries : []).filter((e) => e.level === 'error' && !!e.fixAction).length;
+}
+
+function updateActivitySummary() {
+ const summaryEl = $('activitySummaryInfo');
+ if (summaryEl) {
+ const fixableErrors = countFixableErrors(activityEntries);
+ summaryEl.textContent = `Fehler mit Fix: ${fixableErrors}`;
+ }
+}
+
+function logActivity(level, message, meta = null) {
+ const classification = classifyActivityEntry(level, message, meta);
const entry = {
id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
level: level || 'info',
message: String(message || ''),
- ts: new Date().toISOString()
+ ts: new Date().toISOString(),
+ category: classification.category,
+ fixAction: classification.fixAction,
+ meta: classification.meta
};
activityEntries.unshift(entry);
if (activityEntries.length > MAX_ACTIVITY_ITEMS) {
activityEntries = activityEntries.slice(0, MAX_ACTIVITY_ITEMS);
}
+ updateActivitySummary();
renderActivityLog();
refreshActivityHeatmapIfVisible();
}
@@ -1682,7 +1815,12 @@ function renderActivityLog() {
if (!list) return;
const filter = ($('activityFilterLevel') && $('activityFilterLevel').value) || 'all';
- const visible = activityEntries.filter(e => filter === 'all' || e.level === filter);
+ const filterFix = ($('activityFilterFix') && $('activityFilterFix').value) || 'all';
+ const visible = activityEntries.filter(e => {
+ const levelMatches = filter === 'all' || e.level === filter;
+ const fixMatches = filterFix === 'all' || !!e.fixAction;
+ return levelMatches && fixMatches;
+ });
if (visible.length === 0) {
list.innerHTML = '';
@@ -1701,6 +1839,9 @@ function renderActivityLog() {
const row = document.createElement('div');
row.className = `activity-log-item ${e.level || 'info'}`;
+ const header = document.createElement('div');
+ header.className = 'activity-log-row-header';
+
const time = document.createElement('span');
time.className = 'activity-log-time';
time.textContent = formatActivityTimestamp(e.ts);
@@ -1709,13 +1850,36 @@ function renderActivityLog() {
level.className = 'activity-log-level';
level.textContent = (e.level || 'info').toUpperCase();
+ const category = document.createElement('span');
+ category.className = 'activity-log-category';
+ category.textContent = e.category || 'Allgemein';
+
+ header.appendChild(time);
+ header.appendChild(level);
+ header.appendChild(category);
+
const msg = document.createElement('span');
msg.className = 'activity-log-message';
msg.textContent = String(e.message || '');
- row.appendChild(time);
- row.appendChild(level);
+ row.appendChild(header);
row.appendChild(msg);
+
+ if (e.fixAction) {
+ const actions = document.createElement('div');
+ actions.className = 'activity-log-actions';
+
+ const fixBtn = document.createElement('button');
+ fixBtn.className = 'activity-log-fix-btn';
+ fixBtn.type = 'button';
+ fixBtn.textContent = e.fixAction.label || '🩹 Fix ausführen';
+ fixBtn.title = e.fixAction.title || 'Empfohlenen Fix ausführen';
+ fixBtn.onclick = () => runActivityFixAction(e);
+
+ actions.appendChild(fixBtn);
+ row.appendChild(actions);
+ }
+
list.appendChild(row);
}
}
@@ -1726,6 +1890,7 @@ function updateRetryQueueBadge(count) {
if (btn) btn.textContent = `🔁 Queue (${retryQueueCount})`;
const info = $('activityQueueInfo');
if (info) info.textContent = `Retry-Queue: ${retryQueueCount}`;
+ updateActivitySummary();
}
function parseBatchRepoInput(raw) {
@@ -3777,10 +3942,12 @@ async function loadRepoContents(owner, repo, path) {
return;
}
// Wenn zur Übersicht gewechselt wird, Gravur zurücksetzen
-function resetProjectGravurTitle() {
+window.resetProjectGravurTitle = function() {
const gravurTitle = document.getElementById('project-gravur-title');
if (gravurTitle) gravurTitle.textContent = '';
-}
+};
+
+// ...existing code...
// Nach dem Laden der Repo-Liste oder beim Klick auf "Zurück" rufe resetProjectGravurTitle() auf
const origLoadRepos = loadRepos;
@@ -5617,7 +5784,10 @@ window.addEventListener('DOMContentLoaded', async () => {
// Event Handlers
if ($('btnLoadGiteaRepos')) {
- $('btnLoadGiteaRepos').onclick = loadRepos;
+ $('btnLoadGiteaRepos').onclick = () => {
+ window.resetProjectGravurTitle();
+ loadRepos();
+ };
}
if ($('btnSelectFolder')) {
@@ -5636,6 +5806,7 @@ window.addEventListener('DOMContentLoaded', async () => {
$('btnBack').onclick = () => {
if (currentState.view === 'gitea-repo') {
if (currentState.path === '' || currentState.path === '/') {
+ window.resetProjectGravurTitle(); // Projektname ausblenden
loadGiteaRepos();
} else {
const parts = currentState.path.split('/').filter(p => p);
@@ -5693,6 +5864,7 @@ window.addEventListener('DOMContentLoaded', async () => {
if ($('btnOpenActivityLog')) {
$('btnOpenActivityLog').onclick = () => {
$('activityLogModal')?.classList.remove('hidden');
+ updateActivitySummary();
renderActivityLog();
};
}
@@ -5705,9 +5877,14 @@ window.addEventListener('DOMContentLoaded', async () => {
$('activityFilterLevel').addEventListener('change', renderActivityLog);
}
+ if ($('activityFilterFix')) {
+ $('activityFilterFix').addEventListener('change', renderActivityLog);
+ }
+
if ($('btnClearActivityLog')) {
$('btnClearActivityLog').onclick = () => {
activityEntries = [];
+ updateActivitySummary();
renderActivityLog();
refreshActivityHeatmapIfVisible();
};
@@ -6358,7 +6535,11 @@ window.addEventListener('DOMContentLoaded', async () => {
updateRetryQueueBadge(size);
if (payload && payload.event === 'queued' && payload.item) {
const p = payload.item.payload || {};
- logActivity('warning', `Queue: ${p.owner}/${p.repo}/${p.path} wurde eingeplant`);
+ logActivity('warning', `Queue: ${p.owner}/${p.repo}/${p.path} wurde eingeplant`, {
+ category: 'Retry-Queue',
+ suggestRetryQueue: true,
+ queueId: payload.item.id || null
+ });
} else if (payload && payload.event === 'processed') {
logActivity('info', `Queue-Retry: ${payload.succeeded || 0} erfolgreich, ${payload.failed || 0} verworfen`);
}
diff --git a/renderer/style.css b/renderer/style.css
index e087bb2..85ee1fc 100644
--- a/renderer/style.css
+++ b/renderer/style.css
@@ -1994,6 +1994,12 @@ input[type="checkbox"] {
.activity-queue-info {
color: var(--text-secondary);
font-size: 12px;
+ margin-bottom: 4px;
+}
+
+.activity-summary-info {
+ color: var(--text-muted);
+ font-size: 12px;
margin-bottom: 10px;
}
@@ -2014,6 +2020,9 @@ input[type="checkbox"] {
border-radius: 12px;
border-left: 3px solid rgba(255, 255, 255, 0.2);
background: linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02));
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
}
.activity-log-item.info {
@@ -2031,14 +2040,34 @@ input[type="checkbox"] {
.activity-log-time {
color: var(--text-muted);
font-size: 11px;
- margin-right: 6px;
+ margin-right: 8px;
}
.activity-log-level {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.4px;
- margin-right: 6px;
+ margin-right: 8px;
+}
+
+.activity-log-row-header {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.activity-log-category {
+ display: inline-flex;
+ align-items: center;
+ height: 20px;
+ padding: 0 8px;
+ border-radius: 999px;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0.35px;
+ color: #cfe8ff;
+ border: 1px solid rgba(140, 190, 255, 0.35);
+ background: rgba(58, 120, 200, 0.18);
}
.activity-log-message {
@@ -2046,6 +2075,30 @@ input[type="checkbox"] {
font-size: 13px;
}
+.activity-log-actions {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.activity-log-fix-btn {
+ height: 30px;
+ padding: 0 10px;
+ border-radius: 8px;
+ border: 1px solid rgba(92, 210, 255, 0.5);
+ background: rgba(17, 92, 132, 0.28);
+ color: #d9f4ff;
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.15s, border-color 0.15s, transform 0.15s;
+}
+
+.activity-log-fix-btn:hover {
+ background: rgba(23, 122, 173, 0.35);
+ border-color: rgba(122, 227, 255, 0.75);
+ transform: translateY(-1px);
+}
+
/* ===========================
CONTEXT MENU
=========================== */