Update agents, dashboard, kb +2 more (+6 ~11)

This commit is contained in:
Echo
2026-01-31 22:05:53 +00:00
parent 6555ea28ee
commit d2d9016da5
17 changed files with 900 additions and 187 deletions

View File

@@ -108,6 +108,8 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
self.handle_activity()
elif self.path.startswith('/api/files'):
self.handle_files_get()
elif self.path.startswith('/api/diff'):
self.handle_git_diff()
elif self.path.startswith('/api/'):
self.send_error(404)
else:
@@ -155,6 +157,14 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
cwd=workspace, capture_output=True, text=True, timeout=5
).stdout.strip()
# Parse uncommitted into structured format
uncommitted_parsed = []
for line in uncommitted:
if len(line) >= 3:
status = line[:2].strip()
filepath = line[3:].strip()
uncommitted_parsed.append({'status': status, 'path': filepath})
self.send_json({
'branch': branch,
'lastCommit': {
@@ -163,6 +173,7 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
'time': commit_parts[2] if len(commit_parts) > 2 else ''
},
'uncommitted': uncommitted,
'uncommittedParsed': uncommitted_parsed,
'uncommittedCount': len(uncommitted),
'diffStat': diff_stat,
'clean': len(uncommitted) == 0
@@ -170,6 +181,60 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
except Exception as e:
self.send_json({'error': str(e)}, 500)
def handle_git_diff(self):
"""Get git diff for a specific file."""
from urllib.parse import urlparse, parse_qs
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
filepath = params.get('path', [''])[0]
if not filepath:
self.send_json({'error': 'path required'}, 400)
return
try:
workspace = Path('/home/moltbot/clawd')
# Security check
target = (workspace / filepath).resolve()
if not str(target).startswith(str(workspace)):
self.send_json({'error': 'Access denied'}, 403)
return
# Get diff (try staged first, then unstaged)
diff = subprocess.run(
['git', 'diff', '--cached', '--', filepath],
cwd=workspace, capture_output=True, text=True, timeout=10
).stdout
if not diff:
diff = subprocess.run(
['git', 'diff', '--', filepath],
cwd=workspace, capture_output=True, text=True, timeout=10
).stdout
# If still no diff, file might be untracked - show full content
if not diff:
status = subprocess.run(
['git', 'status', '--short', '--', filepath],
cwd=workspace, capture_output=True, text=True, timeout=5
).stdout.strip()
if status.startswith('??'):
# Untracked file - show as new
if target.exists():
content = target.read_text(encoding='utf-8', errors='replace')[:50000]
diff = f"+++ b/{filepath}\n" + '\n'.join(f'+{line}' for line in content.split('\n'))
self.send_json({
'path': filepath,
'diff': diff or 'No changes',
'hasDiff': bool(diff)
})
except Exception as e:
self.send_json({'error': str(e)}, 500)
def handle_agents_status(self):
"""Get agents status - fast version reading session files directly."""
try:
@@ -361,11 +426,44 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
'text': message[:60] + ('...' if len(message) > 60 else ''),
'agent': 'git',
'time': local_time.strftime('%H:%M'),
'timestamp': timestamp * 1000
'timestamp': timestamp * 1000,
'commitHash': commit_hash[:8]
})
except:
pass
# 2b. Git uncommitted files
try:
result = subprocess.run(
['git', 'status', '--short'],
cwd=workspace, capture_output=True, text=True, timeout=10
)
if result.returncode == 0 and result.stdout.strip():
for line in result.stdout.strip().split('\n'):
if len(line) >= 4:
# Git status format: XY filename (XY = 2 chars status)
# Handle both "M " and " M" formats
status = line[:2]
# Find filepath - skip status chars and any spaces
filepath = line[2:].lstrip()
if not filepath:
continue
status_clean = status.strip()
status_labels = {'M': 'modificat', 'A': 'adăugat', 'D': 'șters', '??': 'nou', 'R': 'redenumit'}
status_label = status_labels.get(status_clean, status_clean)
activities.append({
'type': 'git-file',
'icon': 'file-diff',
'text': f"{filepath}",
'agent': f"git ({status_label})",
'time': 'acum',
'timestamp': int(datetime.now().timestamp() * 1000),
'path': filepath,
'gitStatus': status_clean
})
except:
pass
# 3. Recent files in kb/ (last 24h)
try:
kb_dir = workspace / 'kb'

4
dashboard/favicon.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<circle cx="16" cy="16" r="14" fill="none" stroke="#3b82f6" stroke-width="2.5"/>
<circle cx="16" cy="16" r="3" fill="#3b82f6"/>
</svg>

After

Width:  |  Height:  |  Size: 200 B

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<title>Echo · Files</title>
<link rel="stylesheet" href="common.css">
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
@@ -294,6 +295,41 @@
background: var(--accent-subtle);
}
.file-item.git-changed {
border-left: 3px solid var(--warning);
}
.git-badge {
display: inline-block;
font-size: 10px;
font-weight: 700;
padding: 1px 4px;
border-radius: 3px;
margin-right: 4px;
font-family: var(--font-mono);
}
.git-modified { background: #3b82f620; color: #3b82f6; }
.git-added { background: #22c55e20; color: #22c55e; }
.git-deleted { background: #ef444420; color: #ef4444; }
.git-untracked { background: #f59e0b20; color: #f59e0b; }
.git-renamed { background: #8b5cf620; color: #8b5cf6; }
.diff-btn {
background: var(--accent);
color: white;
border: none;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
font-family: var(--font-mono);
}
.diff-btn:hover {
opacity: 0.8;
}
.file-icon {
width: 40px;
height: 40px;
@@ -419,6 +455,9 @@
.preview-active #markdownPreview { display: block; }
.btn-preview.active { background: var(--accent); color: white; }
.btn-diff.active { background: var(--warning); color: white; }
#gitFilterBtn.active { background: var(--warning); color: white; }
.editor-footer {
padding: var(--space-2) var(--space-5);
@@ -493,6 +532,13 @@
<span class="breadcrumb-item current" onclick="loadPath('')">~/clawd</span>
</div>
<div class="toolbar-actions">
<!-- Git Filter Toggle -->
<div class="view-toggle">
<button class="view-btn" id="gitFilterBtn" onclick="toggleGitFilter()" title="Git Changes">
<i data-lucide="git-branch"></i>
</button>
</div>
<!-- View Mode Toggle -->
<div class="view-toggle" id="viewModeToggle">
<button class="view-btn" data-view="list" onclick="setViewMode('list')" title="Listă">
@@ -554,6 +600,9 @@
<button class="btn btn-ghost btn-preview" onclick="togglePreview()" id="previewBtn" style="display:none;" title="Preview Markdown">
<i data-lucide="eye"></i>
</button>
<button class="btn btn-ghost btn-diff" onclick="toggleDiff()" id="diffBtn" style="display:none;" title="Git Diff">
<i data-lucide="git-compare"></i>
</button>
<button class="btn btn-ghost" onclick="reloadFile()" id="reloadBtn" disabled title="Reload">
<i data-lucide="refresh-cw"></i>
</button>
@@ -611,6 +660,8 @@
let currentSortBy = localStorage.getItem('filesSortBy') || 'name';
let currentSortDir = localStorage.getItem('filesSortDir') || 'asc';
let currentItems = [];
let gitStatus = {}; // Map of filepath -> status (M, A, D, ??)
let gitOnlyMode = false; // Show only git-changed files
// Initialize view mode
function initViewMode() {
@@ -690,6 +741,63 @@
document.getElementById('editorBtn').classList.add('active');
}
async function loadGitStatus() {
try {
const response = await fetch(`${API_BASE}/api/git?` + Date.now());
const data = await response.json();
gitStatus = {};
if (data.uncommittedParsed) {
data.uncommittedParsed.forEach(item => {
gitStatus[item.path] = item.status;
});
}
} catch (e) {
console.error('Failed to load git status:', e);
}
}
async function showDiff(filepath, event) {
if (event) event.stopPropagation();
try {
const response = await fetch(`${API_BASE}/api/diff?path=${encodeURIComponent(filepath)}`);
const data = await response.json();
// Show in a modal or the editor
const diffHtml = data.diff
.split('\n')
.map(line => {
if (line.startsWith('+') && !line.startsWith('+++')) {
return `<span style="color:var(--success)">${escapeHtml(line)}</span>`;
} else if (line.startsWith('-') && !line.startsWith('---')) {
return `<span style="color:var(--error)">${escapeHtml(line)}</span>`;
} else if (line.startsWith('@@')) {
return `<span style="color:var(--accent)">${escapeHtml(line)}</span>`;
}
return escapeHtml(line);
})
.join('\n');
// Open file in editor with diff view
document.getElementById('editorFileName').textContent = `DIFF: ${filepath}`;
document.getElementById('codeEditor').value = data.diff;
document.getElementById('markdownPreview').innerHTML = `<pre style="font-family:var(--font-mono);font-size:13px;line-height:1.5">${diffHtml}</pre>`;
document.getElementById('editorBody').classList.add('preview-active');
document.getElementById('previewBtn').style.display = 'flex';
document.getElementById('previewBtn').classList.add('active');
document.getElementById('saveBtn').disabled = true;
document.getElementById('reloadBtn').disabled = true;
currentFile = null;
setStatus('Diff view', 'saved');
showEditor();
} catch (e) {
alert('Eroare la încărcare diff: ' + e.message);
}
}
function escapeHtml(text) {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
async function loadPath(path = '') {
currentPath = path;
updateBreadcrumb();
@@ -704,6 +812,7 @@
}
if (data.type === 'dir') {
await loadGitStatus(); // Refresh git status
renderFileGrid(data.items);
updateURL(path);
} else if (data.type === 'file') {
@@ -796,37 +905,42 @@
const fileType = item.type === 'dir' ? 'Folder' : getFileType(item.name);
const sizeStr = item.size !== undefined ? formatSize(item.size) : '-';
// Git status
const gStatus = gitStatus[item.path] || '';
const gitBadge = gStatus ? getGitBadge(gStatus) : '';
const hasGitChange = !!gStatus;
if (currentViewMode === 'details') {
return `
<div class="file-item ${currentFile === item.path ? 'active' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
<div class="file-item ${currentFile === item.path ? 'active' : ''} ${hasGitChange ? 'git-changed' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
</div>
<div class="file-name">${item.name}</div>
<div class="file-name">${gitBadge}${item.name}</div>
<div class="file-meta">
<span class="file-type">${fileType}</span>
<span class="file-size">${sizeStr}</span>
<span class="file-date">${dateStr || '-'}</span>
<span class="file-date">${hasGitChange ? `<button class="diff-btn" onclick="showDiff('${item.path}', event)" title="Vezi diff">diff</button>` : (dateStr || '-')}</span>
</div>
</div>
`;
} else if (currentViewMode === 'list') {
return `
<div class="file-item ${currentFile === item.path ? 'active' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
<div class="file-item ${currentFile === item.path ? 'active' : ''} ${hasGitChange ? 'git-changed' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
</div>
<div class="file-name">${item.name}</div>
<div class="file-name">${gitBadge}${item.name}</div>
</div>
`;
} else {
// Tiles view - original style
return `
<div class="file-item ${currentFile === item.path ? 'active' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
<div class="file-item ${currentFile === item.path ? 'active' : ''} ${hasGitChange ? 'git-changed' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
</div>
<div class="file-name">${item.name}</div>
<div class="file-name">${gitBadge}${item.name}</div>
</div>
`;
}
@@ -881,6 +995,17 @@
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
function getGitBadge(status) {
const badges = {
'M': '<span class="git-badge git-modified" title="Modificat">M</span>',
'A': '<span class="git-badge git-added" title="Adăugat">A</span>',
'D': '<span class="git-badge git-deleted" title="Șters">D</span>',
'??': '<span class="git-badge git-untracked" title="Nou (untracked)">+</span>',
'R': '<span class="git-badge git-renamed" title="Redenumit">R</span>',
};
return badges[status] || `<span class="git-badge">${status}</span>`;
}
function handleClick(path, type) {
if (type === 'dir') {
loadPath(path);
@@ -905,6 +1030,10 @@
const isMarkdown = path.endsWith('.md');
document.getElementById('previewBtn').style.display = isMarkdown ? 'flex' : 'none';
// Always show diff button - let user check if file has changes
document.getElementById('diffBtn').style.display = 'flex';
document.getElementById('diffBtn').classList.remove('active');
// Auto-activate preview for markdown files
if (isMarkdown) {
const preview = document.getElementById('markdownPreview');
@@ -929,6 +1058,7 @@
function togglePreview() {
const editorBody = document.getElementById('editorBody');
const previewBtn = document.getElementById('previewBtn');
const diffBtn = document.getElementById('diffBtn');
const preview = document.getElementById('markdownPreview');
const content = document.getElementById('codeEditor').value;
@@ -936,16 +1066,69 @@
// Switch to edit mode
editorBody.classList.remove('preview-active');
previewBtn.classList.remove('active');
if (diffBtn) diffBtn.classList.remove('active');
setStatus('Edit mode', 'saved');
} else {
// Switch to preview mode
preview.innerHTML = marked.parse(content);
editorBody.classList.add('preview-active');
previewBtn.classList.add('active');
if (diffBtn) diffBtn.classList.remove('active');
setStatus('Preview mode', 'saved');
}
}
async function toggleDiff() {
if (!currentFile) return;
const editorBody = document.getElementById('editorBody');
const diffBtn = document.getElementById('diffBtn');
const previewBtn = document.getElementById('previewBtn');
const preview = document.getElementById('markdownPreview');
// If already showing diff, switch back to edit
if (diffBtn.classList.contains('active')) {
editorBody.classList.remove('preview-active');
diffBtn.classList.remove('active');
setStatus('Edit mode', 'saved');
return;
}
try {
setStatus('Se încarcă diff...', 'modified');
const response = await fetch(`${API_BASE}/api/diff?path=${encodeURIComponent(currentFile)}`);
const data = await response.json();
if (data.error) {
setStatus('Eroare: ' + data.error, 'error');
return;
}
// Format diff with colors
const diffHtml = data.diff
.split('\n')
.map(line => {
if (line.startsWith('+') && !line.startsWith('+++')) {
return `<span style="color:var(--success)">${escapeHtml(line)}</span>`;
} else if (line.startsWith('-') && !line.startsWith('---')) {
return `<span style="color:var(--error)">${escapeHtml(line)}</span>`;
} else if (line.startsWith('@@')) {
return `<span style="color:var(--accent)">${escapeHtml(line)}</span>`;
}
return escapeHtml(line);
})
.join('\n');
preview.innerHTML = `<pre style="font-family:var(--font-mono);font-size:13px;line-height:1.5;white-space:pre-wrap">${diffHtml || 'Nicio modificare față de ultima versiune comisă.'}</pre>`;
editorBody.classList.add('preview-active');
diffBtn.classList.add('active');
if (previewBtn) previewBtn.classList.remove('active');
setStatus('Diff view', 'saved');
} catch (e) {
setStatus('Eroare: ' + e.message, 'error');
}
}
async function saveFile() {
if (!currentFile) return;
@@ -1028,7 +1211,68 @@
// Init
initViewMode();
loadPath(getPathFromURL());
// Check for git mode
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('git') === '1') {
gitOnlyMode = true;
loadGitChangedFiles();
} else {
loadPath(getPathFromURL());
}
function toggleGitFilter() {
gitOnlyMode = !gitOnlyMode;
const btn = document.getElementById('gitFilterBtn');
btn.classList.toggle('active', gitOnlyMode);
if (gitOnlyMode) {
loadGitChangedFiles();
} else {
// Return to normal browse
window.history.replaceState(null, '', 'files.html');
loadPath('');
}
}
async function loadGitChangedFiles() {
await loadGitStatus();
const changedPaths = Object.keys(gitStatus);
// Update button state
document.getElementById('gitFilterBtn').classList.add('active');
if (changedPaths.length === 0) {
document.getElementById('breadcrumb').innerHTML = `
<span class="breadcrumb-item" onclick="toggleGitFilter()">~/clawd</span>
<span class="breadcrumb-sep"><i data-lucide="chevron-right"></i></span>
<span class="breadcrumb-item current" style="color:var(--success)">✓ Git curat</span>
`;
lucide.createIcons();
showError('Nicio modificare git - totul e comis!');
return;
}
// Update breadcrumb
document.getElementById('breadcrumb').innerHTML = `
<span class="breadcrumb-item" onclick="toggleGitFilter()">~/clawd</span>
<span class="breadcrumb-sep"><i data-lucide="chevron-right"></i></span>
<span class="breadcrumb-item current" style="color:var(--warning)">🔸 Git Changes (${changedPaths.length})</span>
`;
lucide.createIcons();
// Create virtual items for changed files
const items = changedPaths.map(path => ({
name: path,
path: path,
type: 'file',
size: null,
mtime: null
}));
currentItems = items;
renderFileGrid(items);
}
</script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<title>Echo · Dashboard</title>
<link rel="stylesheet" href="common.css">
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
@@ -14,61 +15,6 @@
padding: var(--space-5);
}
/* Stats Summary */
.stats-summary {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-3);
margin-bottom: var(--space-4);
}
@media (max-width: 768px) {
.stats-summary {
grid-template-columns: repeat(2, 1fr);
}
}
.stat-card {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-4);
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
}
.stat-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--accent-subtle);
border-radius: var(--radius-md);
color: var(--accent);
}
.stat-icon svg {
width: 20px;
height: 20px;
}
.stat-content {
flex: 1;
}
.stat-value {
font-size: var(--text-xl);
font-weight: 700;
color: var(--text-primary);
}
.stat-label {
font-size: var(--text-xs);
color: var(--text-muted);
}
.page-header {
margin-bottom: var(--space-4);
}
@@ -518,6 +464,11 @@
color: #f97316;
}
.activity-icon.git-file {
background: rgba(234, 179, 8, 0.2);
color: #eab308;
}
.activity-icon.file {
background: rgba(20, 184, 166, 0.2);
color: #14b8a6;
@@ -1000,38 +951,6 @@
</div>
</div>
<!-- Stats Summary -->
<div class="stats-summary" id="statsSummary">
<div class="stat-card" title="Task-uri completate azi">
<div class="stat-icon"><i data-lucide="check-circle"></i></div>
<div class="stat-content">
<div class="stat-value" id="statToday">0</div>
<div class="stat-label">Tasks azi</div>
</div>
</div>
<div class="stat-card" title="Task-uri completate săptămâna aceasta">
<div class="stat-icon"><i data-lucide="calendar"></i></div>
<div class="stat-content">
<div class="stat-value" id="statWeek">0</div>
<div class="stat-label">Tasks săpt.</div>
</div>
</div>
<div class="stat-card" title="Task-uri completate luna aceasta">
<div class="stat-icon"><i data-lucide="trending-up"></i></div>
<div class="stat-content">
<div class="stat-value" id="statMonth">0</div>
<div class="stat-label">Tasks lună</div>
</div>
</div>
<div class="stat-card" title="Număr de fișiere insights">
<div class="stat-icon"><i data-lucide="lightbulb"></i></div>
<div class="stat-content">
<div class="stat-value" id="statInsights">0</div>
<div class="stat-label">Insights</div>
</div>
</div>
</div>
<div class="dashboard-grid">
<!-- Activity Panel -->
<div class="panel activity-panel" id="activityPanel">
@@ -1297,40 +1216,28 @@
// Update details
const details = document.getElementById('gitDetails');
const GITEA_URL = 'https://gitea.romfast.ro/romfast/clawd';
let html = `
<div class="status-detail-item">
<i data-lucide="git-branch"></i>
<span>Branch: <strong>${git.branch}</strong></span>
</div>
<div class="status-detail-item">
<i data-lucide="git-commit"></i>
<span>Last: <code>${git.lastCommit.hash}</code> ${git.lastCommit.message} (${git.lastCommit.time})</span>
<span><a href="${GITEA_URL}/commit/${git.lastCommit.hash}" target="_blank" style="color:var(--accent)">${git.lastCommit.hash}</a> ${git.lastCommit.message.substring(0, 40)}${git.lastCommit.message.length > 40 ? '...' : ''} <small>(${git.lastCommit.time})</small></span>
</div>
`;
if (git.uncommittedCount > 0) {
const files = git.uncommitted.slice(0, 3).map(f => f.trim().split(' ').pop()).join(', ');
const more = git.uncommittedCount > 3 ? ` +${git.uncommittedCount - 3}` : '';
html += `<div class="status-detail-item uncommitted">
<i data-lucide="alert-circle"></i>
<span><strong>${git.uncommittedCount}</strong> fișiere necomise:</span>
</div>`;
git.uncommitted.slice(0, 5).forEach(file => {
html += `<div class="status-detail-item uncommitted">
<i data-lucide="file"></i>
<code>${file}</code>
</div>`;
});
if (git.uncommittedCount > 5) {
html += `<div class="status-detail-item uncommitted">
<span>... și încă ${git.uncommittedCount - 5}</span>
</div>`;
}
} else {
html += `<div class="status-detail-item">
<i data-lucide="check-circle"></i>
<span>Totul comis ✓</span>
<span><a href="files.html?git=1" style="color:var(--warning)"><strong>${git.uncommittedCount}</strong> necomise</a>: <small>${files}${more}</small></span>
</div>`;
}
html += `<div class="status-detail-item">
<i data-lucide="external-link"></i>
<a href="${GITEA_URL}" target="_blank" style="color:var(--accent);font-size:var(--text-xs)">gitea.romfast.ro/romfast/clawd</a>
</div>`;
details.innerHTML = html;
lucide.createIcons();
@@ -1354,7 +1261,9 @@
badge.className = 'status-badge ' + (status.anaf.ok !== false ? 'ok' : 'warning');
const subtitle = document.getElementById('anafSubtitle');
subtitle.textContent = status.anaf.message || 'Nicio modificare detectată';
const lastCheck = status.anaf.lastCheck || '-';
const msg = status.anaf.ok !== false ? 'Nicio modificare' : (status.anaf.message || 'Modificări!');
subtitle.textContent = `${msg} · ${lastCheck}`;
if (status.anaf.lastCheck) {
document.getElementById('anafLastCheck').textContent =
@@ -1542,51 +1451,9 @@
function refreshActivity() {
loadActivity();
loadStats();
showToast('Activitate reîmprospătată');
}
async function loadStats() {
try {
// Load tasks
const tasksRes = await fetch('tasks.json');
const tasksData = await tasksRes.json();
// Get done tasks
const doneColumn = tasksData.columns.find(c => c.id === 'done');
const doneTasks = doneColumn ? doneColumn.tasks : [];
const now = new Date();
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const weekStart = new Date(todayStart);
weekStart.setDate(weekStart.getDate() - weekStart.getDay() + 1); // Monday
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
let today = 0, week = 0, month = 0;
doneTasks.forEach(task => {
if (!task.completed) return;
const completed = new Date(task.completed);
if (completed >= todayStart) today++;
if (completed >= weekStart) week++;
if (completed >= monthStart) month++;
});
document.getElementById('statToday').textContent = today;
document.getElementById('statWeek').textContent = week;
document.getElementById('statMonth').textContent = month;
// Count insights
const insightsRes = await fetch('/echo/api/files?path=kb/insights');
if (insightsRes.ok) {
const insightsData = await insightsRes.json();
document.getElementById('statInsights').textContent = insightsData.files ? insightsData.files.length : 0;
}
} catch (e) {
console.log('Stats load error:', e);
}
}
function renderActivity() {
const body = document.getElementById('activityBody');
@@ -1607,6 +1474,7 @@
const typeLabels = {
'cron': '⏰ Cron Jobs',
'git': '📦 Git Commits',
'git-file': '🔸 Git Changes',
'file': '📄 Fișiere',
'task': '✅ Task-uri'
};
@@ -1617,13 +1485,22 @@
<i data-lucide="activity"></i>
Ultimele 24h
</div>
${activityData.map(item => `
<div class="activity-item" ${item.path ? `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"` : ''}>
${activityData.map(item => {
let clickAttr = '';
if (item.type === 'git-file' && item.path) {
clickAttr = `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"`;
} else if (item.path) {
clickAttr = `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"`;
} else if (item.type === 'git' && item.commitHash) {
clickAttr = `onclick="window.open('https://gitea.romfast.ro/romfast/clawd/commit/${item.commitHash}', '_blank')" style="cursor:pointer"`;
}
return `
<div class="activity-item" ${clickAttr}>
<div class="activity-icon ${item.type}">
<i data-lucide="${item.icon || 'activity'}"></i>
</div>
<div class="activity-content">
<div class="activity-text">${item.text}</div>
<div class="activity-text">${item.type === 'git' && item.commitHash ? `<code style="font-size:10px;margin-right:4px">${item.commitHash}</code>` : ''}${item.text}</div>
<div class="activity-meta">
<span class="activity-type">${typeLabels[item.type] || item.type}</span>
<span class="activity-agent">${item.agent}</span>
@@ -1631,7 +1508,7 @@
</div>
</div>
</div>
`).join('')}
`}).join('')}
</div>
`;
lucide.createIcons();
@@ -1867,7 +1744,6 @@
loadStatus();
loadIssues();
loadActivity();
loadStats();
</script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<title>Echo · KB</title>
<link rel="stylesheet" href="common.css">
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
@@ -1203,7 +1204,7 @@
try {
let content = notesCache[file];
if (!content) {
const response = await fetch(file);
const response = await fetch(file + '?t=' + Date.now());
content = await response.text();
notesCache[file] = content;
}
@@ -1222,7 +1223,7 @@
async function preloadNotes() {
for (const note of notesIndex) {
try {
const response = await fetch(note.file);
const response = await fetch(note.file + '?t=' + Date.now());
notesCache[note.file] = await response.text();
} catch (e) {
notesCache[note.file] = '';

View File

@@ -1,4 +1,19 @@
{
"git": {"status": "4 fișiere", "clean": false, "files": 4},
"lastReport": {"type": "evening", "summary": "notes.html îmbunătățit (filtre colorate), rețetă salvată", "time": "30 Jan 2026, 22:00"}
}
"git": {
"status": "4 fișiere",
"clean": false,
"files": 4
},
"lastReport": {
"type": "evening",
"summary": "notes.html îmbunătățit (filtre colorate), rețetă salvată",
"time": "30 Jan 2026, 22:00"
},
"anaf": {
"ok": true,
"status": "OK",
"message": "Nicio modificare detectată",
"lastCheck": "31 Jan 2026, 13:43",
"changesCount": 0
}
}