auto-git:
[change] index.html [change] main.js [change] preload.js [change] renderer.js [change] settings.html
This commit is contained in:
24
index.html
24
index.html
@@ -183,26 +183,24 @@
|
|||||||
|
|
||||||
<!-- Content-Area -->
|
<!-- Content-Area -->
|
||||||
<div class="flex-1 p-4 overflow-y-auto content-area">
|
<div class="flex-1 p-4 overflow-y-auto content-area">
|
||||||
<!--<h3 id="currentTitle" class="text-xl font-semibold mb-2"
|
<div id="folderTitleDrop"
|
||||||
style="color: var(--accent)">
|
class="relative flex items-baseline space-x-1 cursor-pointer select-none group pb-2">
|
||||||
No folder selected
|
<svg id="folderTitleArrow"
|
||||||
</h3>-->
|
class="h-4 w-6 flex-none transition-transform"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
<div id="folderTitleDrop" class="relative flex items-baseline space-x-1 cursor-pointer select-none group">
|
<path stroke="currentColor" stroke-width="2" fill="none"
|
||||||
<svg id="folderTitleArrow" class="h-6 w-6 flex-none transition-transform" viewBox="0 0 24 24">
|
stroke-linecap="round" stroke-linejoin="round"
|
||||||
<path stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"
|
d="M9 5l7 7-7 7"/>
|
||||||
d="M9 5l7 7-7 7"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
<span id="currentTitle" class="font-semibold text-xl truncate"></span>
|
<span id="currentTitle" class="font-semibold text-xl truncate"></span>
|
||||||
</div>
|
</div>
|
||||||
<!-- ASCII-Baum als <pre> (bleibt direkt unterhalb des Titels) -->
|
<!-- ASCII-Baum als <pre> (direkt unter dem Titel, keine Extras) -->
|
||||||
<pre id="folderHierarchyDropdown"
|
<pre id="folderHierarchyDropdown"
|
||||||
class="hidden text-sm font-mono bg-gray-100 border rounded-xl p-4 mb-2 mt-1 max-h-80 overflow-auto select-text"></pre>
|
class="hidden text-sm font-mono select-text leading-snug mb-2 mt-1 max-h-80 overflow-auto"
|
||||||
|
style="background: none; border: none; color: inherit; padding: 0;"></pre>
|
||||||
|
|
||||||
<div class="border-t mb-4" style="border-color: var(--border)"></div>
|
<div class="border-t mb-4" style="border-color: var(--border)"></div>
|
||||||
<ul id="contentList" class="space-y-1"></ul>
|
<ul id="contentList" class="space-y-1"></ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Cat -->
|
<!-- Cat -->
|
||||||
|
|||||||
45
main.js
45
main.js
@@ -12,7 +12,8 @@ const store = new Store({
|
|||||||
folders: [],
|
folders: [],
|
||||||
selected: null,
|
selected: null,
|
||||||
skymode: true,
|
skymode: true,
|
||||||
skipGitPrompt: true
|
skipGitPrompt: true,
|
||||||
|
intelligentCommitThreshold: 100
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -178,8 +179,8 @@ function stopMonitoringWatcher(folderPath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function autoCommit(folder, message) {
|
async function autoCommit(folderPath, message) {
|
||||||
const git = simpleGit(folder);
|
const git = simpleGit(folderPath);
|
||||||
const status = await git.status();
|
const status = await git.status();
|
||||||
if (
|
if (
|
||||||
status.not_added.length === 0 &&
|
status.not_added.length === 0 &&
|
||||||
@@ -242,6 +243,36 @@ async function autoCommit(folder, message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *** Zeilenzählung ***
|
||||||
|
let diffOutput = await git.diff(['--numstat']);
|
||||||
|
// Zeilensumme berechnen:
|
||||||
|
let changedLines = 0;
|
||||||
|
for (let line of diffOutput.split('\n')) {
|
||||||
|
// Format: <added> <deleted> <filename>
|
||||||
|
const match = line.match(/^(\d+|\-)\s+(\d+|\-)\s+(.*)$/);
|
||||||
|
if (match) {
|
||||||
|
const added = match[1] === '-' ? 0 : parseInt(match[1], 10);
|
||||||
|
const deleted = match[2] === '-' ? 0 : parseInt(match[2], 10);
|
||||||
|
changedLines += added + deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Folder-Objekt holen und aktualisieren
|
||||||
|
let folders = store.get('folders') || [];
|
||||||
|
let idx = folders.findIndex(f => f.path === folderPath);
|
||||||
|
if (idx !== -1) {
|
||||||
|
folders[idx].linesChanged = (folders[idx].linesChanged || 0) + changedLines;
|
||||||
|
store.set('folders', folders);
|
||||||
|
|
||||||
|
// Threshold holen
|
||||||
|
const threshold = store.get('intelligentCommitThreshold') || 10;
|
||||||
|
if (folders[idx].linesChanged >= threshold) {
|
||||||
|
folders[idx].linesChanged = 0;
|
||||||
|
store.set('folders', folders);
|
||||||
|
debug('Congratulations! You changed enough lines of code :)');
|
||||||
|
// Optional: Toast anzeigen, etc.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await git.add(['-A']);
|
await git.add(['-A']);
|
||||||
debug('[autoCommit] Alle Änderungen gestaged.');
|
debug('[autoCommit] Alle Änderungen gestaged.');
|
||||||
await git.commit(message || '[auto]');
|
await git.commit(message || '[auto]');
|
||||||
@@ -312,7 +343,7 @@ app.whenReady().then(() => {
|
|||||||
let folders = store.get('folders') || [];
|
let folders = store.get('folders') || [];
|
||||||
let folderObj = folders.find(f => f.path === newFolder);
|
let folderObj = folders.find(f => f.path === newFolder);
|
||||||
if (!folderObj) {
|
if (!folderObj) {
|
||||||
folderObj = { path: newFolder, monitoring: true };
|
folderObj = { path: newFolder, monitoring: true, linesChanged: 0 };
|
||||||
folders.push(folderObj);
|
folders.push(folderObj);
|
||||||
store.set('folders', folders);
|
store.set('folders', folders);
|
||||||
}
|
}
|
||||||
@@ -619,6 +650,12 @@ app.whenReady().then(() => {
|
|||||||
return monitoring;
|
return monitoring;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-intelligent-commit-threshold', () => store.get('intelligentCommitThreshold'));
|
||||||
|
ipcMain.handle('set-intelligent-commit-threshold', (_e, value) => {
|
||||||
|
store.set('intelligentCommitThreshold', value);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// … Ende der IPC-Handler …
|
// … Ende der IPC-Handler …
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
showFolderContextMenu: folderPath => ipcRenderer.send('show-folder-context-menu', folderPath),
|
showFolderContextMenu: folderPath => ipcRenderer.send('show-folder-context-menu', folderPath),
|
||||||
setMonitoring: (folderObj, monitoring) => ipcRenderer.invoke('set-monitoring', folderObj.path, monitoring),
|
setMonitoring: (folderObj, monitoring) => ipcRenderer.invoke('set-monitoring', folderObj.path, monitoring),
|
||||||
getFolderTree: (folderPath) => ipcRenderer.invoke('get-folder-tree', folderPath),
|
getFolderTree: (folderPath) => ipcRenderer.invoke('get-folder-tree', folderPath),
|
||||||
|
getIntelligentCommitThreshold: () => ipcRenderer.invoke('get-intelligent-commit-threshold'),
|
||||||
|
setIntelligentCommitThreshold: value => ipcRenderer.invoke('set-intelligent-commit-threshold', value),
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('repo-updated', (_e, folder) => {
|
ipcRenderer.on('repo-updated', (_e, folder) => {
|
||||||
|
|||||||
79
renderer.js
79
renderer.js
@@ -5,6 +5,7 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
const folderList = document.getElementById('folderList');
|
const folderList = document.getElementById('folderList');
|
||||||
const addBtn = document.getElementById('addFolderBtn');
|
const addBtn = document.getElementById('addFolderBtn');
|
||||||
const titleEl = document.getElementById('currentTitle');
|
const titleEl = document.getElementById('currentTitle');
|
||||||
|
const treeviewEl = document.getElementById('folderHierarchyDropdown');
|
||||||
const contentList = document.getElementById('contentList');
|
const contentList = document.getElementById('contentList');
|
||||||
const panel = document.querySelector('.flex-1.p-4.overflow-y-auto');
|
const panel = document.querySelector('.flex-1.p-4.overflow-y-auto');
|
||||||
|
|
||||||
@@ -42,8 +43,10 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
const hour = new Date().getHours();
|
const hour = new Date().getHours();
|
||||||
if (hour >= 18 || hour < 6) {
|
if (hour >= 18 || hour < 6) {
|
||||||
titleEl.style.color = '#fff';
|
titleEl.style.color = '#fff';
|
||||||
|
treeviewEl.style.color = '#fff';
|
||||||
} else {
|
} else {
|
||||||
titleEl.style.color = '';
|
titleEl.style.color = '';
|
||||||
|
treeviewEl.style.color = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateTitleColor();
|
updateTitleColor();
|
||||||
@@ -227,7 +230,7 @@ folders.forEach(folderObj => {
|
|||||||
folderTitleArrow.classList.remove('open');
|
folderTitleArrow.classList.remove('open');
|
||||||
isDropdownOpen = false;
|
isDropdownOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASCII-Baum: wie bei ChatGPT!
|
// ASCII-Baum: wie bei ChatGPT!
|
||||||
function renderFolderTreeAscii(tree, prefix = '', indent = '') {
|
function renderFolderTreeAscii(tree, prefix = '', indent = '') {
|
||||||
if (!Array.isArray(tree)) return '';
|
if (!Array.isArray(tree)) return '';
|
||||||
@@ -464,72 +467,12 @@ folders.forEach(folderObj => {
|
|||||||
commitBtn.textContent = 'Commit';
|
commitBtn.textContent = 'Commit';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const inputCommitThreshold = document.getElementById('intelligentCommitThreshold');
|
||||||
|
window.settingsAPI.getIntelligentCommitThreshold().then(val => inputCommitThreshold.value = val);
|
||||||
|
inputCommitThreshold.addEventListener('change', e => {
|
||||||
});
|
const num = Math.max(1, Math.min(1000, Number(inputCommitThreshold.value)));
|
||||||
|
window.settingsAPI.setIntelligentCommitThreshold(num);
|
||||||
/*
|
inputCommitThreshold.value = num;
|
||||||
const folderTitleDrop = document.getElementById('folderTitleDrop');
|
|
||||||
const folderTitleArrow = document.getElementById('folderTitleArrow');
|
|
||||||
const folderHierarchyDropdown = document.getElementById('folderHierarchyDropdown');
|
|
||||||
const titleEl = document.getElementById('currentTitle');
|
|
||||||
let isDropdownOpen = false;
|
|
||||||
|
|
||||||
folderTitleDrop.addEventListener('click', async () => {
|
|
||||||
if (isDropdownOpen) {
|
|
||||||
closeDropdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const selected = await window.electronAPI.getSelected();
|
|
||||||
if (!selected || !selected.path) return;
|
|
||||||
|
|
||||||
// Lade die aktuelle Ordnerhierarchie
|
|
||||||
folderHierarchyDropdown.innerHTML = '<div class="text-gray-400 italic">Lade Verzeichnis...</div>';
|
|
||||||
folderHierarchyDropdown.classList.remove('hidden');
|
|
||||||
folderHierarchyDropdown.classList.add('open');
|
|
||||||
folderTitleArrow.classList.add('open');
|
|
||||||
isDropdownOpen = true;
|
|
||||||
|
|
||||||
const tree = await window.electronAPI.getFolderTree(selected.path);
|
|
||||||
folderHierarchyDropdown.innerHTML = renderFolderTree(tree);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function closeDropdown() {
|
});
|
||||||
folderHierarchyDropdown.classList.add('hidden');
|
|
||||||
folderHierarchyDropdown.classList.remove('open');
|
|
||||||
folderTitleArrow.classList.remove('open');
|
|
||||||
isDropdownOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// KEIN automatisches Schließen beim Klick außerhalb!
|
|
||||||
|
|
||||||
function renderFolderTree(tree, level = 0) {
|
|
||||||
if (!Array.isArray(tree)) return '';
|
|
||||||
return `<ul class="${level === 0 ? 'font-mono text-[0.98rem] space-y-1' : 'pl-4 space-y-1'}">` + tree.map(node => {
|
|
||||||
if (node.type === 'dir') {
|
|
||||||
return `
|
|
||||||
<li>
|
|
||||||
<span class="inline-flex items-center font-semibold text-blue-700">
|
|
||||||
<svg class="h-4 w-4 mr-1 inline-block" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7a2 2 0 012-2h4l2 2h6a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7"/>
|
|
||||||
</svg>
|
|
||||||
${node.name}
|
|
||||||
</span>
|
|
||||||
${renderFolderTree(node.children, level + 1)}
|
|
||||||
</li>
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
return `
|
|
||||||
<li class="flex items-center text-gray-800">
|
|
||||||
<svg class="h-4 w-4 mr-1 opacity-60" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17v-6a2 2 0 012-2h6"/>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7 17a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2h-6a2 2 0 00-2 2v10z"/>
|
|
||||||
</svg>
|
|
||||||
<span>${node.name}</span>
|
|
||||||
</li>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}).join('') + '</ul>';
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -74,29 +74,57 @@
|
|||||||
button:hover { background: #f3f4f6; }
|
button:hover { background: #f3f4f6; }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener('DOMContentLoaded', async () => {
|
window.addEventListener('DOMContentLoaded', async () => {
|
||||||
const cbSky = document.getElementById('skymode');
|
// Elemente holen
|
||||||
const cbSkip = document.getElementById('skipPrompt');
|
const cbSky = document.getElementById('skymode');
|
||||||
const ok = document.getElementById('okBtn');
|
const cbSkip = document.getElementById('skipPrompt');
|
||||||
const cancel = document.getElementById('cancelBtn');
|
const thresholdInput = document.getElementById('intelligentCommitThresholdInput');
|
||||||
const close = document.getElementById('closeBtn');
|
const ok = document.getElementById('okBtn');
|
||||||
|
const cancel = document.getElementById('cancelBtn');
|
||||||
|
const close = document.getElementById('closeBtn');
|
||||||
|
|
||||||
const [initialSky, initialSkip] = await Promise.all([
|
// Initialwerte parallel laden
|
||||||
window.settingsAPI.getSkyMode(),
|
const [initialSky, initialSkip, initialThreshold] = await Promise.all([
|
||||||
window.settingsAPI.getSkipPrompt()
|
window.settingsAPI.getSkyMode(),
|
||||||
]);
|
window.settingsAPI.getSkipPrompt(),
|
||||||
|
window.electronAPI.getIntelligentCommitThreshold()
|
||||||
|
]);
|
||||||
|
|
||||||
cbSky.checked = initialSky;
|
// Inputs setzen
|
||||||
cbSkip.checked = initialSkip;
|
cbSky.checked = initialSky;
|
||||||
|
cbSkip.checked = initialSkip;
|
||||||
|
thresholdInput.value = initialThreshold;
|
||||||
|
|
||||||
ok.addEventListener('click', async () => {
|
// **Vorschau**: Sky-Mode sofort übernehmen
|
||||||
await window.settingsAPI.setSkyMode(cbSky.checked);
|
cbSky.addEventListener('change', async () => {
|
||||||
await window.settingsAPI.setSkipPrompt(cbSkip.checked);
|
await window.settingsAPI.setSkyMode(cbSky.checked);
|
||||||
window.close();
|
|
||||||
});
|
|
||||||
cancel.addEventListener('click', () => window.close());
|
|
||||||
close.addEventListener('click', () => window.close());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Threshold live speichern + clamp
|
||||||
|
thresholdInput.addEventListener('blur', async () => {
|
||||||
|
let v = parseInt(thresholdInput.value, 100);
|
||||||
|
if (isNaN(v)) v = initialThreshold; // wenn komplett leer gelassen
|
||||||
|
if (v < 1) v = 1;
|
||||||
|
if (v > 1000) v = 1000;
|
||||||
|
thresholdInput.value = v;
|
||||||
|
await window.settingsAPI.setIntelligentCommitThreshold(v);
|
||||||
|
});
|
||||||
|
|
||||||
|
// OK: alles final speichern
|
||||||
|
ok.addEventListener('click', async () => {
|
||||||
|
await window.settingsAPI.setSkipPrompt(cbSkip.checked);
|
||||||
|
// SkyMode haben wir ja schon beim Change gesetzt
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel / Close: SkyMode zurücksetzen, dann schließen
|
||||||
|
const rollback = async () => {
|
||||||
|
await window.settingsAPI.setSkyMode(initialSky);
|
||||||
|
window.close();
|
||||||
|
};
|
||||||
|
cancel.addEventListener('click', rollback);
|
||||||
|
close.addEventListener('click', rollback);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -110,6 +138,11 @@
|
|||||||
<input type="checkbox" id="skymode" />
|
<input type="checkbox" id="skymode" />
|
||||||
Sky Mode
|
Sky Mode
|
||||||
</label>
|
</label>
|
||||||
|
<label class="block mb-2 font-semibold">
|
||||||
|
Intelligent commit after
|
||||||
|
<input type="number" id="intelligentCommitThresholdInput" min="1" max="1000" value="10" step="1" class="border rounded px-2 py-1 w-20 mx-1">
|
||||||
|
lines of code changed
|
||||||
|
</label>
|
||||||
<label class="option">
|
<label class="option">
|
||||||
<input type="checkbox" id="skipPrompt" />
|
<input type="checkbox" id="skipPrompt" />
|
||||||
Skip prompt asking to remove .git folder if it has only one commit and no changes
|
Skip prompt asking to remove .git folder if it has only one commit and no changes
|
||||||
|
|||||||
Reference in New Issue
Block a user