Added screenshot + fixed v2 bugs

This commit is contained in:
2025-10-27 01:46:44 +01:00
parent 930d59c460
commit c6c35bb66a
4 changed files with 351 additions and 161 deletions

View File

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 264 KiB

View File

@@ -99,21 +99,6 @@ class ArgumentBuilder {
return args; return args;
} }
addSubcommand(name = '', description = '') {
const subcommandSection = document.createElement('div');
subcommandSection.className = 'subcommand-section';
const header = this.createSubcommandHeader(name, description);
const argsContainer = this.createArgumentsContainer();
subcommandSection.appendChild(header);
subcommandSection.appendChild(argsContainer);
this.subcommandContainer.appendChild(subcommandSection);
return subcommandSection;
}
updateHelpPreview() { updateHelpPreview() {
const header = this.headerInput.value.trim(); const header = this.headerInput.value.trim();
const args = this.getArguments(); const args = this.getArguments();
@@ -142,34 +127,6 @@ class ArgumentBuilder {
this.helpContent.textContent = helpText; this.helpContent.textContent = helpText;
} }
createSubcommandHeader(name, description) {
const header = document.createElement('div');
header.className = 'subcommand-header';
header.innerHTML = `
<input type="text" class="subcommand-name"
placeholder="container" value="${name}">
<input type="text" class="subcommand-desc"
placeholder="Manage containers" value="${description}">
<button class="toggle-btn">▼</button>
<button class="remove-btn">×</button>
`;
return header;
}
getSubcommands() {
const sections = this.subcommandContainer.querySelectorAll('.subcommand-section');
return Array.from(sections).map(section => {
const nameInput = section.querySelector('.subcommand-name');
const descInput = section.querySelector('.subcommand-desc');
const args = this.getArgumentsFromSection(section);
return {
name: nameInput.value.trim(),
description: descInput.value.trim(),
arguments: args
};
}).filter(sc => sc.name);
}
async generateScript() { async generateScript() {
const header = this.headerInput.value.trim(); const header = this.headerInput.value.trim();
@@ -181,9 +138,8 @@ class ArgumentBuilder {
} }
const payload = { const payload = {
header: this.headerInput.value.trim(), header: header,
arguments: this.getArguments(), arguments: args
subcommands: this.getSubcommands()
}; };
try { try {

View File

@@ -1,31 +1,120 @@
class ArgumentBuilder { class ArgumentBuilder {
constructor() { constructor() {
this.arguments = [];
this.container = document.getElementById('arguments-container');
this.headerInput = document.getElementById('custom-header'); this.headerInput = document.getElementById('custom-header');
this.generateBtn = document.getElementById('generate-btn'); this.generateBtn = document.getElementById('generate-btn');
this.helpContent = document.getElementById('help-content'); this.helpContent = document.getElementById('help-content');
this.outputSection = document.getElementById('output-section'); this.outputSection = document.getElementById('output-section');
this.generatedCode = document.getElementById('generated-code'); this.generatedCode = document.getElementById('generated-code');
this.copyBtn = document.getElementById('copy-btn'); this.copyBtn = document.getElementById('copy-btn');
this.globalContainer =
document.getElementById('global-arguments-container') ||
document.getElementById('arguments-container');
this.subcommandsContainer = document.querySelector('.subcommands-container');
this.addSubcommandBtn = document.getElementById('add-subcommand-btn');
this.init(); this.init();
} }
init() { init() {
// Add initial empty row if (!this.headerInput || !this.generateBtn || !this.helpContent || !this.globalContainer) {
this.addArgumentRow(); return;
}
this.hydrateArgumentContainer(this.globalContainer);
if (this.subcommandsContainer) {
const sections = Array.from(this.subcommandsContainer.querySelectorAll('.subcommand-section'));
sections.forEach(section => this.hydrateSubcommandSection(section));
}
if (this.addSubcommandBtn) {
const clone = this.addSubcommandBtn.cloneNode(true);
clone.type = 'button';
clone.addEventListener('click', () => {
const section = this.createSubcommandSection();
if (section) {
section.classList.remove('collapsed');
section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
const nameInput = section.querySelector('.subcommand-name-input');
if (nameInput) {
nameInput.focus();
}
this.updateHelpPreview();
}
});
this.addSubcommandBtn.replaceWith(clone);
this.addSubcommandBtn = clone;
}
// Event listeners
this.generateBtn.addEventListener('click', () => this.generateScript()); this.generateBtn.addEventListener('click', () => this.generateScript());
this.headerInput.addEventListener('input', () => this.updateHelpPreview()); this.headerInput.addEventListener('input', () => this.updateHelpPreview());
this.copyBtn.addEventListener('click', () => this.copyToClipboard());
// Update help preview initially if (this.copyBtn) {
this.copyBtn.addEventListener('click', () => this.copyToClipboard());
}
this.updateHelpPreview(); this.updateHelpPreview();
} }
addArgumentRow(params = '', command = '', helpText = '') { hydrateArgumentContainer(container) {
if (!container) {
return;
}
const rows = Array.from(container.querySelectorAll('.argument-row'));
if (rows.length === 0) {
this.createArgumentRow(container);
return;
}
rows.forEach(row => this.attachRowHandlers(row, container));
this.ensureTrailingRow(container);
}
attachRowHandlers(row, container) {
const inputs = Array.from(row.querySelectorAll('input'));
if (inputs.length === 0) {
return;
}
const update = () => {
this.updateHelpPreview();
this.ensureTrailingRow(container);
};
inputs.forEach(input => {
input.addEventListener('input', update);
});
const removeBtn = row.querySelector('.remove-btn');
if (removeBtn) {
removeBtn.type = 'button';
removeBtn.addEventListener('click', () => {
row.remove();
this.updateHelpPreview();
this.ensureTrailingRow(container);
});
}
}
ensureTrailingRow(container) {
if (!container) {
return;
}
const rows = Array.from(container.querySelectorAll('.argument-row'));
if (rows.length === 0) {
this.createArgumentRow(container);
return;
}
const lastRow = rows[rows.length - 1];
const hasContent = Array.from(lastRow.querySelectorAll('input')).some(
input => input.value.trim() !== ''
);
if (hasContent) {
this.createArgumentRow(container);
}
}
createArgumentRow(container, params = '', command = '', helpText = '') {
if (!container) {
return null;
}
const row = document.createElement('div'); const row = document.createElement('div');
row.className = 'argument-row'; row.className = 'argument-row';
@@ -46,148 +135,287 @@ class ArgumentBuilder {
const removeBtn = document.createElement('button'); const removeBtn = document.createElement('button');
removeBtn.className = 'remove-btn'; removeBtn.className = 'remove-btn';
removeBtn.type = 'button';
removeBtn.innerHTML = '×'; removeBtn.innerHTML = '×';
removeBtn.addEventListener('click', () => {
row.remove();
this.updateHelpPreview();
});
// Add input event listeners for real-time updates
const updatePreview = () => {
this.updateHelpPreview();
// Add new empty row if this is the last row and has content
const rows = this.container.querySelectorAll('.argument-row');
const isLastRow = rows[rows.length - 1] === row;
const hasContent = commandInput.value.trim() !== '';
if (isLastRow && hasContent) {
this.addArgumentRow();
}
};
paramsInput.addEventListener('input', updatePreview);
commandInput.addEventListener('input', updatePreview);
helpTextInput.addEventListener('input', updatePreview);
row.appendChild(paramsInput); row.appendChild(paramsInput);
row.appendChild(commandInput); row.appendChild(commandInput);
row.appendChild(helpTextInput); row.appendChild(helpTextInput);
row.appendChild(removeBtn); row.appendChild(removeBtn);
this.container.appendChild(row); container.appendChild(row);
this.attachRowHandlers(row, container);
return row;
} }
getArguments() { hydrateSubcommandSection(section, presetArgs) {
const rows = this.container.querySelectorAll('.argument-row'); if (!section) {
const args = []; return;
}
rows.forEach(row => { const nameInput = section.querySelector('.subcommand-name-input');
const inputs = row.querySelectorAll('input'); const descInput = section.querySelector('.subcommand-desc-input');
const params = inputs[0].value.trim(); const toggleBtn = section.querySelector('.toggle-collapse-btn');
const command = inputs[1].value.trim(); const removeBtn = section.querySelector('.subcommand-header .remove-btn');
const helpText = inputs[2].value.trim(); const argsContainer = section.querySelector('.arguments-container');
if (command) { if (nameInput) {
args.push({ nameInput.addEventListener('input', () => this.updateHelpPreview());
}
if (descInput) {
descInput.addEventListener('input', () => this.updateHelpPreview());
}
if (toggleBtn) {
toggleBtn.type = 'button';
toggleBtn.addEventListener('click', () => {
section.classList.toggle('collapsed');
});
}
if (removeBtn) {
removeBtn.type = 'button';
removeBtn.addEventListener('click', () => {
section.remove();
this.updateHelpPreview();
});
}
if (argsContainer) {
if (Array.isArray(presetArgs)) {
argsContainer.innerHTML = '';
presetArgs.forEach(arg => {
this.createArgumentRow(
argsContainer,
arg.params || '',
arg.command || '',
arg.helpText || ''
);
});
}
this.hydrateArgumentContainer(argsContainer);
}
}
createSubcommandSection(name = '', description = '', args = []) {
if (!this.subcommandsContainer) {
return null;
}
const section = document.createElement('div');
section.className = 'subcommand-section';
const header = document.createElement('div');
header.className = 'subcommand-header';
const nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.className = 'subcommand-name-input';
nameInput.placeholder = 'container';
nameInput.value = name;
const descInput = document.createElement('input');
descInput.type = 'text';
descInput.className = 'subcommand-desc-input';
descInput.placeholder = 'Manage containers';
descInput.value = description;
const toggleBtn = document.createElement('button');
toggleBtn.className = 'toggle-collapse-btn';
toggleBtn.type = 'button';
const removeBtn = document.createElement('button');
removeBtn.className = 'remove-btn';
removeBtn.type = 'button';
removeBtn.innerHTML = '×';
header.appendChild(nameInput);
header.appendChild(descInput);
header.appendChild(toggleBtn);
header.appendChild(removeBtn);
const body = document.createElement('div');
body.className = 'subcommand-body';
const headerRow = this.createArgumentHeader();
const argsContainer = document.createElement('div');
argsContainer.className = 'arguments-container';
body.appendChild(headerRow);
body.appendChild(argsContainer);
section.appendChild(header);
section.appendChild(body);
if (this.addSubcommandBtn) {
this.addSubcommandBtn.before(section);
} else {
this.subcommandsContainer.appendChild(section);
}
this.hydrateSubcommandSection(section, args);
return section;
}
createArgumentHeader() {
const row = document.createElement('div');
row.className = 'argument-header';
const labels = ['Parameters', 'Variable Name', 'Help Text', ''];
labels.forEach(text => {
const span = document.createElement('span');
span.textContent = text;
row.appendChild(span);
});
return row;
}
getArgumentsFromContainer(container) {
if (!container) {
return [];
}
const rows = Array.from(container.querySelectorAll('.argument-row'));
return rows
.map(row => {
const inputs = row.querySelectorAll('input');
if (inputs.length < 3) {
return null;
}
const params = inputs[0].value.trim();
const command = inputs[1].value.trim();
const helpText = inputs[2].value.trim();
if (!command) {
return null;
}
return {
params: params || command, params: params || command,
command: command, command: command,
helpText: helpText || command helpText: helpText || command
}); };
} })
}); .filter(Boolean);
return args;
} }
addSubcommand(name = '', description = '') { getGlobalArguments() {
const subcommandSection = document.createElement('div'); return this.getArgumentsFromContainer(this.globalContainer);
subcommandSection.className = 'subcommand-section'; }
const header = this.createSubcommandHeader(name, description); getSubcommands() {
const argsContainer = this.createArgumentsContainer(); if (!this.subcommandsContainer) {
return [];
}
const sections = Array.from(
this.subcommandsContainer.querySelectorAll('.subcommand-section')
);
return sections
.map(section => {
const name =
section.querySelector('.subcommand-name-input')?.value.trim() || '';
const description =
section.querySelector('.subcommand-desc-input')?.value.trim() || '';
const argsContainer = section.querySelector('.arguments-container');
const arguments = this.getArgumentsFromContainer(argsContainer);
return {
name,
description,
arguments
};
})
.filter(subcmd => subcmd.name);
}
subcommandSection.appendChild(header); padLabel(label, width = 20) {
subcommandSection.appendChild(argsContainer); if (!label) {
return ''.padEnd(width, ' ');
this.subcommandContainer.appendChild(subcommandSection); }
if (label.length >= width) {
return subcommandSection; return label + ' ';
}
return label + ' '.repeat(width - label.length);
} }
updateHelpPreview() { updateHelpPreview() {
const header = this.headerInput.value.trim(); if (!this.helpContent) {
const args = this.getArguments(); return;
}
if (args.length === 0) { const header = this.headerInput ? this.headerInput.value.trim() : '';
const globalArgs = this.getGlobalArguments();
const subcommands = this.getSubcommands();
if (globalArgs.length === 0 && subcommands.length === 0) {
this.helpContent.textContent = 'Add arguments to see the help output...'; this.helpContent.textContent = 'Add arguments to see the help output...';
return; return;
} }
let helpText = ''; const lines = [];
if (header) { if (header) {
helpText += header + '\n\n'; lines.push(header, '');
} }
helpText += 'Usage: script.sh [OPTIONS]\n\n'; let usage = 'Usage: script.sh';
helpText += 'Options:\n'; if (subcommands.length > 0) {
if (globalArgs.length > 0) {
usage += ' [GLOBAL OPTIONS]';
}
usage += ' COMMAND [COMMAND OPTIONS]';
} else {
usage += ' [OPTIONS]';
}
lines.push(usage, '');
args.forEach(arg => { if (globalArgs.length > 0) {
const params = arg.params || arg.command; lines.push('Global Options:');
const help = arg.helpText || arg.command; globalArgs.forEach(arg => {
helpText += ` ${params.padEnd(20)} ${help}\n`; const params = arg.params || arg.command;
}); const help = arg.helpText || arg.command;
lines.push(` ${this.padLabel(params)}${help}`);
});
lines.push(` ${this.padLabel('-h --help')}Show this help message`, '');
}
helpText += ` ${'-h --help'.padEnd(20)} Show this help message\n`; if (subcommands.length > 0) {
lines.push('Subcommands:');
subcommands.forEach(subcmd => {
const desc = subcmd.description || subcmd.name;
lines.push(` ${this.padLabel(subcmd.name)}${desc}`);
});
lines.push('', "Run 'script.sh COMMAND --help' for more information on a command.");
this.helpContent.textContent = helpText; subcommands.forEach(subcmd => {
} if (subcmd.arguments.length === 0) {
createSubcommandHeader(name, description) { return;
const header = document.createElement('div'); }
header.className = 'subcommand-header'; lines.push('', `${subcmd.name} Options:`);
header.innerHTML = ` subcmd.arguments.forEach(arg => {
<input type="text" class="subcommand-name" const params = arg.params || arg.command;
placeholder="container" value="${name}"> const help = arg.helpText || arg.command;
<input type="text" class="subcommand-desc" lines.push(` ${this.padLabel(params)}${help}`);
placeholder="Manage containers" value="${description}"> });
<button class="toggle-btn">▼</button> lines.push(` ${this.padLabel('-h --help')}Show this help message`);
<button class="remove-btn">×</button> });
`; }
return header;
}
getSubcommands() { this.helpContent.textContent = lines.join('\n');
const sections = this.subcommandContainer.querySelectorAll('.subcommand-section');
return Array.from(sections).map(section => {
const nameInput = section.querySelector('.subcommand-name');
const descInput = section.querySelector('.subcommand-desc');
const args = this.getArgumentsFromSection(section);
return {
name: nameInput.value.trim(),
description: descInput.value.trim(),
arguments: args
};
}).filter(sc => sc.name);
} }
async generateScript() { async generateScript() {
const header = this.headerInput.value.trim();
const args = this.getArguments();
if (args.length === 0) {
alert('Please add at least one argument');
return;
}
const payload = { const payload = {
header: this.headerInput.value.trim(), header: this.headerInput ? this.headerInput.value.trim() : '',
arguments: this.getArguments(), arguments: this.getGlobalArguments(),
subcommands: this.getSubcommands() subcommands: this.getSubcommands()
}; };
if (payload.arguments.length === 0 && payload.subcommands.length === 0) {
alert('Please add at least one global argument or subcommand before generating.');
return;
}
try { try {
const response = await fetch('/generate', { const response = await fetch('/generate/v2', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@@ -200,19 +428,24 @@ class ArgumentBuilder {
} }
const scriptCode = await response.text(); const scriptCode = await response.text();
this.generatedCode.textContent = scriptCode; if (this.generatedCode) {
this.outputSection.style.display = 'block'; this.generatedCode.textContent = scriptCode;
}
// Smooth scroll to output if (this.outputSection) {
this.outputSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); this.outputSection.style.display = 'block';
this.outputSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
} catch (error) { } catch (error) {
alert('Error generating script: ' + error.message); alert('Error generating script: ' + error.message);
} }
} }
async copyToClipboard() { async copyToClipboard() {
const code = this.generatedCode.textContent; if (!this.generatedCode || !this.copyBtn) {
return;
}
const code = this.generatedCode.textContent;
try { try {
await navigator.clipboard.writeText(code); await navigator.clipboard.writeText(code);
this.copyBtn.textContent = 'Copied!'; this.copyBtn.textContent = 'Copied!';
@@ -228,7 +461,6 @@ class ArgumentBuilder {
} }
} }
// Initialize the application when DOM is ready
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new ArgumentBuilder(); new ArgumentBuilder();
}); });

View File

@@ -150,6 +150,7 @@
</section> </section>
</div> </div>
<script src="/static/js/v2.js"></script>
<script> <script>
// Mode switching // Mode switching
document.querySelectorAll('.mode-btn').forEach(btn => { document.querySelectorAll('.mode-btn').forEach(btn => {
@@ -176,6 +177,7 @@
console.log('Add subcommand'); console.log('Add subcommand');
}); });
</script> </script>
</body> </body>
</html> </html>