Fügt CLI-Toolverwaltung und erweiterte UI-Funktionen hinzu
Ermöglicht die Verwaltung (Erstellen, Auflisten, Ausführen) von Tools über eine Befehlszeilenschnittstelle für Automatisierung und Integration. Die Benutzeroberfläche wurde mit dedizierten Ansichten für Toolerstellung und -bearbeitung, In-App-Lua-Hilfe sowie kontextsensitiven Menüs erweitert. Neue Abhängigkeiten (CLI11, rang) wurden integriert und eine umfassende Testsuite hinzugefügt. Die Lizenzierung wurde auf AGPL-3.0 umgestellt und ein Kontributionsleitfaden bereitgestellt.
This commit is contained in:
+203
-131
@@ -20,6 +20,7 @@
|
||||
<button onclick="openFileSystem()" id="file-system-button" class="button icon-button">📁</button>
|
||||
</div>
|
||||
<button onclick="openEinstFirst()" class="button icon-button" id="option-button" style="display: none;">⚙</button>
|
||||
<button onclick="toggleGeneralSettings()" class="button icon-button" id="general-settings-button" title="Einstellungen">⚙</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main" id="toolbox">
|
||||
@@ -32,36 +33,54 @@
|
||||
<!--Schmidti.Digital © 2024-->
|
||||
</div>
|
||||
</div>
|
||||
<div id="settings-overlay" class="settings-overlay" hidden>
|
||||
<div class="settings-panel">
|
||||
<div class="settings-panel-header">
|
||||
<h2>Einstellungen</h2>
|
||||
<button type="button" class="settings-panel-close" onclick="toggleGeneralSettings()">×</button>
|
||||
</div>
|
||||
<div class="settings-panel-body">
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-label">
|
||||
<span class="settings-row-title">Fenster-Deckkraft</span>
|
||||
<span class="settings-row-desc">70% bis 100% – bei 100% ist das Fenster vollstaendig deckend.</span>
|
||||
</div>
|
||||
<div class="settings-row-control">
|
||||
<input type="range" id="opacity-slider" min="70" max="100" value="100" step="1">
|
||||
<span id="opacity-value" class="settings-row-value">100%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="saucer://embedded/html/tool-runtime.js"></script>
|
||||
<script>
|
||||
function getNewContent(){
|
||||
saucer.exposed.getNewContent();
|
||||
}
|
||||
|
||||
function runLuaWithId(event) {
|
||||
async function runLuaWithId(event) {
|
||||
event.preventDefault();
|
||||
if (event.target.tagName === 'BUTTON') {
|
||||
// Falls der Button gedrückt wurde, brich die Funktion ab
|
||||
if (event.target.closest('button')) {
|
||||
return false;
|
||||
}
|
||||
const button = event.target;
|
||||
|
||||
const toolCard = button.closest('.tool-card');
|
||||
const toolCard = event.currentTarget;
|
||||
if (toolCard) {
|
||||
const toolId = toolCard.getAttribute('data-tool-id');
|
||||
const toolKey = toolCard.dataset.toolKey;
|
||||
|
||||
if (sessionStorage.getItem(`toolButtonDisabled_${toolId}`) !== 'true') {
|
||||
console.log(`Disabling button for tool ID: ${toolId}`);
|
||||
saucer.exposed.run_lua(parseInt(toolId, 10));
|
||||
|
||||
// Button deaktivieren
|
||||
button.disabled = true;
|
||||
button.classList.add('running');
|
||||
button.textContent = 'Wird ausgeführt...';
|
||||
|
||||
// Speichern des Zustands im sessionStorage
|
||||
sessionStorage.setItem(`toolButtonDisabled_${toolId}`, 'true');
|
||||
} else {
|
||||
if (ToolRuntime.isCardRunBlocked(toolCard)) {
|
||||
console.log(`Button is already disabled for tool ID: ${toolId}`);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const response = JSON.parse(await saucer.exposed.run_lua(toolKey));
|
||||
if (response.singleInstance === true) {
|
||||
await loadToolboxItems();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Starten des Tools:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,83 +98,93 @@
|
||||
}
|
||||
|
||||
function openAddTool() {
|
||||
openFileSystem();
|
||||
saucer.exposed.openAddTool();
|
||||
}
|
||||
|
||||
function createToolCard(item) {
|
||||
const toolId = String(item.id);
|
||||
const card = document.createElement('div');
|
||||
card.className = 'tool-card';
|
||||
card.dataset.url = String(item.url || '');
|
||||
card.dataset.toolId = toolId;
|
||||
card.dataset.toolKey = String(item.path || '');
|
||||
card.addEventListener('click', runLuaWithId);
|
||||
card.addEventListener('contextmenu', showContextMenu);
|
||||
|
||||
const icon = document.createElement('img');
|
||||
const iconData = typeof item.icon === 'string' ? item.icon : '';
|
||||
icon.src = iconData.startsWith('data:image/png;base64,')
|
||||
? iconData
|
||||
: `data:image/png;base64,${iconData}`;
|
||||
icon.alt = String(item.name || '');
|
||||
|
||||
const info = document.createElement('div');
|
||||
info.className = 'tool-info';
|
||||
|
||||
const name = document.createElement('h3');
|
||||
name.textContent = String(item.name || '');
|
||||
|
||||
const description = document.createElement('p');
|
||||
description.textContent = String(item.description || '');
|
||||
|
||||
const action = document.createElement('div');
|
||||
action.className = 'tool-action';
|
||||
|
||||
const settingsButton = document.createElement('button');
|
||||
settingsButton.type = 'button';
|
||||
settingsButton.textContent = '⚙';
|
||||
settingsButton.title = 'Tool bearbeiten';
|
||||
settingsButton.setAttribute('aria-label', 'Tool bearbeiten');
|
||||
settingsButton.addEventListener('click', event => {
|
||||
openEinst(event, card.dataset.toolKey);
|
||||
});
|
||||
|
||||
const runningStatus = document.createElement('span');
|
||||
runningStatus.className = 'tool-running-status';
|
||||
runningStatus.textContent = 'Läuft…';
|
||||
runningStatus.hidden = true;
|
||||
info.append(name, description);
|
||||
action.append(runningStatus, settingsButton);
|
||||
card.append(icon, info, action);
|
||||
ToolRuntime.applyCardState(card, ToolRuntime.initialRunning(item));
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderToolboxItems(items) {
|
||||
const toolbox = document.getElementById('toolbox');
|
||||
toolbox.querySelectorAll('.tool-card').forEach(card => card.remove());
|
||||
|
||||
const cards = document.createDocumentFragment();
|
||||
items.forEach(item => cards.appendChild(createToolCard(item)));
|
||||
toolbox.insertBefore(cards, toolbox.querySelector('.add-tool-card'));
|
||||
}
|
||||
|
||||
function loadToolboxItems() {
|
||||
return saucer.exposed.getToolboxItems()
|
||||
.then(jsonString => {
|
||||
const items = JSON.parse(jsonString);
|
||||
renderToolboxItems(Array.isArray(items) ? items : []);
|
||||
})
|
||||
.catch(error => console.error('Fehler beim Abrufen der Toolbox-Elemente:', error));
|
||||
}
|
||||
|
||||
function reloadContent() {
|
||||
const toolbox = document.getElementById('toolbox');
|
||||
|
||||
// Toolbox leeren, aber das add-tool-card Element behalten
|
||||
const addToolCard = toolbox.querySelector('.add-tool-card');
|
||||
toolbox.innerHTML = '';
|
||||
|
||||
// Toolbox-Daten neu abrufen
|
||||
saucer.exposed.getToolboxItems().then(jsonString => {
|
||||
const m_toolboxItems = JSON.parse(jsonString);
|
||||
|
||||
m_toolboxItems.forEach(item => {
|
||||
const iconSrc = item.icon.startsWith('data:image/png;base64,')
|
||||
? item.icon
|
||||
: `data:image/png;base64,${item.icon}`;
|
||||
|
||||
const toolCardHTML = `
|
||||
<div class="tool-card" data-url="${item.url}"
|
||||
data-tool-id="${item.id}"
|
||||
onclick="runLuaWithId(event)">
|
||||
<img src="${iconSrc}" alt="${item.name}">
|
||||
<div class="tool-info">
|
||||
<h3>${item.name}</h3>
|
||||
<p>${item.description}</p>
|
||||
</div>
|
||||
<div class="tool-action">
|
||||
<button onclick="openEinst(event, ${item.id})">Einst</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
toolbox.innerHTML += toolCardHTML;
|
||||
});
|
||||
|
||||
// Das add-tool-card Element wieder hinzufügen
|
||||
if (addToolCard) {
|
||||
toolbox.appendChild(addToolCard);
|
||||
}
|
||||
|
||||
// Überprüfen und Wiederherstellen des Zustands der Buttons
|
||||
document.querySelectorAll('.tool-card').forEach(card => {
|
||||
const toolId = card.getAttribute('data-tool-id');
|
||||
const button = card.querySelector('button');
|
||||
if (sessionStorage.getItem(`toolButtonDisabled_${toolId}`) === 'true') {
|
||||
button.disabled = true;
|
||||
button.classList.add('running');
|
||||
button.textContent = 'Wird ausgeführt...';
|
||||
}
|
||||
});
|
||||
}).catch(error => console.error('Fehler beim Abrufen der Toolbox-Elemente:', error));
|
||||
loadToolboxItems();
|
||||
}
|
||||
|
||||
function enableButtonByToolId(toolId) {
|
||||
saucer.exposed.debug_print();
|
||||
// Finden Sie den Button mit dem entsprechenden data-tool-id
|
||||
const button = document.querySelector(`.tool-card[data-tool-id='${toolId}']`);
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.classList.remove('running');
|
||||
button.textContent = 'Einst';
|
||||
// Speichern des Zustands im sessionStorage
|
||||
sessionStorage.setItem(`toolButtonDisabled_${toolId}`, 'false');
|
||||
} else {
|
||||
console.error('Button mit Tool-ID ' + toolId + ' nicht gefunden.');
|
||||
}
|
||||
function setToolRunningByPath(toolKey, running) {
|
||||
const card = Array.from(document.querySelectorAll('.tool-card'))
|
||||
.find(candidate => candidate.dataset.toolKey === toolKey);
|
||||
ToolRuntime.applyCardState(card, running);
|
||||
}
|
||||
|
||||
function openEinst(event, toolId){
|
||||
function openEinst(event, toolPath){
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
console.log('Einst Button geklickt!');
|
||||
saucer.exposed.openEinst(toolId);
|
||||
saucer.exposed.openEinst(toolPath);
|
||||
}
|
||||
|
||||
function openEinstFirst(){
|
||||
@@ -163,11 +192,100 @@
|
||||
if (!firstCard) {
|
||||
return;
|
||||
}
|
||||
const toolId = firstCard.getAttribute('data-tool-id');
|
||||
saucer.exposed.openEinst(parseInt(toolId));
|
||||
saucer.exposed.openEinst(firstCard.dataset.toolKey);
|
||||
}
|
||||
|
||||
let activeContextMenu = null;
|
||||
|
||||
function closeContextMenu() {
|
||||
if (activeContextMenu) {
|
||||
activeContextMenu.remove();
|
||||
activeContextMenu = null;
|
||||
}
|
||||
}
|
||||
|
||||
function showContextMenu(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
closeContextMenu();
|
||||
const card = event.currentTarget;
|
||||
const toolKey = card.dataset.toolKey;
|
||||
const toolName = card.querySelector('h3')?.textContent || 'dieses Tool';
|
||||
|
||||
const menu = document.createElement('div');
|
||||
menu.className = 'context-menu';
|
||||
|
||||
const editBtn = document.createElement('button');
|
||||
editBtn.type = 'button';
|
||||
editBtn.textContent = 'Bearbeiten';
|
||||
editBtn.addEventListener('click', () => {
|
||||
closeContextMenu();
|
||||
openEinst(null, toolKey);
|
||||
});
|
||||
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.type = 'button';
|
||||
deleteBtn.textContent = 'Löschen';
|
||||
deleteBtn.className = 'context-menu-danger';
|
||||
deleteBtn.addEventListener('click', () => {
|
||||
closeContextMenu();
|
||||
confirmDeleteTool(toolKey, toolName);
|
||||
});
|
||||
|
||||
menu.append(editBtn, deleteBtn);
|
||||
document.body.append(menu);
|
||||
activeContextMenu = menu;
|
||||
|
||||
const mx = Math.min(event.clientX, window.innerWidth - 180);
|
||||
const my = Math.min(event.clientY, window.innerHeight - 90);
|
||||
menu.style.left = mx + 'px';
|
||||
menu.style.top = my + 'px';
|
||||
}
|
||||
|
||||
async function confirmDeleteTool(toolKey, toolName) {
|
||||
if (!confirm(`"${toolName}" wirklich löschen?`)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = JSON.parse(await saucer.exposed.deleteTool(toolKey));
|
||||
if (response.success) {
|
||||
await loadToolboxItems();
|
||||
} else {
|
||||
alert(response.message || 'Löschen fehlgeschlagen.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen:', error);
|
||||
alert('Löschen fehlgeschlagen.');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', closeContextMenu);
|
||||
|
||||
let settingsOverlayVisible = false;
|
||||
let currentOpacity = 100;
|
||||
|
||||
function toggleGeneralSettings() {
|
||||
const overlay = document.getElementById('settings-overlay');
|
||||
settingsOverlayVisible = !settingsOverlayVisible;
|
||||
overlay.hidden = !settingsOverlayVisible;
|
||||
if (settingsOverlayVisible) {
|
||||
const slider = document.getElementById('opacity-slider');
|
||||
const valueLabel = document.getElementById('opacity-value');
|
||||
slider.value = currentOpacity;
|
||||
valueLabel.textContent = currentOpacity + '%';
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const slider = document.getElementById('opacity-slider');
|
||||
const valueLabel = document.getElementById('opacity-value');
|
||||
slider.addEventListener('input', () => {
|
||||
const val = parseInt(slider.value, 10);
|
||||
valueLabel.textContent = val + '%';
|
||||
currentOpacity = val;
|
||||
document.getElementById('shell').style.opacity = (val / 100).toString();
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
const link = e.target.closest('a');
|
||||
if (link) {
|
||||
@@ -175,53 +293,7 @@
|
||||
}
|
||||
}, true);
|
||||
|
||||
const toolbox = document.getElementById('toolbox');
|
||||
|
||||
// Das add-tool-card Element entfernen und später wieder hinzufügen
|
||||
const addToolCard = toolbox.querySelector('.add-tool-card');
|
||||
|
||||
saucer.exposed.getToolboxItems().then(jsonString => {
|
||||
const m_toolboxItems = JSON.parse(jsonString);
|
||||
|
||||
m_toolboxItems.forEach(item => {
|
||||
const iconSrc = item.icon.startsWith('data:image/png;base64,')
|
||||
? item.icon
|
||||
: `data:image/png;base64,${item.icon}`;
|
||||
|
||||
const toolCardHTML = `
|
||||
<div class="tool-card" data-url="${item.url}"
|
||||
data-tool-id="${item.id}"
|
||||
onclick="runLuaWithId(event)">
|
||||
<img src="${iconSrc}" alt="${item.name}">
|
||||
<div class="tool-info">
|
||||
<h3>${item.name}</h3>
|
||||
<p>${item.description}</p>
|
||||
</div>
|
||||
<div class="tool-action">
|
||||
<button onclick="openEinst(event, ${item.id})">Einst</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
toolbox.innerHTML += toolCardHTML;
|
||||
});
|
||||
|
||||
// Das add-tool-card Element wieder hinzufügen
|
||||
if (addToolCard) {
|
||||
toolbox.appendChild(addToolCard);
|
||||
}
|
||||
|
||||
// Überprüfen und Wiederherstellen des Zustands beim Laden der Seite
|
||||
document.querySelectorAll('.tool-card').forEach(card => {
|
||||
const toolId = card.getAttribute('data-tool-id');
|
||||
const button = card.querySelector('button');
|
||||
if (sessionStorage.getItem(`toolButtonDisabled_${toolId}`) === 'true') {
|
||||
button.disabled = true;
|
||||
button.classList.add('running');
|
||||
button.textContent = 'Wird ausgeführt...';
|
||||
}
|
||||
});
|
||||
}).catch(error => console.error('Error fetching toolbox items:', error));
|
||||
loadToolboxItems();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user