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

@@ -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>