Upload via GUI (85 Dateien)

This commit is contained in:
2026-10-22 09:13:21 +00:00
parent 2ad8777311
commit f4c16b173f
23 changed files with 1982 additions and 539 deletions
+45
View File
@@ -0,0 +1,45 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; script-src 'self'">
<title>Git Manager</title>
<style>
html, body { margin: 0; height: 100%; overflow: hidden; background: transparent; user-select: none; cursor: default; }
body { font-family: "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif; }
/* Logo und Schriftzug in der Mitte */
.marke {
position: fixed; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
transition: opacity .28s ease, transform .32s ease;
}
.buehne { perspective: 700px; animation: rein .45s cubic-bezier(.2, .9, .3, 1.2) both; }
.logo {
width: 132px; height: 132px; display: block; object-fit: contain;
filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .55)) drop-shadow(0 0 22px rgba(90, 160, 255, .35));
animation: drehen 1.3s cubic-bezier(.45, .05, .25, 1) .15s infinite;
}
.name {
font-size: 34px; font-weight: 600; letter-spacing: .06em;
background: linear-gradient(90deg, #7fe3ff, #6c9cff); -webkit-background-clip: text; background-clip: text; color: transparent;
filter: drop-shadow(0 2px 8px rgba(0, 0, 0, .8));
animation: schrift .5s ease .25s both;
}
@keyframes rein { from { opacity: 0; transform: scale(.6); } to { opacity: 1; transform: none; } }
@keyframes drehen { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } }
@keyframes schrift { from { opacity: 0; transform: translateY(8px); letter-spacing: .3em; } to { opacity: 1; transform: none; letter-spacing: .06em; } }
/* das fertige Fenster als Bild, wächst von der Mitte nach außen */
#fenster { position: fixed; inset: 0; width: 100%; height: 100%; visibility: hidden; }
body.aufbau .marke { opacity: 0; transform: scale(.82); }
</style>
</head>
<body>
<div class="marke">
<div class="buehne"><img class="logo" src="./icon.png" alt=""></div>
<div class="name">Git Manager</div>
</div>
<img id="fenster" alt="">
<script src="./splash.js"></script>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
// Vom Hauptprozess aufgerufen: das Bild des fertigen Fensters von der Mitte aus aufziehen.
// Liefert true, wenn die Animation durchgelaufen ist, sonst false.
window.aufbauen = (bildAdresse) => new Promise((fertig) => {
const bild = document.getElementById('fenster');
bild.onerror = () => fertig(false);
bild.onload = () => {
const v = Math.max(0, innerHeight / 2 - 66);
const h = Math.max(0, innerWidth / 2 - 66);
bild.style.visibility = 'visible';
document.body.classList.add('aufbau');
bild.animate([
{ clipPath: `inset(${v}px ${h}px round 30px)`, opacity: 0.2 },
{ opacity: 1, offset: 0.25 },
{ clipPath: 'inset(0px 0px round 0px)', opacity: 1 }
], { duration: 560, easing: 'cubic-bezier(.2, .8, .2, 1)', fill: 'forwards' })
.finished.then(() => fertig(true), () => fertig(false));
};
bild.src = bildAdresse;
});
+4
View File
@@ -4576,3 +4576,7 @@ select option:checked { background: #2d4ea4; color: #ffffff; }
color: #04101f !important;
border-color: transparent !important;
}
.upload-history-row { padding: 6px 8px; border-radius: 8px; cursor: pointer; }
.upload-history-row:hover { background: rgba(88, 213, 255, 0.14); }
.upload-history-err { opacity: 0.75; padding-left: 22px; }
+71
View File
@@ -0,0 +1,71 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'self'">
<title>Git Manager Menü</title>
<style>
:root {
--bg: #0f1a2e;
--fg: #e8eefc;
--muted: #8fa0c0;
--hover: rgba(88, 213, 255, 0.16);
--border: rgba(88, 213, 255, 0.24);
--sep: rgba(138, 164, 214, 0.22);
--accent: #67e8f9;
}
body.light {
--bg: #f7f9fd;
--fg: #16233d;
--muted: #6a7a99;
--hover: rgba(40, 110, 220, 0.12);
--border: rgba(30, 60, 110, 0.2);
--sep: rgba(30, 60, 110, 0.14);
--accent: #1d6fe0;
}
html, body {
margin: 0;
background: transparent;
color: var(--fg);
font: 13px "Segoe UI Variable Text", "Segoe UI", Arial, sans-serif;
user-select: none;
overflow: hidden;
}
body { padding: 8px; }
#menu {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 6px;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
}
.header { font-weight: 700; padding: 8px 12px 2px; }
.status { color: var(--muted); font-size: 12px; padding: 2px 12px; }
.sep { height: 1px; background: var(--sep); margin: 6px 4px; }
.item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
height: 32px;
padding: 0 12px;
box-sizing: border-box;
border: 0;
border-radius: 8px;
background: transparent;
color: inherit;
font: inherit;
text-align: left;
cursor: default;
outline: none;
}
.item:hover:not(:disabled), .item.active:not(:disabled) { background: var(--hover); }
.item:disabled { color: var(--muted); opacity: 0.6; }
.check { width: 14px; flex: 0 0 14px; text-align: center; color: var(--accent); font-weight: 700; }
</style>
</head>
<body>
<div id="menu" role="menu"></div>
<script src="./tray-menu.js"></script>
</body>
</html>
+80
View File
@@ -0,0 +1,80 @@
// Zeichnet das Tray-Menü aus dem Zustand, den der Hauptprozess schickt.
// Ohne Electron (Seite direkt im Browser geöffnet) greift ein Stub, damit sich die Optik prüfen lässt.
(function () {
const api = window.trayApi || {
onState: () => {},
action: (id) => console.log('action', id),
resize: () => {},
close: () => {}
};
const menu = document.getElementById('menu');
let buttons = [];
let activeIndex = -1;
function setActive(index) {
activeIndex = index;
buttons.forEach((b, i) => b.classList.toggle('active', i === index));
}
function render(state) {
document.body.classList.toggle('light', state && state.dark === false);
menu.textContent = '';
buttons = [];
activeIndex = -1;
for (const item of (state && state.items) || []) {
if (item.type === 'separator') {
const sep = document.createElement('div');
sep.className = 'sep';
menu.appendChild(sep);
continue;
}
if (item.id === 'header' || item.id === 'status' || item.id === 'queue') {
const line = document.createElement('div');
line.className = item.id === 'header' ? 'header' : 'status';
line.textContent = item.label;
menu.appendChild(line);
continue;
}
const button = document.createElement('button');
button.className = 'item';
button.disabled = item.enabled === false;
button.setAttribute('role', item.type === 'checkbox' ? 'menuitemcheckbox' : 'menuitem');
if (item.type === 'checkbox') button.setAttribute('aria-checked', String(!!item.checked));
const check = document.createElement('span');
check.className = 'check';
check.textContent = item.type === 'checkbox' && item.checked ? '✓' : '';
const label = document.createElement('span');
label.textContent = item.label;
button.append(check, label);
button.addEventListener('click', () => api.action(item.id));
button.addEventListener('mouseenter', () => { if (!button.disabled) setActive(buttons.indexOf(button)); });
button.addEventListener('mouseleave', () => setActive(-1));
menu.appendChild(button);
buttons.push(button);
}
api.resize(Math.ceil(document.body.scrollHeight));
}
function move(step) {
const enabled = buttons.map((b, i) => (b.disabled ? -1 : i)).filter((i) => i >= 0);
if (enabled.length === 0) return;
const pos = enabled.indexOf(activeIndex);
const next = pos < 0 ? (step > 0 ? 0 : enabled.length - 1) : (pos + step + enabled.length) % enabled.length;
setActive(enabled[next]);
}
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') api.close();
else if (e.key === 'ArrowDown') { e.preventDefault(); move(1); }
else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1); }
else if ((e.key === 'Enter' || e.key === ' ') && buttons[activeIndex]) { e.preventDefault(); buttons[activeIndex].click(); }
});
window.__renderTray = render;
api.onState(render);
})();