Add settings dialog for Sky Mode and Skip Prompt, update main HTML and script
- Created `settings.html` with a dialog for adjusting settings related to Sky Mode and Skip Prompt. - Updated `main.html` to include context menus for folder operations. - Enhanced `main.js` with functionality to handle settings changes, apply Sky Mode, and add copy-to-clipboard feature for diffs. This commit message was automatically generated by auto-git
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
dist
|
||||
BIN
assets/cat/joy.png
Normal file
BIN
assets/cat/joy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/icon/linux/icon.png
Normal file
BIN
assets/icon/linux/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
assets/icon/mac/icon.icns
Normal file
BIN
assets/icon/mac/icon.icns
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
assets/icon/win/icon.ico
Normal file
BIN
assets/icon/win/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
114
index.html
114
index.html
@@ -5,9 +5,25 @@
|
||||
<title>auto-git</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
:root {
|
||||
/* Default-Modus: Weißes Panel mit Rosa-Akzenten */
|
||||
--bg-main: #fff; /* Hintergrund Content */
|
||||
--bg-sidebar: #fff1f2; /* Sidebar */
|
||||
--accent: #9f1239; /* Rosa */
|
||||
--border: #ffe4e6;
|
||||
}
|
||||
body.sky-mode {
|
||||
/* Sky-Mode: Himmelshintergrund und passende Akzente */
|
||||
--bg-main: rgb(173,216,230); /* sanftes Baby-Blau */
|
||||
--bg-sidebar: rgb(200,220,240); /* etwas dunkleres Blau */
|
||||
--accent: rgb(20,60,100); /* dunkles Marine-Blau als Akzent */
|
||||
--border: rgb(180,200,220);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
#catSlot img {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
@@ -18,6 +34,7 @@
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease;
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
/* Pfeil-Icon: 90°-Rotation */
|
||||
@@ -53,6 +70,7 @@
|
||||
button.disabled:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0%, 100% { box-shadow: 0 0 4px gold; }
|
||||
50% { box-shadow: 0 0 12px gold; }
|
||||
@@ -66,32 +84,104 @@
|
||||
border-style: solid !important;
|
||||
border-color: gold !important;
|
||||
}
|
||||
|
||||
#currentTitle {
|
||||
/* erlauben, dass der Text umbricht */
|
||||
white-space: normal;
|
||||
word-break: break-all; /* bricht auch mitten in langen Wörtern */
|
||||
overflow-wrap: anywhere; /* bricht an Notfällen wie '/' */
|
||||
/* keine Ellipse mehr */
|
||||
overflow: visible;
|
||||
/* falls der Parent eine horizontale Scrollbar bekommt, wird sie nicht vom Titel verursacht */
|
||||
}
|
||||
|
||||
/* Sicherstellen, dass der Content-Bereich nicht horizontal scrollt */
|
||||
.flex-1.p-4.overflow-y-auto {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* generelle List-Items in der Sidebar */
|
||||
aside ul#folderList li {
|
||||
background: transparent; /* kein festes Rosa mehr */
|
||||
color: var(--accent); /* Text- und Icon-Farbe */
|
||||
border-radius: 0.375rem; /* gleiche r-Klasse wie before */
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
/* Hover-Effekt */
|
||||
aside ul#folderList li:hover,
|
||||
aside ul#folderList li.selected {
|
||||
background: var(--border); /* statt hover:bg-[#fecdd3] */
|
||||
}
|
||||
|
||||
/* Icon und Text separat noch sicher auf var(--accent) setzen */
|
||||
aside ul#folderList li svg,
|
||||
aside ul#folderList li span {
|
||||
color: var(--accent) !important;
|
||||
}
|
||||
|
||||
.diff-container {
|
||||
position: relative;
|
||||
}
|
||||
.copy-diff-btn {
|
||||
all: unset !important;
|
||||
position: absolute !important;
|
||||
top: 0.25rem !important; /* knapp unter dem Container-Rand */
|
||||
right: 0.25rem !important;
|
||||
padding: 0.25rem !important;
|
||||
border-radius: 0.25rem !important;
|
||||
cursor: pointer !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.copy-diff-btn:hover {
|
||||
background: transparent !important;
|
||||
}
|
||||
/* Entferne ggf. globales pre-Top-Padding */
|
||||
.diff-container pre {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="flex h-screen bg-white">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-64 bg-[#fff1f2] border-r border-[#ffe4e6] flex flex-col">
|
||||
<h2 class="px-4 py-3 text-lg font-semibold text-gray-900">Monitored Folders</h2>
|
||||
<aside class="w-64 border-r flex flex-col"
|
||||
style="background: var(--bg-sidebar); border-color: var(--border)">
|
||||
<h2 class="px-4 py-3 text-lg font-semibold"
|
||||
style="color: var(--accent)">
|
||||
Monitored Folders
|
||||
</h2>
|
||||
<ul id="folderList" class="flex-1 px-2 space-y-2 overflow-y-auto"></ul>
|
||||
<button id="addFolderBtn" class="mx-4 mb-4 flex items-center justify-center space-x-2 px-3 py-2 bg-[#ffe4e6] rounded hover:bg-[#fecdd3]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-[#9f1239]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||||
<button id="addFolderBtn"
|
||||
class="mx-4 mb-4 flex items-center justify-center space-x-2 px-3 py-2 rounded"
|
||||
style="background: var(--border); color: var(--accent)"
|
||||
onmouseenter="this.style.background='var(--bg-sidebar)'"
|
||||
onmouseleave="this.style.background='var(--border)'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
style="color: var(--accent)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-[#9f1239]">Add Folder</span>
|
||||
<span class="text-sm font-medium" style="color: var(--accent)">Add Folder</span>
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<!-- Main panel -->
|
||||
<main class="relative flex flex-col flex-1 bg-white">
|
||||
<!-- Main panel: wieder p-4 wie gehabt -->
|
||||
<main class="relative flex flex-col flex-1" style="background: var(--bg-main)">
|
||||
<div class="flex-1 p-4 overflow-y-auto">
|
||||
<h3 id="currentTitle" class="text-xl font-semibold mb-2">No folder selected</h3>
|
||||
<div class="border-t border-[#ffe4e6] mb-4"></div>
|
||||
<h3 id="currentTitle" class="text-xl font-semibold mb-2"
|
||||
style="color: var(--accent)">
|
||||
No folder selected
|
||||
</h3>
|
||||
<div class="border-t mb-4" style="border-color: var(--border)"></div>
|
||||
<ul id="contentList" class="space-y-1"></ul>
|
||||
</div>
|
||||
|
||||
<div id="catSlot" class="absolute bottom-10 right-4"></div>
|
||||
<div class="w-full flex items-center p-4 bg-[#fff1f2] border-t border-[#ffe4e6]"></div>
|
||||
<div id="catSlot" class="absolute bottom-12 right-2"></div>
|
||||
<div class="w-full h-16 flex items-center p-4"
|
||||
style="background: var(--bg-sidebar); border-top: 1px solid var(--border)">
|
||||
</div>
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
<script type="module">
|
||||
|
||||
112
main.js
112
main.js
@@ -1,4 +1,5 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const { app, BrowserWindow, ipcMain, dialog, Menu, shell, clipboard } = require('electron');
|
||||
app.name = 'Auto-Git';
|
||||
const { exec } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
@@ -9,7 +10,9 @@ const chokidar = require('chokidar');
|
||||
const store = new Store({
|
||||
defaults: {
|
||||
folders: [],
|
||||
selected: null
|
||||
selected: null,
|
||||
skymode: true,
|
||||
skipGitPrompt: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,7 +27,9 @@ function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 600,
|
||||
title: 'auto-git',
|
||||
minWidth: 600,
|
||||
minHeight: 400,
|
||||
title: 'Auto-Git',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true
|
||||
@@ -34,6 +39,30 @@ function createWindow() {
|
||||
return win;
|
||||
}
|
||||
|
||||
// Settings-Fenster
|
||||
let settingsWin;
|
||||
function openSettings(win) {
|
||||
if (settingsWin) {
|
||||
settingsWin.focus();
|
||||
return;
|
||||
}
|
||||
settingsWin = new BrowserWindow({
|
||||
parent: win,
|
||||
modal: true,
|
||||
width: 400,
|
||||
height: 300,
|
||||
resizable: false,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true
|
||||
}
|
||||
});
|
||||
settingsWin.removeMenu();
|
||||
settingsWin.loadFile('settings.html');
|
||||
settingsWin.on('closed', () => settingsWin = null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Startet einen File-Watcher auf .git/refs/heads/master,
|
||||
* sendet bei Änderungen 'repo-updated' an den Renderer.
|
||||
@@ -56,7 +85,7 @@ async function initGitRepo(folder) {
|
||||
const gitDir = path.join(folder, '.git');
|
||||
if (!fs.existsSync(gitDir)) {
|
||||
await git.init();
|
||||
const message = `Initial commit: ${new Date().toISOString()}`;
|
||||
const message = `Initial commit (generated by auto-git)`;
|
||||
const readmePath = path.join(folder, 'README.md');
|
||||
fs.writeFileSync(readmePath, `# Projekt in ${path.basename(folder)}\n`);
|
||||
await git.add('./*');
|
||||
@@ -67,6 +96,20 @@ async function initGitRepo(folder) {
|
||||
app.whenReady().then(() => {
|
||||
const win = createWindow();
|
||||
|
||||
// Menüs
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{
|
||||
role: 'appMenu',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Settings', click: () => openSettings(win)
|
||||
},
|
||||
{ role: 'quit', label: 'Quit' }
|
||||
]
|
||||
}, // mehr menüs hier
|
||||
]);
|
||||
Menu.setApplicationMenu(menu);
|
||||
|
||||
// 1) Beim Start bereits gespeicherte Ordner überwachen
|
||||
const folders = store.get('folders');
|
||||
folders.forEach(folder => {
|
||||
@@ -78,6 +121,7 @@ app.whenReady().then(() => {
|
||||
|
||||
// 2) IPC-Handler
|
||||
|
||||
|
||||
// Liste aller Folders
|
||||
ipcMain.handle('get-folders', () => store.get('folders'));
|
||||
|
||||
@@ -122,6 +166,30 @@ app.whenReady().then(() => {
|
||||
return updated;
|
||||
});
|
||||
|
||||
// Zähle Commits
|
||||
ipcMain.handle('get-commit-count', async (_e, folder) => {
|
||||
const git = simpleGit(folder);
|
||||
const log = await git.log();
|
||||
return log.total; // Anzahl der Commits
|
||||
});
|
||||
|
||||
// Prüfe, ob es ungestagte Änderungen gibt
|
||||
ipcMain.handle('has-diffs', async (_e, folder) => {
|
||||
const git = simpleGit(folder);
|
||||
const status = await git.status();
|
||||
// modified, not_added, deleted, etc.
|
||||
return status.files.length > 0;
|
||||
});
|
||||
|
||||
// Entferne das .git-Verzeichnis
|
||||
ipcMain.handle('remove-git-folder', async (_e, folder) => {
|
||||
const gitDir = path.join(folder, '.git');
|
||||
if (fs.existsSync(gitDir)) {
|
||||
await fs.promises.rm(gitDir, { recursive: true, force: true });
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
// Selected
|
||||
ipcMain.handle('get-selected', () => store.get('selected'));
|
||||
ipcMain.handle('set-selected', (_e, folder) => {
|
||||
@@ -186,9 +254,43 @@ app.whenReady().then(() => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// IPC für skymode
|
||||
ipcMain.handle('get-skymode', () => store.get('skymode'));
|
||||
ipcMain.handle('set-skymode', (_e, val) => {
|
||||
store.set('skymode', val);
|
||||
// sende an alle Fenster
|
||||
BrowserWindow.getAllWindows().forEach(win => {
|
||||
win.webContents.send('skymode-changed', val);
|
||||
});
|
||||
});
|
||||
ipcMain.handle('get-skip-git-prompt', () => store.get('skipGitPrompt'));
|
||||
ipcMain.handle('set-skip-git-prompt', (_e,val) => store.set('skipGitPrompt', val));
|
||||
});
|
||||
|
||||
ipcMain.on('show-folder-context-menu', (event, folderPath) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
const template = [
|
||||
{
|
||||
label: 'Copy Folder Path',
|
||||
click: () => {
|
||||
clipboard.writeText(folderPath);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Open Folder',
|
||||
click: () => {
|
||||
// öffnet den Ordner in der nativen Dateiansicht
|
||||
shell.openPath(folderPath);
|
||||
}
|
||||
}
|
||||
];
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
menu.popup({ window: win });
|
||||
});
|
||||
|
||||
// clean up on exit
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
});
|
||||
|
||||
33
package.json
33
package.json
@@ -1,16 +1,41 @@
|
||||
{
|
||||
"name": "auto-git",
|
||||
"productName": "Auto-Git",
|
||||
"name": "Auto-Git",
|
||||
"description": "Auto-Git: Git-Surveillance with automatic LLM-Commit-Message and README.md compilation",
|
||||
"version": "1.0.0",
|
||||
"author": "Victor Giers",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron ."
|
||||
"start": "electron .",
|
||||
"dist": "electron-builder"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.deinname.auto-git",
|
||||
"productName": "Auto-Git",
|
||||
"files": [
|
||||
"**/*"
|
||||
],
|
||||
"directories": {
|
||||
"buildResources": "assets/icon"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "mac/icon.icns"
|
||||
},
|
||||
"win": {
|
||||
"icon": "win/icon.ico"
|
||||
},
|
||||
"linux": {
|
||||
"icon": "linux/icon.png"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.3",
|
||||
"electron-store": "^8.2.0",
|
||||
"simple-git": "^3.20.0"
|
||||
"simple-git": "^3.20.0",
|
||||
"suncalc": "^1.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^25.0.0"
|
||||
"electron": "^25.0.0",
|
||||
"electron-builder": "^26.0.12"
|
||||
}
|
||||
}
|
||||
|
||||
17
preload.js
17
preload.js
@@ -1,5 +1,12 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('settingsAPI', {
|
||||
getSkyMode: () => ipcRenderer.invoke('get-skymode'),
|
||||
setSkyMode: val => ipcRenderer.invoke('set-skymode', val),
|
||||
getSkipPrompt: () => ipcRenderer.invoke('get-skip-git-prompt'),
|
||||
setSkipPrompt: val => ipcRenderer.invoke('set-skip-git-prompt', val)
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
getFolders: () => ipcRenderer.invoke('get-folders'),
|
||||
addFolder: () => ipcRenderer.invoke('add-folder'),
|
||||
@@ -12,8 +19,18 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
revertCommit: (folder, hash) => ipcRenderer.invoke('revert-commit', folder, hash),
|
||||
snapshotCommit: (folder, hash) => ipcRenderer.invoke('snapshot-commit', folder, hash),
|
||||
checkoutCommit: (folder, hash) => ipcRenderer.invoke('checkout-commit', folder, hash),
|
||||
getCommitCount: folder => ipcRenderer.invoke('get-commit-count', folder),
|
||||
hasDiffs: folder => ipcRenderer.invoke('has-diffs', folder),
|
||||
removeGitFolder: folder => ipcRenderer.invoke('remove-git-folder', folder),
|
||||
getSkipPrompt: () => ipcRenderer.invoke('get-skip-git-prompt'),
|
||||
setSkipPrompt: val => ipcRenderer.invoke('set-skip-git-prompt', val),
|
||||
showFolderContextMenu: folderPath => ipcRenderer.send('show-folder-context-menu', folderPath),
|
||||
});
|
||||
|
||||
ipcRenderer.on('repo-updated', (_e, folder) => {
|
||||
window.dispatchEvent(new CustomEvent('repo-updated', { detail: folder }));
|
||||
});
|
||||
|
||||
ipcRenderer.on('skymode-changed', (_e, val) => {
|
||||
window.dispatchEvent(new CustomEvent('skymode-changed', { detail: val }));
|
||||
});
|
||||
263
renderer.js
263
renderer.js
@@ -1,65 +1,178 @@
|
||||
// renderer.jsx
|
||||
// renderer.js
|
||||
|
||||
window.addEventListener('DOMContentLoaded', async () => {
|
||||
// Elemente holen
|
||||
const folderList = document.getElementById('folderList');
|
||||
const addBtn = document.getElementById('addFolderBtn');
|
||||
const titleEl = document.getElementById('currentTitle');
|
||||
const contentList = document.getElementById('contentList');
|
||||
const panel = document.querySelector('.flex-1.p-4.overflow-y-auto');
|
||||
|
||||
window.addEventListener('repo-updated', e => {
|
||||
const updatedFolder = e.detail;
|
||||
const current = titleEl.textContent;
|
||||
if (current === updatedFolder) {
|
||||
renderContent(current);
|
||||
// 1) Baby-Blau und Nacht-Blau als RGB-Arrays
|
||||
const DAY_COLOR = [173, 216, 230];
|
||||
const NIGHT_COLOR = [0, 0, 50];
|
||||
|
||||
// 2) Linearer Interpolator
|
||||
function lerpColor(c1, c2, t) {
|
||||
return c1.map((v, i) => Math.round(v + t * (c2[i] - v)));
|
||||
}
|
||||
|
||||
// 3) Entscheider für den Zeit-Factor
|
||||
function getTimeFactor() {
|
||||
const now = new Date();
|
||||
const md = now.getHours() * 60 + now.getMinutes();
|
||||
if (md < 4 * 60) return 0;
|
||||
if (md < 8 * 60) return (md - 4*60) / (4*60);
|
||||
if (md < 16 * 60) return 1;
|
||||
if (md < 20 * 60) return 1 - ((md - 16*60) / (4*60));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 4) setzt die Hintergrundfarbe
|
||||
function updateBackground() {
|
||||
const factor = getTimeFactor();
|
||||
const [r,g,b] = lerpColor(NIGHT_COLOR, DAY_COLOR, factor);
|
||||
panel.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
|
||||
// 5) applySkyMode-Funktion
|
||||
let skyIntervalId, titleIntervalId;
|
||||
function applySkyMode(enabled) {
|
||||
document.body.classList.toggle('sky-mode', enabled);
|
||||
|
||||
// alte Intervalle löschen
|
||||
clearInterval(skyIntervalId);
|
||||
clearInterval(titleIntervalId);
|
||||
|
||||
if (enabled) {
|
||||
// Hintergrund updaten
|
||||
updateBackground();
|
||||
skyIntervalId = setInterval(updateBackground, 60_000);
|
||||
|
||||
// Titel-Farbe je nach Uhrzeit
|
||||
function updateTitleColor() {
|
||||
const hour = new Date().getHours();
|
||||
if (hour >= 18 || hour < 6) {
|
||||
titleEl.style.color = '#fff';
|
||||
} else {
|
||||
titleEl.style.color = '';
|
||||
}
|
||||
}
|
||||
updateTitleColor();
|
||||
titleIntervalId = setInterval(updateTitleColor, 60_000);
|
||||
} else {
|
||||
// Sky-Mode aus → zurücksetzen
|
||||
panel.style.backgroundColor = '';
|
||||
titleEl.style.color = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 6) initial anwenden und auf Event lauschen
|
||||
const initialSky = await window.settingsAPI.getSkyMode();
|
||||
applySkyMode(initialSky);
|
||||
window.addEventListener('skymode-changed', e => applySkyMode(e.detail));
|
||||
|
||||
// … restliches Rendering wie gehabt …
|
||||
function basename(fullPath) {
|
||||
return fullPath.replace(/.*[\\/]/, '');
|
||||
}
|
||||
|
||||
async function renderSidebar() {
|
||||
const folders = await window.electronAPI.getFolders();
|
||||
const selected = await window.electronAPI.getSelected();
|
||||
folderList.innerHTML = '';
|
||||
folders.forEach(folder => {
|
||||
const li = document.createElement('li');
|
||||
li.className = [
|
||||
'flex items-center justify-between px-3 py-2 rounded cursor-pointer text-[#9f1239]',
|
||||
folder === selected ? 'bg-[#fecdd3]' : ''
|
||||
].join(' ');
|
||||
li.innerHTML = `
|
||||
<span class="flex items-center space-x-2 truncate">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-[#9f1239]" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 7a2 2 0 012-2h4l2 2h6a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7"/>
|
||||
</svg>
|
||||
<span class="truncate">${basename(folder)}</span>
|
||||
</span>
|
||||
<button class="text-[#9f1239] hover:text-opacity-80 remove-btn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
li.addEventListener('click', async e => {
|
||||
if (e.target.closest('.remove-btn')) return;
|
||||
await window.electronAPI.setSelected(folder);
|
||||
await renderSidebar();
|
||||
await renderContent(folder);
|
||||
});
|
||||
li.querySelector('.remove-btn').addEventListener('click', async () => {
|
||||
await window.electronAPI.removeFolder(folder);
|
||||
await renderSidebar();
|
||||
async function renderSidebar() {
|
||||
const folders = await window.electronAPI.getFolders();
|
||||
const selected = await window.electronAPI.getSelected();
|
||||
folderList.innerHTML = '';
|
||||
|
||||
folders.forEach(folder => {
|
||||
// 1) li anlegen, selected-Klasse statt bg-[#fecdd3]
|
||||
const li = document.createElement('li');
|
||||
li.className = [
|
||||
'flex items-center justify-between px-3 py-2 rounded cursor-pointer',
|
||||
folder === selected ? 'selected' : ''
|
||||
].join(' ');
|
||||
|
||||
// 2) inneres Markup, ohne Hard-Coded-Farben
|
||||
li.innerHTML = `
|
||||
<div class="flex items-center space-x-2 overflow-hidden">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5 flex-shrink-0"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 7a2 2 0 012-2h4l2 2h6a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7"/>
|
||||
</svg>
|
||||
<span class="truncate text-sm font-medium">${basename(folder)}</span>
|
||||
</div>
|
||||
<button class="remove-btn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
|
||||
// rechter Mausklick in der Sidebar
|
||||
li.addEventListener('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.showFolderContextMenu(folder);
|
||||
});
|
||||
|
||||
// 3) Klick auf li (Select)
|
||||
li.addEventListener('click', async e => {
|
||||
if (e.target.closest('.remove-btn')) return;
|
||||
await window.electronAPI.setSelected(folder);
|
||||
await renderSidebar();
|
||||
await renderContent(folder);
|
||||
});
|
||||
|
||||
// 4) Remove-Button
|
||||
li.querySelector('.remove-btn').addEventListener('click', async e => {
|
||||
e.stopPropagation();
|
||||
|
||||
// Commit-Count / Diffs / SkipPrompt-Logik wie gehabt
|
||||
const count = await window.electronAPI.getCommitCount(folder);
|
||||
const hasUnstaged= await window.electronAPI.hasDiffs(folder);
|
||||
const skipPrompt = await window.settingsAPI.getSkipPrompt();
|
||||
|
||||
if (count === 1 && !hasUnstaged) {
|
||||
if (skipPrompt) {
|
||||
await window.electronAPI.removeGitFolder(folder);
|
||||
} else {
|
||||
const ok = confirm(
|
||||
'Dieser Ordner hat nur einen Initial-Commit und keine Änderungen.\n' +
|
||||
'Möchtest du das gesamte Git-Repository (den .git-Ordner) löschen?'
|
||||
);
|
||||
if (ok) {
|
||||
await window.electronAPI.removeGitFolder(folder);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5) aus Sidebar/Store entfernen
|
||||
await window.electronAPI.removeFolder(folder);
|
||||
await renderSidebar();
|
||||
|
||||
// 6) neue Selektion
|
||||
const all = await window.electronAPI.getFolders();
|
||||
if (all.length === 0) {
|
||||
titleEl.textContent = 'No folder selected';
|
||||
contentList.innerHTML = '';
|
||||
});
|
||||
folderList.appendChild(li);
|
||||
} else {
|
||||
// entfernten Index vorher merken, dann neuen auswählen
|
||||
const idxOld = folders.indexOf(folder);
|
||||
let idxNew = Math.max(0, idxOld - 1);
|
||||
const pick = all[idxNew];
|
||||
await window.electronAPI.setSelected(pick);
|
||||
await renderSidebar();
|
||||
await renderContent(pick);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
folderList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
async function renderContent(folder) {
|
||||
const currentFolder = folder;
|
||||
@@ -115,7 +228,17 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||
Revert
|
||||
</button>-->
|
||||
</div>
|
||||
<div class="diff-container">
|
||||
<div class="diff-container relative">
|
||||
<!-- copy-Button oben rechts -->
|
||||
<button class="copy-diff-btn absolute top-1 right-1 p-1 border rounded hover:bg-gray-100 flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<!-- Copy-Icon: -->
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h6a2 2 0 012 2v2m0 4h2a2 2 0 002-2v-6a2 2 0 00-2-2h-6a2 2 0 00-2 2v2" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M16 16h2a2 2 0 002-2v-2m0 0V8a2 2 0 00-2-2h-2m0 4v6m0 0H8m8 0h2" />
|
||||
</svg>
|
||||
</button>
|
||||
<pre class="m-0"></pre>
|
||||
</div>
|
||||
</li>
|
||||
@@ -209,13 +332,46 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||
});
|
||||
});
|
||||
const currentEl = contentList.querySelector('li.current-commit');
|
||||
|
||||
// Copy-Diff-Button
|
||||
contentList.querySelectorAll('.diff-container').forEach(container => {
|
||||
const btn = container.querySelector('.copy-diff-btn');
|
||||
const pre = container.querySelector('pre');
|
||||
|
||||
// speicher die ursprüngliche SVG
|
||||
const originalSVG = btn.innerHTML;
|
||||
// erstelle die Checkmark-SVG
|
||||
const checkSVG = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4 text-green-600"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(pre.textContent)
|
||||
.then(() => {
|
||||
// Icon tauschen
|
||||
btn.innerHTML = checkSVG;
|
||||
// nach 1s wieder zurück
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = originalSVG;
|
||||
}, 1000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Clipboard write failed', err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (currentEl) {
|
||||
// weich scrollen und zentriert in den Viewport bringen
|
||||
currentEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}
|
||||
|
||||
// initial
|
||||
await renderSidebar();
|
||||
const initial = await window.electronAPI.getSelected();
|
||||
if (initial) await renderContent(initial);
|
||||
@@ -227,4 +383,15 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||
const sel = await window.electronAPI.getSelected();
|
||||
if (sel) await renderContent(sel);
|
||||
});
|
||||
|
||||
// repo-updated, contextmenus usw.
|
||||
window.addEventListener('repo-updated', e => {
|
||||
if (titleEl.textContent === e.detail) renderContent(e.detail);
|
||||
});
|
||||
titleEl.addEventListener('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
if (titleEl.textContent !== 'No folder selected') {
|
||||
window.electronAPI.showFolderContextMenu(titleEl.textContent);
|
||||
}
|
||||
});
|
||||
});
|
||||
123
settings.html
Normal file
123
settings.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Einstellungen</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden; /* keine Scrollbars */
|
||||
display: flex;
|
||||
align-items: center; /* vertikal zentrieren */
|
||||
justify-content: center; /* horizontal zentrieren */
|
||||
background: rgba(0,0,0,0.1);/* halbtransparenter Untergrund */
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.dialog {
|
||||
border: 2px solid #fbcfe8;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
width: 100%; /* 90% der Fensterbreite */
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background: white;
|
||||
position: relative;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 1.25rem;
|
||||
margin: 0;
|
||||
}
|
||||
.close-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 1.25rem;
|
||||
cursor: pointer;
|
||||
color: #9f1239;
|
||||
}
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.option input[type="checkbox"] {
|
||||
margin-left: 1.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.buttons {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
button {
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover { background: #f3f4f6; }
|
||||
</style>
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', async () => {
|
||||
const cbSky = document.getElementById('skymode');
|
||||
const cbSkip = document.getElementById('skipPrompt');
|
||||
const ok = document.getElementById('okBtn');
|
||||
const cancel = document.getElementById('cancelBtn');
|
||||
const close = document.getElementById('closeBtn');
|
||||
|
||||
const [initialSky, initialSkip] = await Promise.all([
|
||||
window.settingsAPI.getSkyMode(),
|
||||
window.settingsAPI.getSkipPrompt()
|
||||
]);
|
||||
|
||||
cbSky.checked = initialSky;
|
||||
cbSkip.checked = initialSkip;
|
||||
|
||||
ok.addEventListener('click', async () => {
|
||||
await window.settingsAPI.setSkyMode(cbSky.checked);
|
||||
await window.settingsAPI.setSkipPrompt(cbSkip.checked);
|
||||
window.close();
|
||||
});
|
||||
cancel.addEventListener('click', () => window.close());
|
||||
close.addEventListener('click', () => window.close());
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dialog">
|
||||
<div class="header">
|
||||
<h1>Einstellungen</h1>
|
||||
<button class="close-btn" id="closeBtn">✕</button>
|
||||
</div>
|
||||
<div class="options">
|
||||
<label class="option">
|
||||
<input type="checkbox" id="skymode" />
|
||||
Sky Mode aktivieren
|
||||
</label>
|
||||
<label class="option">
|
||||
<input type="checkbox" id="skipPrompt" />
|
||||
Skip prompt asking to remove .git folder if it has only one commit and no changes
|
||||
</label>
|
||||
<!-- Weitere Optionen in Zukunft hier hinzufügen -->
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button id="cancelBtn">Abbrechen</button>
|
||||
<button id="okBtn">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user