Files
Yadciel c611b008af 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.
2026-07-17 17:22:15 +02:00

348 lines
15 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tool erstellen</title>
<link rel="stylesheet" href="saucer://embedded/html/styles.css">
</head>
<body class="settings-page">
<div id="shell" class="settings-shell">
<div class="resize-handle resize-top" data-webview-resize="t"></div>
<div class="resize-handle resize-right" data-webview-resize="r"></div>
<div class="resize-handle resize-bottom" data-webview-resize="b"></div>
<div class="resize-handle resize-left" data-webview-resize="l"></div>
<div class="resize-handle resize-top-left" data-webview-resize="tl"></div>
<div class="resize-handle resize-top-right" data-webview-resize="tr"></div>
<div class="resize-handle resize-bottom-left" data-webview-resize="bl"></div>
<div class="resize-handle resize-bottom-right" data-webview-resize="br"></div>
<div class="titlebar" data-webview-drag>
<div class="titlebar-left" data-webview-drag>
<button class="titlebar-btn" id="back-button" data-webview-ignore></button>
<span class="titlebar-title" data-webview-drag>Neues Tool</span>
</div>
<div class="titlebar-actions" data-webview-drag>
<button class="titlebar-btn" data-webview-minimize data-webview-ignore></button>
<button class="titlebar-btn close" data-webview-close data-webview-ignore>×</button>
</div>
</div>
<main class="add-tool-page">
<header class="add-tool-heading">
<div>
<span class="add-tool-eyebrow">Toolbox Builder</span>
<h1>Tool erstellen</h1>
<p>Erstellt einen vollständigen Ordner in <code>.Toolbox</code>.</p>
</div>
<div class="add-tool-icon-preview" id="icon-preview" aria-label="Icon-Vorschau">+</div>
</header>
<form id="add-tool-form" class="add-tool-form">
<section class="add-tool-section add-tool-template-section">
<div class="add-tool-section-heading">
<div>
<h2>Vorlage auswählen</h2>
<p>Ein Ziel auswählen Toolbox erzeugt Name und Startskript automatisch.</p>
</div>
<span class="add-tool-template-badge">Einfach</span>
</div>
<div class="add-tool-template-grid" role="group" aria-label="Tool-Vorlage">
<button type="button" class="add-tool-template" data-template="program">
<span class="add-tool-template-icon"></span>
<span><strong>Programm</strong><small>Eine .exe starten</small></span>
</button>
<button type="button" class="add-tool-template" data-template="folder">
<span class="add-tool-template-icon"></span>
<span><strong>Ordner</strong><small>Im Explorer öffnen</small></span>
</button>
<button type="button" class="add-tool-template" data-template="website">
<span class="add-tool-template-icon"></span>
<span><strong>Website</strong><small>Eine URL öffnen</small></span>
</button>
<button type="button" class="add-tool-template is-active" data-template="lua" aria-pressed="true">
<span class="add-tool-template-icon">λ</span>
<span><strong>Freies Lua</strong><small>Eigenes Skript</small></span>
</button>
</div>
<div id="website-template-controls" class="add-tool-template-controls" hidden>
<input id="website-url" class="settings-input" type="url" autocomplete="url"
placeholder="https://example.com">
<button type="button" id="apply-website-template" class="add-tool-secondary">Übernehmen</button>
</div>
</section>
<section class="add-tool-section">
<h2>Allgemein</h2>
<label for="tool-name">Name <span aria-hidden="true">*</span></label>
<input id="tool-name" class="settings-input" maxlength="80" autocomplete="off" required
placeholder="Zum Beispiel: Projekt öffnen">
<label for="tool-description">Beschreibung</label>
<textarea id="tool-description" class="settings-input add-tool-description" maxlength="8192"
placeholder="Was erledigt dieses Tool?"></textarea>
<div class="add-tool-grid">
<div>
<label for="tool-timeout">Lua-Timeout</label>
<div class="add-tool-input-suffix">
<input id="tool-timeout" class="settings-input" type="number" min="0" max="86400" value="30">
<span>Sek.</span>
</div>
</div>
<div>
<label for="tool-icon">Icon (PNG)</label>
<input id="tool-icon" class="settings-input add-tool-file" type="file" accept="image/png">
</div>
</div>
</section>
<details id="advanced-script" class="add-tool-section add-tool-script-section" open>
<summary>
<span>Erweitert: start.lua bearbeiten</span>
<small>optional</small>
</summary>
<div class="add-tool-section-heading">
<h2>start.lua</h2>
<div class="add-tool-script-meta">
<span>Wird beim Klick auf die Karte ausgeführt</span>
<button type="button" class="lua-help-trigger" id="lua-help-button">Lua-Cheatsheet</button>
</div>
</div>
<div class="add-tool-code-editor">
<pre id="tool-script-highlight" aria-hidden="true"><code class="language-lua"></code></pre>
<textarea id="tool-script" class="settings-input add-tool-script" spellcheck="false" wrap="off"
aria-label="Lua-Skript">-- Dein Toolbox-Skript
print("Tool gestartet")
</textarea>
</div>
</details>
<div class="add-tool-actions">
<div id="create-status" class="settings-status" role="status" aria-live="polite"></div>
<button type="button" id="cancel-button" class="add-tool-secondary">Abbrechen</button>
<button type="submit" id="create-button" class="back-button settings-save">Tool erstellen</button>
</div>
</form>
</main>
</div>
<script src="saucer://embedded/vendor/prism/prism-core.min.js"></script>
<script src="saucer://embedded/vendor/prism/prism-lua.min.js"></script>
<script>
const form = document.getElementById('add-tool-form');
const nameInput = document.getElementById('tool-name');
const descriptionInput = document.getElementById('tool-description');
const timeoutInput = document.getElementById('tool-timeout');
const scriptInput = document.getElementById('tool-script');
const scriptHighlight = document.querySelector('#tool-script-highlight code');
const scriptHighlightContainer = document.getElementById('tool-script-highlight');
const iconInput = document.getElementById('tool-icon');
const iconPreview = document.getElementById('icon-preview');
const status = document.getElementById('create-status');
const createButton = document.getElementById('create-button');
const advancedScript = document.getElementById('advanced-script');
const templateButtons = [...document.querySelectorAll('[data-template]')];
const websiteControls = document.getElementById('website-template-controls');
const websiteUrl = document.getElementById('website-url');
const applyWebsiteButton = document.getElementById('apply-website-template');
let iconBase64 = '';
let templateType = 'lua';
let templateTarget = '';
function highlightLua() {
scriptHighlight.textContent = scriptInput.value + (scriptInput.value.endsWith('\n') ? ' ' : '');
Prism.highlightElement(scriptHighlight);
}
scriptInput.addEventListener('input', () => {
if (templateType !== 'lua') {
selectTemplate('lua');
advancedScript.open = true;
}
highlightLua();
});
scriptInput.addEventListener('scroll', () => {
scriptHighlightContainer.scrollTop = scriptInput.scrollTop;
scriptHighlightContainer.scrollLeft = scriptInput.scrollLeft;
});
function selectTemplate(kind) {
if (kind !== templateType) {
templateTarget = '';
}
templateType = kind;
templateButtons.forEach(button => {
const selected = button.dataset.template === kind;
button.classList.toggle('is-active', selected);
button.setAttribute('aria-pressed', String(selected));
});
websiteControls.hidden = kind !== 'website';
}
function applyTemplateResponse(responseText, kind) {
const response = JSON.parse(responseText);
if (response.cancelled) {
status.textContent = '';
delete status.dataset.kind;
return;
}
if (!response.success || !response.draft) {
status.textContent = response.message || 'Vorlage konnte nicht erstellt werden.';
status.dataset.kind = 'error';
return;
}
nameInput.value = response.draft.name;
descriptionInput.value = response.draft.description;
scriptInput.value = response.draft.luaScript;
templateType = response.draft.templateType;
templateTarget = response.draft.templateTarget;
highlightLua();
advancedScript.open = false;
status.textContent = `${kind} ist bereit. Nur noch „Tool erstellen“ klicken.`;
status.dataset.kind = 'success';
nameInput.focus();
nameInput.select();
}
async function chooseNativeTemplate(kind) {
selectTemplate(kind === 'Programm' ? 'program' : 'folder');
status.textContent = kind === 'Programm' ? 'Programm auswählen…' : 'Ordner auswählen…';
status.dataset.kind = 'pending';
try {
const responseText = kind === 'Programm'
? await saucer.exposed.selectProgramTemplate()
: await saucer.exposed.selectFolderTemplate();
applyTemplateResponse(responseText, kind);
} catch (error) {
console.error('Vorlage konnte nicht erstellt werden:', error);
status.textContent = 'Der Auswahldialog konnte nicht geöffnet werden.';
status.dataset.kind = 'error';
}
}
templateButtons.forEach(button => {
button.addEventListener('click', () => {
const kind = button.dataset.template;
if (kind === 'program') {
void chooseNativeTemplate('Programm');
} else if (kind === 'folder') {
void chooseNativeTemplate('Ordner');
} else if (kind === 'website') {
selectTemplate('website');
websiteUrl.focus();
} else {
selectTemplate('lua');
advancedScript.open = true;
scriptInput.focus();
}
});
});
async function applyWebsiteTemplate() {
selectTemplate('website');
status.textContent = 'Website-Vorlage wird erstellt…';
status.dataset.kind = 'pending';
try {
applyTemplateResponse(
await saucer.exposed.createWebsiteTemplate(websiteUrl.value.trim()),
'Website'
);
} catch (error) {
console.error('Website-Vorlage konnte nicht erstellt werden:', error);
status.textContent = 'Website-Vorlage konnte nicht erstellt werden.';
status.dataset.kind = 'error';
}
}
applyWebsiteButton.addEventListener('click', () => void applyWebsiteTemplate());
websiteUrl.addEventListener('keydown', event => {
if (event.key === 'Enter') {
event.preventDefault();
void applyWebsiteTemplate();
}
});
function returnToToolbox() {
saucer.exposed.openIndex();
}
document.getElementById('back-button').addEventListener('click', returnToToolbox);
document.getElementById('cancel-button').addEventListener('click', returnToToolbox);
document.getElementById('lua-help-button').addEventListener('click', () => {
saucer.exposed.openLuaHelp();
});
iconInput.addEventListener('change', () => {
const file = iconInput.files[0];
iconBase64 = '';
iconPreview.replaceChildren('+');
if (!file) {
return;
}
if (file.type !== 'image/png' || file.size > 2 * 1024 * 1024) {
iconInput.value = '';
status.textContent = 'Bitte ein PNG mit maximal 2 MB auswählen.';
status.dataset.kind = 'error';
return;
}
const reader = new FileReader();
reader.addEventListener('load', () => {
const dataUrl = String(reader.result || '');
iconBase64 = dataUrl.includes(',') ? dataUrl.split(',', 2)[1] : '';
const image = document.createElement('img');
image.src = dataUrl;
image.alt = 'Gewähltes Tool-Icon';
iconPreview.replaceChildren(image);
status.textContent = '';
delete status.dataset.kind;
});
reader.readAsDataURL(file);
});
form.addEventListener('submit', async event => {
event.preventDefault();
const name = nameInput.value.trim();
if (!name) {
status.textContent = 'Bitte einen Namen eingeben.';
status.dataset.kind = 'error';
nameInput.focus();
return;
}
createButton.disabled = true;
status.textContent = 'Tool wird erstellt…';
status.dataset.kind = 'pending';
try {
const responseText = await saucer.exposed.createTool(JSON.stringify({
name,
description: descriptionInput.value,
timeoutSeconds: Number.parseInt(timeoutInput.value, 10) || 0,
luaScript: scriptInput.value,
iconBase64,
templateType,
templateTarget,
}));
const response = JSON.parse(responseText);
status.textContent = response.message || (response.success ? 'Tool wurde erstellt.' : 'Tool konnte nicht erstellt werden.');
status.dataset.kind = response.success ? 'success' : 'error';
if (response.success) {
window.setTimeout(returnToToolbox, 700);
return;
}
} catch (error) {
console.error('Tool konnte nicht erstellt werden:', error);
status.textContent = 'Tool konnte nicht erstellt werden.';
status.dataset.kind = 'error';
}
createButton.disabled = false;
});
highlightLua();
nameInput.focus();
</script>
</body>
</html>