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:
347
res/html/add_tool.html
Normal file
347
res/html/add_tool.html
Normal file
@@ -0,0 +1,347 @@
|
||||
<!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>
|
||||
@@ -1,165 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Toolbox</title>
|
||||
<link rel="stylesheet" href="saucer://embedded/html/styles.css">
|
||||
</head>
|
||||
<body class="settings-page">
|
||||
<div id="shell">
|
||||
<!-- Sidebar für Optionen -->
|
||||
<div class="sidebar">
|
||||
<h2>Einstellungen</h2>
|
||||
<ul>
|
||||
<li><a href="#" onclick="showContent('general')">Einstellungen</a></li>
|
||||
<li><a href="#" onclick="showContent('editor')">Skript</a></li>
|
||||
<li><a href="#" onclick="showContent('none')">Option 3</a></li>
|
||||
<li><a href="#" onclick="showContent('none')">Option 4</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Hauptcontainer -->
|
||||
<div class="main-container">
|
||||
|
||||
<!-- Header mit Zurück-Button -->
|
||||
<div class="header">
|
||||
<h1>Einstellungen</h1>
|
||||
<button class="back-button" onclick="goBack()">←</button>
|
||||
</div>
|
||||
|
||||
<!-- Zentraler Editor (Standardmäßig versteckt) -->
|
||||
<div class="editor" id="editor">
|
||||
<label for="code-editor"></label>
|
||||
<textarea id="code-editor" placeholder="Write Lua code here..."></textarea>
|
||||
<pre><code id="highlightedOutput" class="language-lua"></code></pre>
|
||||
</div>
|
||||
|
||||
<div class="general" id="general">
|
||||
<div class="settings-card">
|
||||
<h3 style="margin: 0 0 10px 0;">Tool</h3>
|
||||
<div id="tool-name" class="settings-tool-name">-</div>
|
||||
|
||||
<label for="lua-timeout">Lua Timeout (Sekunden)</label>
|
||||
<input id="lua-timeout" class="settings-input" type="number" min="0" step="1" value="30">
|
||||
<div class="settings-help">
|
||||
0 = kein Timeout
|
||||
</div>
|
||||
|
||||
<button id="save-settings" class="back-button settings-save">Speichern</button>
|
||||
<div id="save-status" class="settings-status"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<!--Schmidti.Digital © 2024-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function goBack() {
|
||||
saucer.exposed.openIndex();
|
||||
}
|
||||
|
||||
function showContent(option) {
|
||||
const general = document.getElementById('general');
|
||||
const editor = document.getElementById('editor');
|
||||
|
||||
if (option === 'editor') {
|
||||
editor.style.display = 'flex';
|
||||
general.style.display = 'none';
|
||||
} else if (option === 'general') {
|
||||
general.style.display = 'flex';
|
||||
editor.style.display = 'none';
|
||||
} else {
|
||||
editor.style.display = 'none';
|
||||
general.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const editor = document.getElementById('code-editor');
|
||||
const output = document.getElementById('highlightedOutput');
|
||||
showContent('general');
|
||||
|
||||
function escapeHtml(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function highlightLua(code) {
|
||||
let escaped = escapeHtml(code);
|
||||
const tokens = [];
|
||||
|
||||
function stash(pattern, className, input) {
|
||||
return input.replace(pattern, (match) => {
|
||||
const key = `@@TOKEN${tokens.length}@@`;
|
||||
tokens.push(`<span class="${className}">${match}</span>`);
|
||||
return key;
|
||||
});
|
||||
}
|
||||
|
||||
escaped = stash(/--\[\[[\s\S]*?\]\]|--[^\n]*/g, 'code-comment', escaped);
|
||||
escaped = stash(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\[\[[\s\S]*?\]\]/g, 'code-string', escaped);
|
||||
|
||||
escaped = escaped.replace(/\b(\d+(\.\d+)?)\b/g, '<span class="code-number">$1</span>');
|
||||
|
||||
const keywords = [
|
||||
'and','break','do','else','elseif','end','false','for','function',
|
||||
'if','in','local','nil','not','or','repeat','return','then','true',
|
||||
'until','while'
|
||||
];
|
||||
const keywordRegex = new RegExp(`\\b(${keywords.join('|')})\\b`, 'g');
|
||||
escaped = escaped.replace(keywordRegex, '<span class="code-keyword">$1</span>');
|
||||
|
||||
tokens.forEach((value, index) => {
|
||||
escaped = escaped.replace(`@@TOKEN${index}@@`, value);
|
||||
});
|
||||
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// Synchronisiere den Inhalt von textarea mit dem Codeblock
|
||||
editor.addEventListener('input', () => {
|
||||
output.innerHTML = highlightLua(editor.value);
|
||||
});
|
||||
|
||||
const toolName = document.getElementById('tool-name');
|
||||
const timeoutInput = document.getElementById('lua-timeout');
|
||||
const saveBtn = document.getElementById('save-settings');
|
||||
const saveStatus = document.getElementById('save-status');
|
||||
|
||||
saucer.exposed.getActiveToolName().then(name => {
|
||||
toolName.textContent = name || '-';
|
||||
});
|
||||
|
||||
saucer.exposed.getLuaTimeoutSeconds().then(value => {
|
||||
timeoutInput.value = value;
|
||||
});
|
||||
|
||||
|
||||
saveBtn.addEventListener('click', () => {
|
||||
const seconds = parseInt(timeoutInput.value, 10);
|
||||
saucer.exposed.setLuaTimeoutSeconds(Number.isFinite(seconds) ? seconds : 0);
|
||||
saveStatus.textContent = 'Gespeichert';
|
||||
setTimeout(() => {
|
||||
saveStatus.textContent = '';
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Toolbox</title>
|
||||
<title>Tool bearbeiten</title>
|
||||
<link rel="stylesheet" href="saucer://embedded/html/styles.css">
|
||||
</head>
|
||||
<body class="settings-page">
|
||||
@@ -16,10 +16,11 @@
|
||||
<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" onclick="goBack()" data-webview-ignore>←</button>
|
||||
<span class="titlebar-title" data-webview-drag>Einstellungen</span>
|
||||
<button class="titlebar-btn" id="back-button" data-webview-ignore>←</button>
|
||||
<span class="titlebar-title" data-webview-drag>Tool bearbeiten</span>
|
||||
</div>
|
||||
<div class="titlebar-actions" data-webview-drag>
|
||||
<button class="titlebar-btn" data-webview-minimize data-webview-ignore>–</button>
|
||||
@@ -27,149 +28,445 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-body">
|
||||
<!-- Sidebar für Optionen -->
|
||||
<div class="sidebar">
|
||||
<h2>Einstellungen</h2>
|
||||
<ul>
|
||||
<li><a href="#" onclick="showContent('general')">Einstellungen</a></li>
|
||||
<li><a href="#" onclick="showContent('editor')">Skript</a></li>
|
||||
<li><a href="#" onclick="showContent('none')">Option 3</a></li>
|
||||
<li><a href="#" onclick="showContent('none')">Option 4</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Hauptcontainer -->
|
||||
<div class="main-container">
|
||||
<!-- Zentraler Editor (Standardmäßig versteckt) -->
|
||||
<div class="editor" id="editor">
|
||||
<label for="code-editor"></label>
|
||||
<textarea id="code-editor" placeholder="Write Lua code here..."></textarea>
|
||||
<pre><code id="highlightedOutput" class="language-lua"></code></pre>
|
||||
<main class="tool-settings-page">
|
||||
<header class="tool-settings-heading">
|
||||
<div id="settings-icon-preview" class="add-tool-icon-preview" aria-label="Tool-Icon">λ</div>
|
||||
<div>
|
||||
<span class="add-tool-eyebrow">Toolbox</span>
|
||||
<h1 id="settings-heading">Tool bearbeiten</h1>
|
||||
<p>Eigenschaften und Startverhalten dieses Tools.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="general" id="general">
|
||||
<div class="settings-card">
|
||||
<h3 style="margin: 0 0 10px 0;">Tool</h3>
|
||||
<div id="tool-name" class="settings-tool-name">-</div>
|
||||
<nav class="tool-settings-tabs" aria-label="Einstellungsbereiche">
|
||||
<button type="button" class="tool-settings-tab is-active" data-panel="general">Allgemein</button>
|
||||
<button type="button" class="tool-settings-tab" data-panel="launch">Startverhalten</button>
|
||||
<button type="button" class="tool-settings-tab" data-panel="actions">Aktionen</button>
|
||||
</nav>
|
||||
|
||||
<label for="lua-timeout">Lua Timeout (Sekunden)</label>
|
||||
<input id="lua-timeout" class="settings-input" type="number" min="0" step="1" value="30">
|
||||
<div class="settings-help">
|
||||
0 = kein Timeout
|
||||
<form id="tool-settings-form" class="tool-settings-form">
|
||||
<section class="tool-settings-panel is-active" data-panel-content="general">
|
||||
<div class="add-tool-section">
|
||||
<h2>Allgemein</h2>
|
||||
<label for="tool-name">Name</label>
|
||||
<input id="tool-name" class="settings-input" maxlength="80" autocomplete="off" required>
|
||||
|
||||
<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>
|
||||
<small class="tool-settings-help">0 deaktiviert den Timeout.</small>
|
||||
</div>
|
||||
<div>
|
||||
<label for="tool-icon">Icon (PNG)</label>
|
||||
<input id="tool-icon" class="settings-input add-tool-file" type="file" accept="image/png">
|
||||
<button type="button" id="remove-icon" class="tool-settings-link">Icon entfernen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tool-settings-panel" data-panel-content="launch">
|
||||
<div class="add-tool-section add-tool-template-section">
|
||||
<div class="add-tool-section-heading">
|
||||
<div>
|
||||
<h2>Starttyp</h2>
|
||||
<p>Standardvorlagen bleiben ohne Lua-Kenntnisse bearbeitbar.</p>
|
||||
</div>
|
||||
<span id="template-badge" class="add-tool-template-badge">Lua</span>
|
||||
</div>
|
||||
<div class="add-tool-template-grid" role="group" aria-label="Starttyp">
|
||||
<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" data-template="lua">
|
||||
<span class="add-tool-template-icon">λ</span>
|
||||
<span><strong>Freies Lua</strong><small>Eigenes Skript</small></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button id="save-settings" class="back-button settings-save">Speichern</button>
|
||||
<div id="save-status" class="settings-status"></div>
|
||||
<div id="native-target-controls" class="tool-settings-target" hidden>
|
||||
<label for="template-target">Ausgewähltes Ziel</label>
|
||||
<div class="add-tool-template-controls">
|
||||
<input id="template-target" class="settings-input" readonly>
|
||||
<button type="button" id="select-target" class="add-tool-secondary">Neu auswählen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="website-target-controls" class="tool-settings-target" hidden>
|
||||
<label for="website-url">Website-Adresse</label>
|
||||
<div class="add-tool-template-controls">
|
||||
<input id="website-url" class="settings-input" type="url" autocomplete="url"
|
||||
placeholder="https://example.com">
|
||||
<button type="button" id="apply-website" class="add-tool-secondary">Übernehmen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<!--Schmidti.Digital © 2024-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lua-section" class="add-tool-section">
|
||||
<div class="add-tool-section-heading">
|
||||
<h2>start.lua</h2>
|
||||
<button type="button" class="lua-help-trigger" id="lua-help-button">Lua-Cheatsheet</button>
|
||||
</div>
|
||||
<p class="tool-settings-help">Wird beim Klick auf die Toolkarte ausgeführt.</p>
|
||||
<div class="add-tool-code-editor tool-settings-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"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="generated-script-note" class="add-tool-section tool-settings-generated" hidden>
|
||||
<span class="tool-settings-generated-icon">✓</span>
|
||||
<div>
|
||||
<h2>Startskript wird automatisch gepflegt</h2>
|
||||
<p>Toolbox erzeugt <code>start.lua</code> beim Speichern sicher aus dem ausgewählten Ziel.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tool-settings-panel" data-panel-content="actions">
|
||||
<div class="add-tool-section tool-settings-action-card">
|
||||
<div>
|
||||
<h2>Toolordner</h2>
|
||||
<p>Öffnet den Ordner mit <code>start.lua</code>, <code>config.ini</code> und weiteren Tooldateien.</p>
|
||||
</div>
|
||||
<button type="button" id="open-tool-folder" class="add-tool-secondary">Im Explorer öffnen</button>
|
||||
</div>
|
||||
<div class="add-tool-section tool-settings-action-card">
|
||||
<div>
|
||||
<h2 style="color:#ff6b6b">Tool löschen</h2>
|
||||
<p>Entfernt dieses Tool unwiderruflich inklusive aller Dateien.</p>
|
||||
</div>
|
||||
<button type="button" id="delete-tool" class="add-tool-secondary" style="color:#ff6b6b;border-color:rgba(255,107,107,.4)">Löschen…</button>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
<footer class="tool-settings-footer">
|
||||
<div id="settings-status" class="settings-status" role="status" aria-live="polite"></div>
|
||||
<button type="button" id="cancel-button" class="add-tool-secondary">Zurück</button>
|
||||
<button type="submit" form="tool-settings-form" id="save-button" class="back-button settings-save">Speichern</button>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="saucer://embedded/vendor/prism/prism-core.min.js"></script>
|
||||
<script src="saucer://embedded/vendor/prism/prism-lua.min.js"></script>
|
||||
<script>
|
||||
function goBack() {
|
||||
const form = document.getElementById('tool-settings-form');
|
||||
const nameInput = document.getElementById('tool-name');
|
||||
const descriptionInput = document.getElementById('tool-description');
|
||||
const timeoutInput = document.getElementById('tool-timeout');
|
||||
const iconInput = document.getElementById('tool-icon');
|
||||
const iconPreview = document.getElementById('settings-icon-preview');
|
||||
const heading = document.getElementById('settings-heading');
|
||||
const scriptInput = document.getElementById('tool-script');
|
||||
const scriptHighlight = document.querySelector('#tool-script-highlight code');
|
||||
const scriptHighlightContainer = document.getElementById('tool-script-highlight');
|
||||
const nativeTargetControls = document.getElementById('native-target-controls');
|
||||
const websiteTargetControls = document.getElementById('website-target-controls');
|
||||
const targetInput = document.getElementById('template-target');
|
||||
const websiteInput = document.getElementById('website-url');
|
||||
const luaSection = document.getElementById('lua-section');
|
||||
const generatedNote = document.getElementById('generated-script-note');
|
||||
const templateBadge = document.getElementById('template-badge');
|
||||
const templateButtons = [...document.querySelectorAll('[data-template]')];
|
||||
const status = document.getElementById('settings-status');
|
||||
const saveButton = document.getElementById('save-button');
|
||||
|
||||
let templateType = 'lua';
|
||||
let templateTarget = '';
|
||||
let iconBase64 = '';
|
||||
let iconChanged = false;
|
||||
|
||||
function setStatus(message = '', kind = '') {
|
||||
status.textContent = message;
|
||||
if (kind) {
|
||||
status.dataset.kind = kind;
|
||||
} else {
|
||||
delete status.dataset.kind;
|
||||
}
|
||||
}
|
||||
|
||||
function returnToToolbox() {
|
||||
saucer.exposed.openIndex();
|
||||
}
|
||||
|
||||
function showContent(option) {
|
||||
const general = document.getElementById('general');
|
||||
const editor = document.getElementById('editor');
|
||||
function highlightLua() {
|
||||
scriptHighlight.textContent = scriptInput.value + (scriptInput.value.endsWith('\n') ? ' ' : '');
|
||||
Prism.highlightElement(scriptHighlight);
|
||||
}
|
||||
|
||||
if (option === 'editor') {
|
||||
editor.style.display = 'flex';
|
||||
general.style.display = 'none';
|
||||
} else if (option === 'general') {
|
||||
general.style.display = 'flex';
|
||||
editor.style.display = 'none';
|
||||
} else {
|
||||
editor.style.display = 'none';
|
||||
general.style.display = 'none';
|
||||
function renderIcon() {
|
||||
iconPreview.replaceChildren();
|
||||
if (!iconBase64) {
|
||||
iconPreview.textContent = 'λ';
|
||||
return;
|
||||
}
|
||||
const image = document.createElement('img');
|
||||
image.src = `data:image/png;base64,${iconBase64}`;
|
||||
image.alt = 'Tool-Icon';
|
||||
iconPreview.appendChild(image);
|
||||
}
|
||||
|
||||
function selectTemplate(kind) {
|
||||
templateType = kind;
|
||||
templateButtons.forEach(button => {
|
||||
const selected = button.dataset.template === kind;
|
||||
button.classList.toggle('is-active', selected);
|
||||
button.setAttribute('aria-pressed', String(selected));
|
||||
});
|
||||
const labels = {program: 'Programm', folder: 'Ordner', website: 'Website', lua: 'Lua'};
|
||||
templateBadge.textContent = labels[kind] || 'Lua';
|
||||
nativeTargetControls.hidden = kind !== 'program' && kind !== 'folder';
|
||||
websiteTargetControls.hidden = kind !== 'website';
|
||||
luaSection.hidden = kind !== 'lua';
|
||||
generatedNote.hidden = kind === 'lua';
|
||||
targetInput.value = kind === 'program' || kind === 'folder' ? templateTarget : '';
|
||||
websiteInput.value = kind === 'website' ? templateTarget : '';
|
||||
}
|
||||
|
||||
function applyTemplateResponse(responseText) {
|
||||
const response = JSON.parse(responseText);
|
||||
if (response.cancelled) {
|
||||
setStatus();
|
||||
return false;
|
||||
}
|
||||
if (!response.success || !response.draft) {
|
||||
setStatus(response.message || 'Das Ziel konnte nicht übernommen werden.', 'error');
|
||||
return false;
|
||||
}
|
||||
templateType = response.draft.templateType;
|
||||
templateTarget = response.draft.templateTarget;
|
||||
scriptInput.value = response.draft.luaScript;
|
||||
selectTemplate(templateType);
|
||||
highlightLua();
|
||||
setStatus('Startziel wurde übernommen. Zum Anwenden noch speichern.', 'success');
|
||||
return true;
|
||||
}
|
||||
|
||||
async function chooseNativeTarget(kind) {
|
||||
setStatus(kind === 'program' ? 'Programm auswählen…' : 'Ordner auswählen…', 'pending');
|
||||
try {
|
||||
const response = kind === 'program'
|
||||
? await saucer.exposed.selectProgramTemplate()
|
||||
: await saucer.exposed.selectFolderTemplate();
|
||||
applyTemplateResponse(response);
|
||||
} catch (error) {
|
||||
console.error('Zielauswahl fehlgeschlagen:', error);
|
||||
setStatus('Der Auswahldialog konnte nicht geöffnet werden.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const editor = document.getElementById('code-editor');
|
||||
const output = document.getElementById('highlightedOutput');
|
||||
showContent('general');
|
||||
|
||||
function escapeHtml(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
async function applyWebsite() {
|
||||
setStatus('Website wird geprüft…', 'pending');
|
||||
try {
|
||||
applyTemplateResponse(await saucer.exposed.createWebsiteTemplate(websiteInput.value.trim()));
|
||||
} catch (error) {
|
||||
console.error('Website-Prüfung fehlgeschlagen:', error);
|
||||
setStatus('Die Website konnte nicht übernommen werden.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function highlightLua(code) {
|
||||
let escaped = escapeHtml(code);
|
||||
const tokens = [];
|
||||
|
||||
function stash(pattern, className, input) {
|
||||
return input.replace(pattern, (match) => {
|
||||
const key = `@@TOKEN${tokens.length}@@`;
|
||||
tokens.push(`<span class="${className}">${match}</span>`);
|
||||
return key;
|
||||
});
|
||||
}
|
||||
|
||||
escaped = stash(/--\[\[[\s\S]*?\]\]|--[^\n]*/g, 'code-comment', escaped);
|
||||
escaped = stash(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\[\[[\s\S]*?\]\]/g, 'code-string', escaped);
|
||||
|
||||
escaped = escaped.replace(/\b(\d+(\.\d+)?)\b/g, '<span class="code-number">$1</span>');
|
||||
|
||||
const keywords = [
|
||||
'and','break','do','else','elseif','end','false','for','function',
|
||||
'if','in','local','nil','not','or','repeat','return','then','true',
|
||||
'until','while'
|
||||
];
|
||||
const keywordRegex = new RegExp(`\\b(${keywords.join('|')})\\b`, 'g');
|
||||
escaped = escaped.replace(keywordRegex, '<span class="code-keyword">$1</span>');
|
||||
|
||||
tokens.forEach((value, index) => {
|
||||
escaped = escaped.replace(`@@TOKEN${index}@@`, value);
|
||||
});
|
||||
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// Synchronisiere den Inhalt von textarea mit dem Codeblock
|
||||
editor.addEventListener('input', () => {
|
||||
output.innerHTML = highlightLua(editor.value);
|
||||
function showPanel(name) {
|
||||
document.querySelectorAll('[data-panel-content]').forEach(panel => {
|
||||
panel.classList.toggle('is-active', panel.dataset.panelContent === name);
|
||||
});
|
||||
|
||||
const toolName = document.getElementById('tool-name');
|
||||
const timeoutInput = document.getElementById('lua-timeout');
|
||||
const saveBtn = document.getElementById('save-settings');
|
||||
const saveStatus = document.getElementById('save-status');
|
||||
|
||||
saucer.exposed.getActiveToolName().then(name => {
|
||||
toolName.textContent = name || '-';
|
||||
});
|
||||
|
||||
saucer.exposed.getLuaTimeoutSeconds().then(value => {
|
||||
timeoutInput.value = value;
|
||||
});
|
||||
|
||||
|
||||
saveBtn.addEventListener('click', () => {
|
||||
const seconds = parseInt(timeoutInput.value, 10);
|
||||
saucer.exposed.setLuaTimeoutSeconds(Number.isFinite(seconds) ? seconds : 0);
|
||||
saveStatus.textContent = 'Gespeichert';
|
||||
setTimeout(() => {
|
||||
saveStatus.textContent = '';
|
||||
}, 1500);
|
||||
document.querySelectorAll('[data-panel]').forEach(tab => {
|
||||
tab.classList.toggle('is-active', tab.dataset.panel === name);
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-panel]').forEach(tab => {
|
||||
tab.addEventListener('click', () => showPanel(tab.dataset.panel));
|
||||
});
|
||||
|
||||
</script>
|
||||
templateButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const kind = button.dataset.template;
|
||||
if (kind === 'program' || kind === 'folder') {
|
||||
void chooseNativeTarget(kind);
|
||||
return;
|
||||
}
|
||||
if (kind === 'website') {
|
||||
templateTarget = '';
|
||||
selectTemplate('website');
|
||||
websiteInput.focus();
|
||||
return;
|
||||
}
|
||||
templateTarget = '';
|
||||
selectTemplate('lua');
|
||||
scriptInput.focus();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('select-target').addEventListener('click', () => void chooseNativeTarget(templateType));
|
||||
document.getElementById('apply-website').addEventListener('click', () => void applyWebsite());
|
||||
websiteInput.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
void applyWebsite();
|
||||
}
|
||||
});
|
||||
|
||||
scriptInput.addEventListener('input', highlightLua);
|
||||
scriptInput.addEventListener('scroll', () => {
|
||||
scriptHighlightContainer.scrollTop = scriptInput.scrollTop;
|
||||
scriptHighlightContainer.scrollLeft = scriptInput.scrollLeft;
|
||||
});
|
||||
|
||||
iconInput.addEventListener('change', () => {
|
||||
const file = iconInput.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
if (file.type !== 'image/png' || file.size > 2 * 1024 * 1024) {
|
||||
iconInput.value = '';
|
||||
setStatus('Bitte ein PNG mit maximal 2 MB auswählen.', 'error');
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => {
|
||||
const dataUrl = String(reader.result || '');
|
||||
iconBase64 = dataUrl.includes(',') ? dataUrl.split(',', 2)[1] : '';
|
||||
iconChanged = true;
|
||||
renderIcon();
|
||||
setStatus('Neues Icon ausgewählt. Zum Anwenden noch speichern.', 'success');
|
||||
});
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
document.getElementById('remove-icon').addEventListener('click', () => {
|
||||
iconBase64 = '';
|
||||
iconChanged = true;
|
||||
iconInput.value = '';
|
||||
renderIcon();
|
||||
setStatus('Icon wird beim Speichern entfernt.', 'success');
|
||||
});
|
||||
|
||||
document.getElementById('open-tool-folder').addEventListener('click', async () => {
|
||||
try {
|
||||
const response = JSON.parse(await saucer.exposed.openToolFolder());
|
||||
setStatus(response.message, response.success ? 'success' : 'error');
|
||||
} catch (error) {
|
||||
console.error('Toolordner konnte nicht geöffnet werden:', error);
|
||||
setStatus('Der Toolordner konnte nicht geöffnet werden.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('lua-help-button').addEventListener('click', () => saucer.exposed.openLuaHelp());
|
||||
document.getElementById('delete-tool')?.addEventListener('click', async () => {
|
||||
const toolName = nameInput.value.trim() || 'dieses Tool';
|
||||
if (!confirm(`"${toolName}" unwiderruflich löschen?`)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const details = JSON.parse(await saucer.exposed.getToolDetails());
|
||||
const toolPath = details.success ? (details.details?.path || details.path || '') : '';
|
||||
if (!toolPath) {
|
||||
setStatus('Tool-Pfad konnte nicht ermittelt werden.', 'error');
|
||||
return;
|
||||
}
|
||||
const response = JSON.parse(await saucer.exposed.deleteTool(toolPath));
|
||||
if (response.success) {
|
||||
returnToToolbox();
|
||||
} else {
|
||||
setStatus(response.message || 'Löschen fehlgeschlagen.', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Löschen fehlgeschlagen:', error);
|
||||
setStatus('Löschen fehlgeschlagen.', 'error');
|
||||
}
|
||||
});
|
||||
document.getElementById('back-button').addEventListener('click', returnToToolbox);
|
||||
document.getElementById('cancel-button').addEventListener('click', returnToToolbox);
|
||||
|
||||
form.addEventListener('submit', async event => {
|
||||
event.preventDefault();
|
||||
if (!nameInput.value.trim()) {
|
||||
showPanel('general');
|
||||
nameInput.focus();
|
||||
setStatus('Bitte einen Toolnamen eingeben.', 'error');
|
||||
return;
|
||||
}
|
||||
if (templateType === 'website') {
|
||||
templateTarget = websiteInput.value.trim();
|
||||
}
|
||||
saveButton.disabled = true;
|
||||
setStatus('Änderungen werden gespeichert…', 'pending');
|
||||
const payload = {
|
||||
name: nameInput.value.trim(),
|
||||
description: descriptionInput.value,
|
||||
timeoutSeconds: Number.parseInt(timeoutInput.value, 10) || 0,
|
||||
luaScript: scriptInput.value,
|
||||
templateType,
|
||||
templateTarget,
|
||||
};
|
||||
if (iconChanged) {
|
||||
payload.iconBase64 = iconBase64;
|
||||
}
|
||||
try {
|
||||
const response = JSON.parse(await saucer.exposed.saveToolDetails(JSON.stringify(payload)));
|
||||
setStatus(response.message || (response.success ? 'Tool wurde gespeichert.' : 'Speichern fehlgeschlagen.'),
|
||||
response.success ? 'success' : 'error');
|
||||
if (response.success) {
|
||||
heading.textContent = nameInput.value.trim();
|
||||
iconChanged = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Tool konnte nicht gespeichert werden:', error);
|
||||
setStatus('Tool konnte nicht gespeichert werden.', 'error');
|
||||
}
|
||||
saveButton.disabled = false;
|
||||
});
|
||||
|
||||
async function loadTool() {
|
||||
setStatus('Tool wird geladen…', 'pending');
|
||||
try {
|
||||
const response = JSON.parse(await saucer.exposed.getToolDetails());
|
||||
if (!response.success || !response.details) {
|
||||
setStatus(response.message || 'Tool konnte nicht geladen werden.', 'error');
|
||||
saveButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
const details = response.details;
|
||||
nameInput.value = details.name || '';
|
||||
descriptionInput.value = details.description || '';
|
||||
timeoutInput.value = details.timeoutSeconds ?? 30;
|
||||
scriptInput.value = details.luaScript || '';
|
||||
templateType = details.templateType || 'lua';
|
||||
templateTarget = details.templateTarget || '';
|
||||
iconBase64 = details.iconBase64 || '';
|
||||
iconChanged = false;
|
||||
heading.textContent = details.name || 'Tool bearbeiten';
|
||||
renderIcon();
|
||||
selectTemplate(templateType);
|
||||
highlightLua();
|
||||
setStatus();
|
||||
} catch (error) {
|
||||
console.error('Tool konnte nicht geladen werden:', error);
|
||||
setStatus('Tool konnte nicht geladen werden.', 'error');
|
||||
saveButton.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void loadTool();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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>
|
||||
|
||||
347
res/html/lua_cheatsheet.html
Normal file
347
res/html/lua_cheatsheet.html
Normal file
@@ -0,0 +1,347 @@
|
||||
/<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lua-Cheatsheet</title>
|
||||
<link rel="stylesheet" href="saucer://embedded/html/styles.css">
|
||||
</head>
|
||||
<body class="settings-page lua-help-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>
|
||||
<span class="lua-help-mark" aria-hidden="true">λ</span>
|
||||
<span class="titlebar-title" data-webview-drag>Lua-Hilfe</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>
|
||||
|
||||
<nav class="lua-help-tabs" aria-label="Lua-Hilfe" role="tablist">
|
||||
<button class="lua-help-tab active" id="cheatsheet-tab" type="button" role="tab"
|
||||
aria-selected="true" aria-controls="cheatsheet-panel">Cheatsheet</button>
|
||||
<button class="lua-help-tab" id="official-docs-tab" type="button" role="tab"
|
||||
aria-selected="false">
|
||||
Offizielle Lua-5.4-Doku
|
||||
</button>
|
||||
<span class="lua-help-doc-status" id="docs-status" role="status" aria-live="polite">Online · lua.org</span>
|
||||
</nav>
|
||||
|
||||
<div class="lua-help-panels">
|
||||
<main class="lua-help-content" id="cheatsheet-panel" role="tabpanel" aria-labelledby="cheatsheet-tab">
|
||||
<header class="lua-help-hero">
|
||||
<div>
|
||||
<span class="add-tool-eyebrow">Toolbox · Lua 5.4.1</span>
|
||||
<h1>Lua auf einen Blick</h1>
|
||||
<p>Die wichtigsten Muster für <code>start.lua</code> – lokal verfügbar, kurz und kopierbar.</p>
|
||||
</div>
|
||||
<div class="lua-help-badge">start.lua</div>
|
||||
</header>
|
||||
|
||||
<section class="lua-help-notice">
|
||||
<strong>Toolbox-Kontext</strong>
|
||||
<span>Das Skript startet beim Klick auf eine Tool-Karte. Timeout <code>0</code> bedeutet unbegrenzt. Tools sind vertrauenswürdig und dürfen über <code>os</code> und <code>io</code> auf das System zugreifen.</span>
|
||||
</section>
|
||||
|
||||
<div class="lua-help-grid">
|
||||
<article class="lua-help-card">
|
||||
<h2>Nur eine laufende Instanz</h2>
|
||||
<pre><code class="language-lua">-- toolbox: single-instance
|
||||
|
||||
-- Während dieses Skript läuft, wird seine
|
||||
-- Tool-Karte rot angezeigt und gesperrt.
|
||||
-- Ohne die Direktive sind parallele Starts erlaubt.</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Variablen & Typen</h2>
|
||||
<pre><code class="language-lua">local name = "Toolbox"
|
||||
local count = 3
|
||||
local enabled = true
|
||||
local nothing = nil
|
||||
|
||||
-- Typen prüfen
|
||||
type(name) -- "string"
|
||||
tostring(count) -- "3"
|
||||
tonumber("42") -- 42</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Operatoren</h2>
|
||||
<pre><code class="language-lua">-- Rechnen: + - * / // % ^
|
||||
local half = 7 // 2 -- 3
|
||||
local text = "Lua " .. "5.4"
|
||||
local length = #text
|
||||
|
||||
-- Vergleich: == ~= < > <= >=
|
||||
-- Logik: and, or, not</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Bedingungen</h2>
|
||||
<pre><code class="language-lua">if count > 10 then
|
||||
print("groß")
|
||||
elseif count > 0 then
|
||||
print("positiv")
|
||||
else
|
||||
print("leer")
|
||||
end</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Schleifen</h2>
|
||||
<pre><code class="language-lua">for i = 1, 5 do
|
||||
print(i)
|
||||
end
|
||||
|
||||
local i = 1
|
||||
while i <= 3 do
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
repeat
|
||||
i = i - 1
|
||||
until i == 0</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Funktionen</h2>
|
||||
<pre><code class="language-lua">local function greet(name)
|
||||
return "Hallo " .. name
|
||||
end
|
||||
|
||||
local function position()
|
||||
return 10, 20
|
||||
end
|
||||
|
||||
local x, y = position()
|
||||
print(greet("Toolbox"), x, y)</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Tabellen</h2>
|
||||
<pre><code class="language-lua">local tool = {
|
||||
name = "Backup",
|
||||
tags = { "files", "daily" }
|
||||
}
|
||||
|
||||
print(tool.name)
|
||||
print(tool.tags[1]) -- ab 1 indiziert
|
||||
|
||||
for key, value in pairs(tool) do
|
||||
print(key, value)
|
||||
end</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Strings</h2>
|
||||
<pre><code class="language-lua">local path = [[C:\Projekte\Toolbox]]
|
||||
local upper = string.upper("lua")
|
||||
local found = string.find("start.lua", "lua")
|
||||
local formatted = string.format("%s: %d", "Dateien", 4)</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Dateien</h2>
|
||||
<pre><code class="language-lua">local file, err = io.open("status.txt", "w")
|
||||
if not file then
|
||||
error(err)
|
||||
end
|
||||
|
||||
file:write("fertig\n")
|
||||
file:close()</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Fehler behandeln</h2>
|
||||
<pre><code class="language-lua">local ok, result = pcall(function()
|
||||
assert(false, "etwas ging schief")
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
print("Fehler:", result)
|
||||
end</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Module</h2>
|
||||
<pre><code class="language-lua">-- helper.lua
|
||||
local M = {}
|
||||
function M.run()
|
||||
return "fertig"
|
||||
end
|
||||
return M
|
||||
|
||||
-- start.lua
|
||||
local helper = require("helper")
|
||||
print(helper.run())</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Gültigkeitsbereiche</h2>
|
||||
<pre><code class="language-lua">local outer = "sichtbar"
|
||||
do
|
||||
local inner = "nur im Block"
|
||||
print(outer, inner)
|
||||
end
|
||||
|
||||
-- Ohne local wird eine globale Variable erzeugt.
|
||||
-- Globale Namen möglichst vermeiden.</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Listen & <code>ipairs</code></h2>
|
||||
<pre><code class="language-lua">local files = { "a.txt", "b.txt" }
|
||||
table.insert(files, "c.txt")
|
||||
local last = table.remove(files)
|
||||
|
||||
for index, file in ipairs(files) do
|
||||
print(index, file)
|
||||
end</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Mehrfachwerte & Varargs</h2>
|
||||
<pre><code class="language-lua">local function bounds()
|
||||
return 10, 20
|
||||
end
|
||||
|
||||
local function join(...)
|
||||
return table.concat({ ... }, ", ")
|
||||
end
|
||||
|
||||
local min, max = bounds()
|
||||
print(join("Lua", min, max))</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>String-Muster</h2>
|
||||
<pre><code class="language-lua">local text = "build-2026.log"
|
||||
local year = text:match("build%-(%d+)%.log")
|
||||
|
||||
local clean = text:gsub("[^%w%.%-]", "_")
|
||||
for word in string.gmatch("a,b,c", "[^,]+") do
|
||||
print(word)
|
||||
end</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Zeit & Datum</h2>
|
||||
<pre><code class="language-lua">local now = os.time()
|
||||
local stamp = os.date("%Y-%m-%d %H:%M:%S", now)
|
||||
print(stamp)
|
||||
|
||||
local started = os.clock()
|
||||
-- Arbeit ...
|
||||
print("CPU-Sekunden", os.clock() - started)</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Coroutinen</h2>
|
||||
<pre><code class="language-lua">local worker = coroutine.create(function()
|
||||
coroutine.yield("Schritt 1")
|
||||
return "fertig"
|
||||
end)
|
||||
|
||||
local ok, value = coroutine.resume(worker)
|
||||
assert(ok, value)
|
||||
print(value)</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Metatables</h2>
|
||||
<pre><code class="language-lua">local defaults = { timeout = 30 }
|
||||
local config = setmetatable({}, {
|
||||
__index = defaults
|
||||
})
|
||||
|
||||
print(config.timeout) -- 30
|
||||
config.timeout = 10</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="lua-help-card">
|
||||
<h2>Defensiv aufräumen</h2>
|
||||
<pre><code class="language-lua">local file <close> = assert(io.open("data.txt", "r"))
|
||||
local content = file:read("*a")
|
||||
-- Lua 5.4 schließt file beim Verlassen
|
||||
-- des Gültigkeitsbereichs automatisch.
|
||||
|
||||
assert(#content > 0, "Datei ist leer")</code></pre>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<section class="lua-help-libraries">
|
||||
<h2>In Toolbox verfügbare Bibliotheken</h2>
|
||||
<div class="lua-help-chips">
|
||||
<code>base</code><code>os</code><code>string</code><code>coroutine</code><code>package</code>
|
||||
<code>table</code><code>math</code><code>utf8</code><code>io</code>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="lua-help-libraries">
|
||||
<h2>Toolbox-Rezepte unter Windows</h2>
|
||||
<div class="lua-help-grid">
|
||||
<article class="lua-help-card">
|
||||
<h2>Programm starten</h2>
|
||||
<pre><code class="language-lua">-- start benötigt einen leeren Fenstertitel vor dem Pfad
|
||||
local command = [[start "" "C:\Program Files\App\app.exe"]]
|
||||
local ok, kind, code = os.execute(command)
|
||||
assert(ok, string.format("Start fehlgeschlagen: %s %s", kind, code))</code></pre>
|
||||
</article>
|
||||
<article class="lua-help-card">
|
||||
<h2>Ordner öffnen</h2>
|
||||
<pre><code class="language-lua">local folder = [[C:\Projekte]]
|
||||
os.execute('explorer "' .. folder .. '"')
|
||||
|
||||
-- Relative Pfade beziehen sich auf das
|
||||
-- Arbeitsverzeichnis von Toolbox.</code></pre>
|
||||
</article>
|
||||
<article class="lua-help-card">
|
||||
<h2>Umgebungsvariablen</h2>
|
||||
<pre><code class="language-lua">local profile = assert(os.getenv("USERPROFILE"))
|
||||
local temp = os.getenv("TEMP") or profile
|
||||
print(profile, temp)</code></pre>
|
||||
</article>
|
||||
<article class="lua-help-card">
|
||||
<h2>Sicheres Kommando-Quoting</h2>
|
||||
<pre><code class="language-lua">local function quote(value)
|
||||
assert(not value:find('"'), "Ungültiges Anführungszeichen")
|
||||
return '"' .. value .. '"'
|
||||
end
|
||||
|
||||
-- Keine ungeprüften Eingaben an os.execute übergeben.</code></pre>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="lua-help-notice lua-help-footer-note">
|
||||
<strong>Timeout & Vertrauen</strong>
|
||||
<span>Toolbox prüft den Timeout zwischen Lua-Instruktionen. Blockierende native Aufrufe wie <code>os.execute()</code> können erst nach ihrer Rückkehr unterbrochen werden. Führe daher nur überprüfte Skripte und Programme aus.</span>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</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 docsTab = document.getElementById('official-docs-tab');
|
||||
docsTab.addEventListener('click', () => {
|
||||
saucer.exposed.showOfficialLuaDocs();
|
||||
});
|
||||
|
||||
Prism.highlightAll();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -15,6 +15,32 @@
|
||||
--accent: #4fc3ff;
|
||||
--accent-deep: #1e74c9;
|
||||
}
|
||||
|
||||
/* Globale Scrollbar-Stile im Toolbox-Design */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(60, 150, 220, 0.95) rgba(20, 45, 70, 0.4);
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(20, 45, 70, 0.3);
|
||||
border-radius: 999px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, rgba(120, 210, 255, 0.9), rgba(60, 150, 220, 0.95));
|
||||
border-radius: 999px;
|
||||
border: 2px solid rgba(20, 45, 70, 0.4);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(180deg, rgba(210, 245, 255, 0.85), rgba(120, 210, 255, 0.9));
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html {
|
||||
background: transparent;
|
||||
border-radius: 18px;
|
||||
@@ -33,7 +59,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background: rgba(10, 26, 40, 0.80);
|
||||
background: rgba(10, 26, 40, 1.0);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -127,20 +153,6 @@
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(120, 210, 255, 0.7) rgba(20, 45, 70, 0.5);
|
||||
}
|
||||
.main::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
.main::-webkit-scrollbar-track {
|
||||
background: rgba(20, 45, 70, 0.45);
|
||||
border-radius: 999px;
|
||||
}
|
||||
.main::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, rgba(120, 210, 255, 0.8), rgba(60, 150, 220, 0.9));
|
||||
border-radius: 999px;
|
||||
border: 2px solid rgba(20, 45, 70, 0.4);
|
||||
}
|
||||
.tool-card {
|
||||
display: flex;
|
||||
@@ -161,6 +173,14 @@
|
||||
background: var(--glass-strong);
|
||||
box-shadow: 0 16px 26px var(--shadow), inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.tool-card.tool-running,
|
||||
.tool-card.tool-running:hover {
|
||||
transform: none;
|
||||
cursor: not-allowed;
|
||||
background: linear-gradient(135deg, rgba(125, 24, 35, 0.82), rgba(68, 18, 29, 0.9));
|
||||
border-color: rgba(255, 102, 112, 0.85);
|
||||
box-shadow: 0 12px 24px rgba(75, 5, 15, 0.48), inset 0 1px 0 rgba(255, 190, 195, 0.18);
|
||||
}
|
||||
.tool-card img {
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
@@ -185,8 +205,16 @@
|
||||
color: #9bc4dc;
|
||||
}
|
||||
.tool-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.tool-running-status {
|
||||
color: #ffb7bd;
|
||||
font-size: 11px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.tool-action button {
|
||||
background: linear-gradient(180deg, rgba(130, 220, 255, 0.9), rgba(40, 120, 190, 0.95));
|
||||
border: 1px solid rgba(140, 210, 255, 0.6);
|
||||
@@ -521,3 +549,899 @@
|
||||
.settings-page .settings-save {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.add-tool-page {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 22px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.add-tool-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.add-tool-heading h1 {
|
||||
margin: 3px 0 5px;
|
||||
color: var(--ink);
|
||||
font-size: 24px;
|
||||
}
|
||||
.add-tool-heading p {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
.add-tool-heading code {
|
||||
color: var(--accent);
|
||||
}
|
||||
.add-tool-eyebrow {
|
||||
color: var(--accent);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.4px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.add-tool-icon-preview {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
flex: 0 0 58px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
background: var(--glass-strong);
|
||||
color: var(--accent);
|
||||
font-size: 28px;
|
||||
box-shadow: 0 10px 20px var(--shadow), inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.add-tool-icon-preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.add-tool-form {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
.add-tool-section {
|
||||
padding: 16px;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 14px;
|
||||
background: var(--glass);
|
||||
box-shadow: 0 12px 22px var(--shadow), inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.add-tool-section h2 {
|
||||
margin: 0 0 14px;
|
||||
color: var(--ink);
|
||||
font-size: 15px;
|
||||
}
|
||||
.add-tool-section label {
|
||||
display: block;
|
||||
margin: 11px 0 6px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.add-tool-description {
|
||||
min-height: 64px;
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
}
|
||||
.add-tool-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(130px, 0.7fr) minmax(180px, 1.3fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.add-tool-input-suffix {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.add-tool-input-suffix span {
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
}
|
||||
.add-tool-file {
|
||||
padding: 7px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.add-tool-section-heading {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
}
|
||||
.add-tool-section-heading span {
|
||||
color: var(--ink-soft);
|
||||
font-size: 11px;
|
||||
}
|
||||
.add-tool-template-section .add-tool-section-heading {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.add-tool-template-section .add-tool-section-heading h2 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.add-tool-template-section .add-tool-section-heading p {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
}
|
||||
.add-tool-template-badge {
|
||||
flex: 0 0 auto;
|
||||
padding: 5px 9px;
|
||||
border: 1px solid rgba(120, 225, 190, 0.38);
|
||||
border-radius: 999px;
|
||||
background: rgba(70, 190, 145, 0.14);
|
||||
color: #91efc5 !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
.add-tool-template-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 9px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.add-tool-template {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 11px;
|
||||
background: rgba(12, 28, 45, 0.48);
|
||||
color: var(--ink);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease;
|
||||
}
|
||||
.add-tool-template:hover {
|
||||
border-color: rgba(140, 210, 255, 0.48);
|
||||
background: rgba(28, 60, 86, 0.58);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.add-tool-template.is-active {
|
||||
border-color: rgba(120, 210, 255, 0.68);
|
||||
background: linear-gradient(180deg, rgba(65, 150, 210, 0.28), rgba(30, 80, 120, 0.32));
|
||||
box-shadow: inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.add-tool-template-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: 0 0 30px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 9px;
|
||||
background: rgba(75, 180, 235, 0.14);
|
||||
color: var(--accent);
|
||||
font-size: 15px;
|
||||
}
|
||||
.add-tool-template strong,
|
||||
.add-tool-template small {
|
||||
display: block;
|
||||
}
|
||||
.add-tool-template strong {
|
||||
margin-bottom: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.add-tool-template small {
|
||||
overflow: hidden;
|
||||
color: var(--ink-soft);
|
||||
font-size: 10px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.add-tool-template-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.add-tool-template-controls[hidden] {
|
||||
display: none;
|
||||
}
|
||||
.add-tool-template-controls .settings-input {
|
||||
margin: 0;
|
||||
}
|
||||
.add-tool-script-section > summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--ink);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
}
|
||||
.add-tool-script-section > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
.add-tool-script-section > summary::before {
|
||||
content: "›";
|
||||
margin-right: 8px;
|
||||
color: var(--accent);
|
||||
font-size: 18px;
|
||||
transition: transform 0.16s ease;
|
||||
}
|
||||
.add-tool-script-section[open] > summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.add-tool-script-section > summary small {
|
||||
margin-left: auto;
|
||||
color: var(--ink-soft);
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.add-tool-script-section[open] > .add-tool-section-heading {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.add-tool-script-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.lua-help-trigger {
|
||||
border: 1px solid rgba(120, 210, 255, 0.42);
|
||||
border-radius: 999px;
|
||||
padding: 5px 10px;
|
||||
background: rgba(50, 135, 200, 0.24);
|
||||
color: #aee6ff;
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: background 0.16s ease, transform 0.16s ease;
|
||||
}
|
||||
.lua-help-trigger:hover {
|
||||
background: rgba(70, 165, 230, 0.38);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.add-tool-code-editor {
|
||||
position: relative;
|
||||
min-height: 150px;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 10px;
|
||||
background: rgba(12, 28, 45, 0.6);
|
||||
box-shadow: inset 0 1px 0 var(--shine);
|
||||
overflow: hidden;
|
||||
}
|
||||
.add-tool-code-editor pre,
|
||||
.settings-page .add-tool-script {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Consolas", "Courier New", monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
white-space: pre;
|
||||
}
|
||||
.add-tool-code-editor pre {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
color: var(--ink);
|
||||
}
|
||||
.settings-page .add-tool-script {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
resize: vertical;
|
||||
overflow: auto;
|
||||
background: transparent;
|
||||
color: transparent;
|
||||
caret-color: var(--ink);
|
||||
box-shadow: none;
|
||||
}
|
||||
.settings-page .add-tool-script::selection {
|
||||
background: rgba(90, 175, 235, 0.35);
|
||||
color: transparent;
|
||||
}
|
||||
.add-tool-code-editor .token.comment {
|
||||
color: #7897a8;
|
||||
font-style: italic;
|
||||
}
|
||||
.add-tool-code-editor .token.string {
|
||||
color: #ffd27a;
|
||||
}
|
||||
.add-tool-code-editor .token.number,
|
||||
.add-tool-code-editor .token.boolean {
|
||||
color: #8fe7ff;
|
||||
}
|
||||
.add-tool-code-editor .token.keyword {
|
||||
color: #88bfff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.add-tool-code-editor .token.function {
|
||||
color: #8be0c5;
|
||||
}
|
||||
.add-tool-code-editor .token.operator,
|
||||
.add-tool-code-editor .token.punctuation {
|
||||
color: #b9d5e5;
|
||||
}
|
||||
.add-tool-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
min-height: 38px;
|
||||
}
|
||||
.add-tool-actions .settings-status {
|
||||
margin: 0 auto 0 2px;
|
||||
}
|
||||
.add-tool-actions .settings-save {
|
||||
margin: 0;
|
||||
}
|
||||
.add-tool-secondary {
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 999px;
|
||||
padding: 8px 14px;
|
||||
background: rgba(12, 28, 45, 0.6);
|
||||
color: var(--ink-soft);
|
||||
cursor: pointer;
|
||||
}
|
||||
.settings-status[data-kind="success"] {
|
||||
color: #83e8b2;
|
||||
}
|
||||
.settings-status[data-kind="error"] {
|
||||
color: #ff9a9a;
|
||||
}
|
||||
.settings-status[data-kind="pending"] {
|
||||
color: var(--accent);
|
||||
}
|
||||
.lua-help-mark {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 1px solid rgba(140, 210, 255, 0.38);
|
||||
border-radius: 8px;
|
||||
background: rgba(79, 195, 255, 0.14);
|
||||
color: #8fe7ff;
|
||||
font: 700 18px/1 Georgia, serif;
|
||||
}
|
||||
.lua-help-tabs {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 9px 16px 0;
|
||||
border-bottom: 1px solid rgba(140, 210, 255, 0.18);
|
||||
background: rgba(10, 26, 40, 0.68);
|
||||
}
|
||||
.lua-help-tab {
|
||||
position: relative;
|
||||
bottom: -1px;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 0;
|
||||
border-radius: 10px 10px 0 0;
|
||||
padding: 9px 14px 10px;
|
||||
background: transparent;
|
||||
color: var(--ink-soft);
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lua-help-tab.active {
|
||||
border-color: rgba(140, 210, 255, 0.28);
|
||||
background: rgba(19, 43, 63, 0.96);
|
||||
color: var(--ink);
|
||||
}
|
||||
.lua-help-tab:hover {
|
||||
color: #8fe7ff;
|
||||
background: rgba(79, 195, 255, 0.1);
|
||||
}
|
||||
.lua-help-doc-status {
|
||||
margin-left: auto;
|
||||
padding-right: 4px;
|
||||
color: #8fe7ff;
|
||||
font-size: 11px;
|
||||
}
|
||||
.lua-help-panels {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.lua-help-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 22px;
|
||||
box-sizing: border-box;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(120, 210, 255, 0.7) rgba(20, 45, 70, 0.5);
|
||||
}
|
||||
.lua-help-hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.lua-help-hero h1 {
|
||||
margin: 4px 0 6px;
|
||||
font-size: 27px;
|
||||
color: var(--ink);
|
||||
}
|
||||
.lua-help-hero p {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
.lua-help-hero p code {
|
||||
color: #8fe7ff;
|
||||
}
|
||||
.lua-help-badge {
|
||||
border: 1px solid rgba(140, 210, 255, 0.36);
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
background: rgba(79, 195, 255, 0.1);
|
||||
color: #9fe2ff;
|
||||
font: 700 12px/1 "Consolas", monospace;
|
||||
box-shadow: inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.lua-help-notice {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 12px;
|
||||
align-items: baseline;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid rgba(255, 210, 122, 0.24);
|
||||
border-radius: 12px;
|
||||
background: rgba(95, 72, 25, 0.16);
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
}
|
||||
.lua-help-notice strong {
|
||||
color: #ffd27a;
|
||||
}
|
||||
.lua-help-notice code {
|
||||
color: #8fe7ff;
|
||||
}
|
||||
.lua-help-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 13px;
|
||||
}
|
||||
.lua-help-card,
|
||||
.lua-help-libraries {
|
||||
min-width: 0;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 14px;
|
||||
background: var(--glass);
|
||||
box-shadow: 0 10px 20px rgba(4, 10, 16, 0.32), inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.lua-help-card {
|
||||
padding: 14px;
|
||||
}
|
||||
.lua-help-card h2,
|
||||
.lua-help-libraries h2 {
|
||||
margin: 0 0 10px;
|
||||
color: #dff6ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.lua-help-card pre {
|
||||
margin: 0;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--ink);
|
||||
font: 12px/1.55 "Consolas", "Courier New", monospace;
|
||||
tab-size: 4;
|
||||
}
|
||||
.lua-help-card .token.comment {
|
||||
color: #7897a8;
|
||||
font-style: italic;
|
||||
}
|
||||
.lua-help-card .token.string {
|
||||
color: #ffd27a;
|
||||
}
|
||||
.lua-help-card .token.number,
|
||||
.lua-help-card .token.boolean {
|
||||
color: #8fe7ff;
|
||||
}
|
||||
.lua-help-card .token.keyword {
|
||||
color: #88bfff;
|
||||
font-weight: 650;
|
||||
}
|
||||
.lua-help-card .token.function {
|
||||
color: #8be0c5;
|
||||
}
|
||||
.lua-help-card .token.operator,
|
||||
.lua-help-card .token.punctuation {
|
||||
color: #b9d5e5;
|
||||
}
|
||||
.lua-help-libraries {
|
||||
margin-top: 14px;
|
||||
padding: 14px;
|
||||
}
|
||||
.lua-help-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
}
|
||||
.lua-help-chips code {
|
||||
border: 1px solid rgba(140, 210, 255, 0.24);
|
||||
border-radius: 999px;
|
||||
padding: 5px 9px;
|
||||
background: rgba(79, 195, 255, 0.1);
|
||||
color: #aee6ff;
|
||||
font-size: 11px;
|
||||
}
|
||||
.lua-help-footer-note {
|
||||
margin: 14px 0 0;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.lua-help-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.lua-help-doc-status,
|
||||
.lua-help-tab.active {
|
||||
display: none;
|
||||
}
|
||||
.add-tool-script-meta {
|
||||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
#create-button:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.65;
|
||||
}
|
||||
.tool-settings-page {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 20px 22px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(120, 210, 255, 0.7) rgba(20, 45, 70, 0.5);
|
||||
}
|
||||
.tool-settings-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.tool-settings-heading h1 {
|
||||
margin: 3px 0 4px;
|
||||
overflow: hidden;
|
||||
color: var(--ink);
|
||||
font-size: 21px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tool-settings-heading p {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
}
|
||||
.tool-settings-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 6px;
|
||||
margin-bottom: 14px;
|
||||
padding: 5px;
|
||||
border: 1px solid rgba(140, 210, 255, 0.22);
|
||||
border-radius: 12px;
|
||||
background: rgba(8, 22, 34, 0.46);
|
||||
}
|
||||
.tool-settings-tab {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 9px;
|
||||
padding: 8px 7px;
|
||||
background: transparent;
|
||||
color: var(--ink-soft);
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
font-weight: 650;
|
||||
cursor: pointer;
|
||||
transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease;
|
||||
}
|
||||
.tool-settings-tab:hover {
|
||||
background: rgba(79, 195, 255, 0.1);
|
||||
color: var(--ink);
|
||||
}
|
||||
.tool-settings-tab.is-active {
|
||||
border-color: rgba(140, 210, 255, 0.38);
|
||||
background: linear-gradient(180deg, rgba(65, 150, 210, 0.28), rgba(30, 80, 120, 0.32));
|
||||
color: var(--ink);
|
||||
box-shadow: inset 0 1px 0 var(--shine);
|
||||
}
|
||||
.tool-settings-form,
|
||||
.tool-settings-panel {
|
||||
min-width: 0;
|
||||
}
|
||||
.tool-settings-panel:not(.is-active) {
|
||||
display: none;
|
||||
}
|
||||
.tool-settings-panel.is-active {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.tool-settings-help {
|
||||
display: block;
|
||||
margin: 6px 0 10px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 11px;
|
||||
}
|
||||
.tool-settings-link {
|
||||
margin-top: 7px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #8fd7ff;
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tool-settings-link:hover {
|
||||
color: #d2f5ff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.tool-settings-target {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.tool-settings-target > label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tool-settings-target .add-tool-secondary {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.tool-settings-editor {
|
||||
min-height: 225px;
|
||||
}
|
||||
.tool-settings-editor pre,
|
||||
.tool-settings-editor .add-tool-script {
|
||||
min-height: 225px;
|
||||
}
|
||||
.tool-settings-generated {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
.tool-settings-generated-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: 0 0 30px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 1px solid rgba(120, 225, 190, 0.38);
|
||||
border-radius: 50%;
|
||||
background: rgba(70, 190, 145, 0.14);
|
||||
color: #91efc5;
|
||||
font-weight: 800;
|
||||
}
|
||||
.tool-settings-generated h2,
|
||||
.tool-settings-action-card h2 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.tool-settings-generated p,
|
||||
.tool-settings-action-card p {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.tool-settings-generated code,
|
||||
.tool-settings-action-card code {
|
||||
color: var(--accent);
|
||||
}
|
||||
.tool-settings-action-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
}
|
||||
.tool-settings-action-card .add-tool-secondary {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.tool-settings-footer {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 38px;
|
||||
padding: 10px 14px;
|
||||
border-top: 1px solid rgba(140, 210, 255, 0.2);
|
||||
background: linear-gradient(180deg, rgba(16, 36, 54, 0.92), rgba(8, 20, 31, 0.94));
|
||||
box-shadow: 0 -8px 20px rgba(4, 10, 16, 0.28);
|
||||
}
|
||||
.tool-settings-footer .settings-status {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
.tool-settings-footer .settings-save {
|
||||
margin: 0;
|
||||
}
|
||||
#save-button:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
/* Kontextmenü */
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
min-width: 160px;
|
||||
padding: 6px 0;
|
||||
border-radius: 10px;
|
||||
background: rgba(15, 30, 50, 0.98);
|
||||
border: 1px solid rgba(140, 210, 255, 0.22);
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.context-menu button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #dff6ff;
|
||||
font: 13px/1.3 "Segoe UI", sans-serif;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.12s ease;
|
||||
}
|
||||
|
||||
.context-menu button:hover {
|
||||
background: rgba(40, 80, 120, 0.6);
|
||||
}
|
||||
|
||||
.context-menu .context-menu-danger {
|
||||
color: #ff8a80;
|
||||
}
|
||||
|
||||
.context-menu .context-menu-danger:hover {
|
||||
background: rgba(180, 40, 40, 0.4);
|
||||
}
|
||||
|
||||
/* Allgemeine Einstellungen – Overlay */
|
||||
.settings-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9000;
|
||||
background: rgba(4, 10, 16, 0.55);
|
||||
backdrop-filter: blur(6px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.settings-overlay[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
width: 340px;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
background: linear-gradient(180deg, rgba(18, 40, 60, 0.97), rgba(12, 28, 45, 0.98));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.settings-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid rgba(140, 210, 255, 0.15);
|
||||
}
|
||||
|
||||
.settings-panel-header h2 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.settings-panel-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--ink-soft);
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
padding: 0 4px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.settings-panel-close:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.settings-panel-body {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
.settings-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.settings-row-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-row-title {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.settings-row-desc {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: var(--ink-soft);
|
||||
opacity: 0.7;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.settings-row-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.settings-row-value {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
min-width: 38px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.settings-row-control input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: rgba(20, 45, 70, 0.6);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.settings-row-control input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(180deg, var(--aqua-2), var(--aqua-3));
|
||||
border: 2px solid rgba(20, 45, 70, 0.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settings-row-control input[type="range"]::-moz-range-thumb {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--aqua-3);
|
||||
border: 2px solid rgba(20, 45, 70, 0.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
33
res/html/tool-runtime.js
Normal file
33
res/html/tool-runtime.js
Normal file
@@ -0,0 +1,33 @@
|
||||
(function (root, factory) {
|
||||
const api = factory();
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = api;
|
||||
}
|
||||
root.ToolRuntime = api;
|
||||
}(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
||||
'use strict';
|
||||
|
||||
function initialRunning(item) {
|
||||
return item?.singleInstance === true && item?.running === true;
|
||||
}
|
||||
|
||||
function isCardRunBlocked(card) {
|
||||
return card?.dataset?.runDisabled === 'true';
|
||||
}
|
||||
|
||||
function applyCardState(card, running) {
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
const active = running === true;
|
||||
card.classList.toggle('tool-running', active);
|
||||
card.dataset.runDisabled = active ? 'true' : 'false';
|
||||
card.setAttribute('aria-busy', active ? 'true' : 'false');
|
||||
const status = card.querySelector('.tool-running-status');
|
||||
if (status) {
|
||||
status.hidden = !active;
|
||||
}
|
||||
}
|
||||
|
||||
return { initialRunning, isCardRunBlocked, applyCardState };
|
||||
}));
|
||||
21
res/vendor/prism/LICENSE.txt
vendored
Normal file
21
res/vendor/prism/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2012 Lea Verou
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
1
res/vendor/prism/prism-core.min.js
vendored
Normal file
1
res/vendor/prism/prism-core.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
res/vendor/prism/prism-lua.min.js
vendored
Normal file
1
res/vendor/prism/prism-lua.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Prism.languages.lua={comment:/^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[^z]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,greedy:!0},number:/\b0x[a-f\d]+(?:\.[a-f\d]*)?(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|(?:\.\d*)?(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,keyword:/\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,function:/(?!\d)\w+(?=\s*(?:[({]))/,operator:[/[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,{pattern:/(^|[^.])\.\.(?!\.)/,lookbehind:!0}],punctuation:/[\[\](){},;]|\.+|:+/};
|
||||
Reference in New Issue
Block a user