Files
Git-Manager-Gui/renderer/renderer.js
2025-12-28 18:06:53 +01:00

621 lines
25 KiB
JavaScript

// renderer.js — Explorer with repo drag-export and folder upload+git push + progress UI
const $ = id => document.getElementById(id);
let selectedFolder = null;
let giteaCache = {}; // cache repo contents
function setStatus(txt) { const s = $('status'); if (s) s.innerText = txt || ''; }
/* -------------------------
Small dynamic progress UI (created if not present)
------------------------- */
function ensureProgressUI() {
if (document.getElementById('folderProgressContainer')) return;
const container = document.createElement('div');
container.id = 'folderProgressContainer';
container.style.position = 'fixed';
container.style.left = '50%';
container.style.top = '12px';
container.style.transform = 'translateX(-50%)';
container.style.zIndex = '10000';
container.style.width = '480px';
container.style.maxWidth = '90%';
container.style.padding = '6px';
container.style.background = 'rgba(20,20,30,0.95)';
container.style.borderRadius = '8px';
container.style.boxShadow = '0 6px 18px rgba(0,0,0,0.45)';
container.style.color = '#fff';
container.style.fontFamily = 'sans-serif';
container.style.display = 'none';
const text = document.createElement('div');
text.id = 'folderProgressText';
text.style.marginBottom = '6px';
text.style.fontSize = '13px';
container.appendChild(text);
const barWrap = document.createElement('div');
barWrap.style.width = '100%';
barWrap.style.height = '10px';
barWrap.style.background = '#333';
barWrap.style.borderRadius = '6px';
barWrap.style.overflow = 'hidden';
const bar = document.createElement('div');
bar.id = 'folderProgressBar';
bar.style.width = '0%';
bar.style.height = '100%';
bar.style.background = '#4caf50';
bar.style.transition = 'width 150ms linear';
barWrap.appendChild(bar);
container.appendChild(barWrap);
document.body.appendChild(container);
}
function showProgress(percent, text) {
ensureProgressUI();
const container = document.getElementById('folderProgressContainer');
const bar = document.getElementById('folderProgressBar');
const txt = document.getElementById('folderProgressText');
txt.innerText = text || '';
bar.style.width = `${percent}%`;
container.style.display = 'block';
}
function hideProgress() {
const container = document.getElementById('folderProgressContainer');
if (container) container.style.display = 'none';
}
/* -------------------------
Initialization & wiring
------------------------- */
async function init() {
const creds = await window.electronAPI.loadCredentials();
if (creds) {
if ($('githubToken')) $('githubToken').value = creds.githubToken || '';
if ($('giteaToken')) $('giteaToken').value = creds.giteaToken || '';
if ($('giteaURL')) $('giteaURL').value = creds.giteaURL || '';
}
if ($('btnLoadGiteaRepos')) $('btnLoadGiteaRepos').addEventListener('click', loadGiteaRepos);
if ($('btnSelectFolder')) $('btnSelectFolder').addEventListener('click', selectLocalFolder);
if ($('btnPush')) $('btnPush').addEventListener('click', pushLocalFolder);
if ($('btnCreateRepo')) $('btnCreateRepo').addEventListener('click', createRepoHandler);
if ($('btnSettings')) $('btnSettings').addEventListener('click', () => $('settingsModal').classList.remove('hidden'));
if ($('btnCloseSettings')) $('btnCloseSettings').addEventListener('click', () => $('settingsModal').classList.add('hidden'));
if ($('btnSaveSettings')) $('btnSaveSettings').addEventListener('click', saveSettings);
// global drag-over/drop: prevent default
document.addEventListener('dragover', e => e.preventDefault());
document.addEventListener('drop', e => e.preventDefault());
// subscribe to progress events
window.electronAPI.onFolderUploadProgress((payload) => {
const { processed, total, percent } = payload;
showProgress(percent, `Upload: ${processed}/${total} (${percent}%)`);
if (processed >= total) setTimeout(hideProgress, 600);
});
window.electronAPI.onFolderDownloadProgress((payload) => {
const { processed, total, percent } = payload;
showProgress(percent, `Download: ${processed}/${total} (${percent}%)`);
if (processed >= total) setTimeout(hideProgress, 600);
});
window.electronAPI.onPushProgress(p => setStatus('Pushing... ' + p + '%'));
ensureProgressUI();
}
/* -------------------------
Settings / buttons
------------------------- */
async function saveSettings() {
const data = { githubToken: $('githubToken')?.value, giteaToken: $('giteaToken')?.value, giteaURL: $('giteaURL')?.value };
const r = await window.electronAPI.saveCredentials(data);
setStatus(r.ok ? 'Settings saved' : 'Save failed: ' + r.error);
$('settingsModal').classList.add('hidden');
}
async function loadGiteaRepos() {
setStatus('Loading Gitea repos...');
const creds = await window.electronAPI.loadCredentials();
if (!creds || !creds.giteaToken || !creds.giteaURL) { setStatus('Set Gitea token & URL in Settings'); return; }
const res = await window.electronAPI.listGiteaRepos({ token: creds.giteaToken, url: creds.giteaURL });
if (!res.ok) { setStatus('Failed to load repos: ' + res.error); return; }
renderGiteaRoots(res.repos);
setStatus(`Loaded ${res.repos.length} repos`);
}
async function selectLocalFolder() {
const folder = await window.electronAPI.selectFolder();
if (!folder) return;
selectedFolder = folder;
setStatus('Selected local folder: ' + folder);
await refreshLocalTree(folder);
await loadBranches(folder);
await loadCommitLogs(folder);
}
async function pushLocalFolder() {
if (!selectedFolder) return alert('Select local folder first');
const branch = $('branchSelect')?.value || 'main';
setStatus('Pushing...');
const res = await window.electronAPI.pushProject({ folder: selectedFolder, branch, repoName: $('repoName')?.value, platform: $('platform')?.value });
setStatus(res.ok ? 'Push succeeded' : 'Push failed: ' + res.error);
if (res.ok) loadCommitLogs(selectedFolder);
}
async function createRepoHandler() {
const name = $('repoName')?.value?.trim();
const platform = $('platform')?.value;
const license = $('licenseSelect')?.value || '';
const autoInit = $('createReadme')?.checked;
if (!name) return alert('Repo name required');
setStatus('Creating repo...');
const res = await window.electronAPI.createRepo({ name, platform, license, autoInit });
setStatus(res.ok ? 'Repo created' : 'Create failed: ' + res.error);
}
/* -------------------------
Render Gitea roots + draggable/droppable nodes
------------------------- */
function renderGiteaRoots(repos) {
const tree = $('fileTree');
const prev = tree.querySelector('[data-is-gitea-root="1"]');
if (prev) {
const next = prev.nextSibling;
prev.remove();
if (next && next.classList && next.classList.contains('gitea-children-wrapper')) next.remove();
}
const groot = document.createElement('div');
groot.className = 'file-node folder';
groot.dataset.isGiteaRoot = '1';
groot.innerText = 'Gitea Repositories';
groot.style.fontWeight = '800';
const wrapper = document.createElement('div');
wrapper.className = 'gitea-children-wrapper';
wrapper.style.marginLeft = '6px';
repos.forEach(repo => {
let owner = (repo.owner && (repo.owner.login || repo.owner.username)) || null;
let repoName = repo.name;
if (!owner && repo.full_name && repo.full_name.includes('/')) {
const parts = repo.full_name.split('/');
owner = parts[0]; repoName = parts[1];
}
const rdiv = document.createElement('div');
rdiv.className = 'file-node folder';
rdiv.innerText = (repo.full_name || `${owner}/${repoName}`);
rdiv.dataset.giteaOwner = owner;
rdiv.dataset.giteaRepo = repoName;
rdiv.dataset.giteaClone = repo.clone_url || repo.clone_url_ssh || (repo.html_url ? `${repo.html_url}.git` : '');
rdiv.style.paddingLeft = '8px';
rdiv.draggable = true;
// dragstart
rdiv.addEventListener('dragstart', async (ev) => {
ev.preventDefault();
setStatus(`Preparing download for ${owner}/${repoName} ...`);
showProgress(0, `Preparing ${owner}/${repoName}...`);
const res = await window.electronAPI.prepareDownloadDrag({ owner, repo: repoName, path: '' });
if (!res.ok) { setStatus('Prepare failed: ' + res.error); hideProgress(); return; }
const tempPath = res.tempPath;
window.electronAPI.startNativeDrag(tempPath);
setStatus('Drag started — drop to desktop or file manager.');
hideProgress();
});
// drop
rdiv.addEventListener('dragover', (ev) => { ev.preventDefault(); rdiv.classList.add('drag-target'); });
rdiv.addEventListener('dragleave', () => { rdiv.classList.remove('drag-target'); });
rdiv.addEventListener('drop', async (ev) => {
ev.preventDefault();
rdiv.classList.remove('drag-target');
const dt = ev.dataTransfer;
if (!dt || !dt.files || dt.files.length === 0) {
setStatus('No files/folders dropped.');
return;
}
const paths = Array.from(dt.files).map(f => f.path);
for (const p of paths) {
setStatus(`Uploading ${p} to ${owner}/${repoName} ...`);
showProgress(0, `Uploading ${ppathBasename(p)}${owner}/${repoName}`);
const res = await window.electronAPI.uploadAndPush({ localFolder: p, owner, repo: repoName, destPath: '', cloneUrl: rdiv.dataset.giteaClone, branch: 'main' });
if (!res.ok) setStatus('Upload failed: ' + res.error);
else setStatus(res.usedGit ? `Upload+Push OK (git used)` : `Upload OK (API used)`);
hideProgress();
}
try { await expandGiteaDir(rdiv, owner, repoName, rdiv.dataset.giteaPath || ''); } catch (_) {}
});
// Click - FIXED: ref: 'main' explicitly added
rdiv.addEventListener('click', async (ev) => {
ev.stopPropagation();
if (rdiv._expanded) {
const next = rdiv.nextSibling;
if (next && next.classList && next.classList.contains('gitea-children')) next.remove();
rdiv._expanded = false;
return;
}
setStatus(`Loading ${owner}/${repoName}...`);
const res = await window.electronAPI.getGiteaRepoContents({ owner, repo: repoName, path: '', ref: 'main' });
if (!res.ok) { setStatus('Failed to load repo: ' + res.error); return; }
const cont = document.createElement('div');
cont.className = 'gitea-children';
cont.style.marginLeft = '12px';
(res.items || []).forEach(item => {
const node = document.createElement('div');
node.className = 'file-node ' + (item.type === 'dir' ? 'folder' : 'file');
node.innerText = item.name;
node.dataset.giteaOwner = owner;
node.dataset.giteaRepo = repoName;
node.dataset.giteaPath = item.path;
node.style.paddingLeft = '8px';
node.addEventListener('dragover', e => e.preventDefault());
node.addEventListener('drop', async (e) => {
e.preventDefault();
const dt = e.dataTransfer;
if (!dt || !dt.files || dt.files.length === 0) { setStatus('No files dropped'); return; }
const dropped = Array.from(dt.files).map(f => f.path);
for (const p of dropped) {
setStatus(`Uploading ${p} to ${owner}/${repoName}/${item.path} ...`);
showProgress(0, `Uploading ${ppathBasename(p)}${owner}/${repoName}/${item.path}`);
const res = await window.electronAPI.uploadAndPush({ localFolder: p, owner, repo: repoName, destPath: item.path, cloneUrl: rdiv.dataset.giteaClone, branch: 'main' });
if (!res.ok) setStatus('Upload failed: ' + res.error);
else setStatus(res.usedGit ? `Upload+Push OK (git used)` : `Upload OK (API used)`);
hideProgress();
}
await expandGiteaDir(node, owner, repoName, item.path);
});
node.addEventListener('click', async (e) => {
e.stopPropagation();
if (item.type === 'dir') await expandGiteaDir(node, owner, repoName, item.path);
else await previewGiteaFile(owner, repoName, item.path);
document.querySelectorAll('.file-node').forEach(n => n.classList.remove('active'));
node.classList.add('active');
});
node.addEventListener('contextmenu', (ev) => {
ev.preventDefault();
showGiteaContextMenu(node, ev.clientX, ev.clientY);
});
cont.appendChild(node);
});
rdiv.after(cont);
rdiv._expanded = true;
setStatus('');
});
// Context menu for Repo
rdiv.addEventListener('contextmenu', (ev) => {
ev.preventDefault();
showRepoContextMenu(rdiv, ev.clientX, ev.clientY, owner, repoName, rdiv.dataset.giteaClone);
});
wrapper.appendChild(rdiv);
});
tree.prepend(groot);
groot.after(wrapper);
}
function ppathBasename(p) {
try { return p.split(/[\\/]/).pop(); } catch (_) { return p; }
}
/* Expand dir / render children / preview */
async function expandGiteaDir(nodeEl, owner, repo, dirPath) {
if (nodeEl._expanded) {
const next = nodeEl.nextSibling;
if (next && next.classList && next.classList.contains('gitea-sub')) next.remove();
nodeEl._expanded = false;
return;
}
const key = `${owner}/${repo}:${dirPath}`;
const ref = 'main'; // FIXED: Always use 'main'
if (giteaCache[key]) { renderGiteaChildren(nodeEl, giteaCache[key], owner, repo); nodeEl._expanded = true; return; }
setStatus(`Loading ${dirPath}...`);
const res = await window.electronAPI.getGiteaRepoContents({ owner, repo, path: dirPath, ref });
if (!res.ok) { setStatus('Failed: ' + res.error); return; }
giteaCache[key] = res.items;
renderGiteaChildren(nodeEl, res.items, owner, repo);
nodeEl._expanded = true;
setStatus('');
}
function renderGiteaChildren(parentNode, items, owner, repo) {
const container = document.createElement('div');
container.className = 'gitea-sub';
container.style.marginLeft = '12px';
items.forEach(item => {
const el = document.createElement('div');
el.className = 'file-node ' + (item.type === 'dir' ? 'folder' : 'file');
el.innerText = item.name;
el.dataset.giteaOwner = owner;
el.dataset.giteaRepo = repo;
el.dataset.giteaPath = item.path;
el.style.paddingLeft = '8px';
el.addEventListener('click', async (ev) => {
ev.stopPropagation();
if (item.type === 'dir') await expandGiteaDir(el, owner, repo, item.path);
else await previewGiteaFile(owner, repo, item.path);
document.querySelectorAll('.file-node').forEach(n => n.classList.remove('active'));
el.classList.add('active');
});
el.addEventListener('dragover', e => e.preventDefault());
el.addEventListener('drop', async (e) => {
e.preventDefault();
const paths = Array.from(e.dataTransfer.files).map(f => f.path);
for (const p of paths) {
setStatus(`Uploading ${p} to ${owner}/${repo}/${item.path} ...`);
showProgress(0, `Uploading ${ppathBasename(p)}${owner}/${repo}/${item.path}`);
const res = await window.electronAPI.uploadAndPush({ localFolder: p, owner, repo, destPath: item.path, cloneUrl: parentNode.previousSibling?.dataset?.giteaClone, branch: 'main' });
if (!res.ok) setStatus('Upload failed: ' + res.error);
else setStatus(res.usedGit ? `Upload+Push OK (git used)` : `Upload OK (API used)`);
hideProgress();
}
await expandGiteaDir(el, owner, repo, item.path);
});
el.addEventListener('contextmenu', (ev) => {
ev.preventDefault();
showGiteaContextMenu(el, ev.clientX, ev.clientY);
});
container.appendChild(el);
});
parentNode.after(container);
}
async function previewGiteaFile(owner, repo, filePath) {
setStatus(`Loading file ${filePath}...`);
const res = await window.electronAPI.getGiteaFileContent({ owner, repo, path: filePath, ref: 'main' });
if (!res.ok) { setStatus('Failed: ' + res.error); $('previewTitle').innerText = filePath; $('previewContent').innerText = ''; return; }
$('previewTitle').innerText = `${owner}/${repo}:${filePath}`;
$('previewContent').innerText = res.content;
setStatus('');
}
/* Local tree */
async function refreshLocalTree(folder) {
const res = await window.electronAPI.getFileTree({ folder, exclude: ['node_modules'], maxDepth: 5 });
const container = $('fileTree');
const existingLocal = container.querySelector('[data-local-root="1"]');
if (existingLocal) {
const next = existingLocal.nextSibling;
existingLocal.remove();
if (next && next.classList && next.classList.contains('local-children')) next.remove();
}
if (!res.ok) { setStatus('Local tree error: ' + res.error); return; }
const root = document.createElement('div');
root.className = 'file-node folder';
root.dataset.localRoot = '1';
root.innerText = 'Local: ' + folder.split(/[\\/]/).pop();
root.style.fontWeight = '800';
root.addEventListener('click', () => {
const next = root.nextSibling;
if (next && next.classList && next.classList.contains('local-children')) next.classList.toggle('hidden');
});
const wrapper = document.createElement('div');
wrapper.className = 'local-children';
wrapper.style.marginLeft = '8px';
function build(node, parent) {
const el = document.createElement('div');
el.className = 'file-node ' + (node.isDirectory ? 'folder' : 'file');
el.innerText = node.name;
el.dataset.path = node.path;
el.style.paddingLeft = (node.depth * 10 + 8) + 'px';
el.addEventListener('click', async (ev) => {
ev.stopPropagation();
if (!node.isDirectory) {
const r = await window.electronAPI.readFile({ path: node.path });
if (r.ok) { $('previewTitle').innerText = node.path; $('previewContent').innerText = r.content; }
else setStatus('Read failed: ' + r.error);
}
});
parent.appendChild(el);
if (node.children && node.children.length) node.children.forEach(c => build(c, parent));
}
res.tree.forEach(n => build(n, wrapper));
const tree = $('fileTree');
tree.appendChild(root);
root.after(wrapper);
}
/* Branches & commits */
async function loadBranches(folder) {
const res = await window.electronAPI.getBranches({ folder });
const sel = $('branchSelect');
sel.innerHTML = '';
if (res.ok) res.branches.forEach(b => sel.appendChild(new Option(b, b)));
else sel.appendChild(new Option('main','main'));
}
async function loadCommitLogs(folder) {
const res = await window.electronAPI.getCommitLogs({ folder });
const container = $('logs');
container.innerHTML = '';
if (!res.ok) return container.innerText = 'No logs or error: ' + res.error;
res.logs.forEach(l => { const d = document.createElement('div'); d.innerText = l; container.appendChild(d); });
}
/* Context menu */
function showGiteaContextMenu(node, x, y) {
const old = document.getElementById('ctxMenu');
if (old) old.remove();
const menu = document.createElement('div');
menu.id = 'ctxMenu';
menu.style.position = 'fixed';
menu.style.left = x + 'px';
menu.style.top = y + 'px';
menu.style.background = '#1b1b2a';
menu.style.border = '1px solid #333';
menu.style.padding = '6px';
menu.style.zIndex = '9999';
menu.style.borderRadius = '6px';
menu.style.color = '#fff';
const owner = node.dataset.giteaOwner;
const repo = node.dataset.giteaRepo;
const filePath = node.dataset.giteaPath || '';
if (node.classList.contains('file')) {
const dl = document.createElement('div'); dl.innerText = 'Download File'; dl.style.padding = '6px'; dl.style.cursor = 'pointer';
dl.onclick = async () => {
const res = await window.electronAPI.downloadGiteaFile({ owner, repo, path: filePath });
setStatus(res.ok ? `Saved to ${res.savedTo}` : 'Download failed: ' + res.error);
menu.remove();
};
menu.appendChild(dl);
}
if (node.classList.contains('folder')) {
const dlf = document.createElement('div'); dlf.innerText = 'Download Folder (recursive)'; dlf.style.padding = '6px'; dlf.style.cursor = 'pointer';
dlf.onclick = async () => {
setStatus(`Choose local folder to save ${owner}/${repo}/${filePath} ...`);
showProgress(0, 'Starting download...');
const res = await window.electronAPI.downloadGiteaFolder({ owner, repo, path: filePath });
setStatus(res.ok ? `Folder downloaded to ${res.savedTo}` : 'Folder download failed: ' + res.error);
hideProgress();
menu.remove();
};
menu.appendChild(dlf);
}
const up = document.createElement('div'); up.innerText = 'Upload Local Folder Here'; up.style.padding = '6px'; up.style.cursor = 'pointer';
up.onclick = async () => {
const folderSel = await window.electronAPI.selectFolder();
if (!folderSel) { setStatus('No folder selected'); menu.remove(); return; }
setStatus(`Uploading local folder ${folderSel} to ${owner}/${repo}/${filePath} ...`);
showProgress(0, 'Starting upload...');
const res = await window.electronAPI.uploadAndPush({ localFolder: folderSel, owner, repo, destPath: filePath, cloneUrl: node.dataset.giteaClone, branch: 'main' });
setStatus(res.ok ? (res.usedGit ? 'Folder uploaded and pushed (git used)' : 'Folder uploaded (API)') : 'Upload failed: ' + res.error);
hideProgress();
menu.remove();
if (node.classList.contains('folder')) await expandGiteaDir(node, owner, repo, filePath);
};
menu.appendChild(up);
if (node.classList.contains('file')) {
const upf = document.createElement('div'); upf.innerText = 'Upload & Overwrite File'; upf.style.padding = '6px'; upf.style.cursor = 'pointer';
upf.onclick = async () => {
const sel = await window.electronAPI.selectFile();
if (!sel.ok || !sel.files || sel.files.length === 0) { setStatus('No file selected'); menu.remove(); return; }
const files = sel.files;
setStatus(`Uploading ${files.length} file(s) to ${owner}/${repo}/${filePath} ...`);
showProgress(0, 'Uploading files...');
const res = await window.electronAPI.uploadGiteaFile({ localPath: files, owner, repo, destPath: filePath, message: 'Upload via GUI', branch: 'main' });
setStatus(res.ok ? 'Upload complete' : 'Upload failed: ' + res.error);
hideProgress();
menu.remove();
if (node.classList.contains('folder')) await expandGiteaDir(node, owner, repo, filePath);
};
menu.appendChild(upf);
}
document.body.appendChild(menu);
document.addEventListener('click', function handler() { menu.remove(); document.removeEventListener('click', handler); });
}
/* Context menu for Repository Root */
function showRepoContextMenu(repoEl, x, y, owner, repoName, cloneUrl) {
const old = document.getElementById('ctxMenu');
if (old) old.remove();
const menu = document.createElement('div');
menu.id = 'ctxMenu';
menu.style.position = 'fixed';
menu.style.left = x + 'px';
menu.style.top = y + 'px';
menu.style.background = '#1b1b2a';
menu.style.border = '1px solid #333';
menu.style.padding = '6px';
menu.style.zIndex = '9999';
menu.style.borderRadius = '6px';
menu.style.color = '#fff';
// Option 1: Download Repository
const dlRepo = document.createElement('div');
dlRepo.innerText = 'Download Repository';
dlRepo.style.padding = '6px';
dlRepo.style.cursor = 'pointer';
dlRepo.style.borderBottom = '1px solid #333';
dlRepo.onclick = async () => {
setStatus(`Downloading repository ${owner}/${repoName}...`);
showProgress(0, 'Starting download...');
const res = await window.electronAPI.downloadGiteaFolder({ owner, repo: repoName, path: '' });
setStatus(res.ok ? `Repository downloaded to ${res.savedTo}` : 'Download failed: ' + res.error);
hideProgress();
menu.remove();
};
menu.appendChild(dlRepo);
// Option 2: Upload Folder to Repository
const upRepo = document.createElement('div');
upRepo.innerText = 'Upload Folder to Repository';
upRepo.style.padding = '6px';
upRepo.style.cursor = 'pointer';
upRepo.style.borderBottom = '1px solid #333';
upRepo.onclick = async () => {
const folderSel = await window.electronAPI.selectFolder();
if (!folderSel) { setStatus('No folder selected'); menu.remove(); return; }
setStatus(`Uploading local folder to ${owner}/${repoName} ...`);
showProgress(0, 'Starting upload...');
const res = await window.electronAPI.uploadAndPush({ localFolder: folderSel, owner, repo: repoName, destPath: '', cloneUrl: cloneUrl, branch: 'main' });
setStatus(res.ok ? (res.usedGit ? 'Upload & Push successful (git)' : 'Upload successful (API)') : 'Upload failed: ' + res.error);
hideProgress();
menu.remove();
if (repoEl._expanded) {
const next = repoEl.nextSibling;
if (next && next.classList && next.classList.contains('gitea-children')) {
next.remove();
repoEl._expanded = false;
repoEl.click(); // Re-expand
}
}
};
menu.appendChild(upRepo);
// Option 3: Delete Repository
const delRepo = document.createElement('div');
delRepo.innerText = 'Delete Repository';
delRepo.style.padding = '6px';
delRepo.style.cursor = 'pointer';
delRepo.style.color = '#ff5555';
delRepo.style.fontWeight = 'bold';
delRepo.onclick = async () => {
if (!confirm(`Are you sure you want to DELETE ${owner}/${repoName}? This cannot be undone.`)) {
menu.remove();
return;
}
setStatus(`Deleting repository ${owner}/${repoName}...`);
const res = await window.electronAPI.deleteGiteaRepo({ owner, repo: repoName });
if (res.ok) {
setStatus('Repository deleted successfully');
repoEl.remove();
} else {
setStatus('Delete failed: ' + res.error);
}
menu.remove();
};
menu.appendChild(delRepo);
document.body.appendChild(menu);
const closeHandler = () => {
menu.remove();
document.removeEventListener('click', closeHandler);
};
setTimeout(() => document.addEventListener('click', closeHandler), 10);
}
/* Start */
window.addEventListener('DOMContentLoaded', init);