Files
argparser/static/js/v2.js

467 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class ArgumentBuilder {
constructor() {
this.headerInput = document.getElementById('custom-header');
this.generateBtn = document.getElementById('generate-btn');
this.helpContent = document.getElementById('help-content');
this.outputSection = document.getElementById('output-section');
this.generatedCode = document.getElementById('generated-code');
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();
}
init() {
if (!this.headerInput || !this.generateBtn || !this.helpContent || !this.globalContainer) {
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;
}
this.generateBtn.addEventListener('click', () => this.generateScript());
this.headerInput.addEventListener('input', () => this.updateHelpPreview());
if (this.copyBtn) {
this.copyBtn.addEventListener('click', () => this.copyToClipboard());
}
this.updateHelpPreview();
}
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');
row.className = 'argument-row';
const paramsInput = document.createElement('input');
paramsInput.type = 'text';
paramsInput.placeholder = '-v --verbose';
paramsInput.value = params;
const commandInput = document.createElement('input');
commandInput.type = 'text';
commandInput.placeholder = 'verbose';
commandInput.value = command;
const helpTextInput = document.createElement('input');
helpTextInput.type = 'text';
helpTextInput.placeholder = 'Enable verbose output';
helpTextInput.value = helpText;
const removeBtn = document.createElement('button');
removeBtn.className = 'remove-btn';
removeBtn.type = 'button';
removeBtn.innerHTML = '×';
row.appendChild(paramsInput);
row.appendChild(commandInput);
row.appendChild(helpTextInput);
row.appendChild(removeBtn);
container.appendChild(row);
this.attachRowHandlers(row, container);
return row;
}
hydrateSubcommandSection(section, presetArgs) {
if (!section) {
return;
}
const nameInput = section.querySelector('.subcommand-name-input');
const descInput = section.querySelector('.subcommand-desc-input');
const toggleBtn = section.querySelector('.toggle-collapse-btn');
const removeBtn = section.querySelector('.subcommand-header .remove-btn');
const argsContainer = section.querySelector('.arguments-container');
if (nameInput) {
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,
command: command,
helpText: helpText || command
};
})
.filter(Boolean);
}
getGlobalArguments() {
return this.getArgumentsFromContainer(this.globalContainer);
}
getSubcommands() {
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);
}
padLabel(label, width = 20) {
if (!label) {
return ''.padEnd(width, ' ');
}
if (label.length >= width) {
return label + ' ';
}
return label + ' '.repeat(width - label.length);
}
updateHelpPreview() {
if (!this.helpContent) {
return;
}
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...';
return;
}
const lines = [];
if (header) {
lines.push(header, '');
}
let usage = 'Usage: script.sh';
if (subcommands.length > 0) {
if (globalArgs.length > 0) {
usage += ' [GLOBAL OPTIONS]';
}
usage += ' COMMAND [COMMAND OPTIONS]';
} else {
usage += ' [OPTIONS]';
}
lines.push(usage, '');
if (globalArgs.length > 0) {
lines.push('Global Options:');
globalArgs.forEach(arg => {
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`, '');
}
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.");
subcommands.forEach(subcmd => {
if (subcmd.arguments.length === 0) {
return;
}
lines.push('', `${subcmd.name} Options:`);
subcmd.arguments.forEach(arg => {
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`);
});
}
this.helpContent.textContent = lines.join('\n');
}
async generateScript() {
const payload = {
header: this.headerInput ? this.headerInput.value.trim() : '',
arguments: this.getGlobalArguments(),
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 {
const response = await fetch('/generate/v2', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error('Failed to generate script');
}
const scriptCode = await response.text();
if (this.generatedCode) {
this.generatedCode.textContent = scriptCode;
}
if (this.outputSection) {
this.outputSection.style.display = 'block';
this.outputSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
} catch (error) {
alert('Error generating script: ' + error.message);
}
}
async copyToClipboard() {
if (!this.generatedCode || !this.copyBtn) {
return;
}
const code = this.generatedCode.textContent;
try {
await navigator.clipboard.writeText(code);
this.copyBtn.textContent = 'Copied!';
this.copyBtn.classList.add('copied');
setTimeout(() => {
this.copyBtn.textContent = 'Copy';
this.copyBtn.classList.remove('copied');
}, 2000);
} catch (error) {
alert('Failed to copy to clipboard');
}
}
}
document.addEventListener('DOMContentLoaded', () => {
new ArgumentBuilder();
});