Added screenshot + fixed v2 bugs
This commit is contained in:
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 264 KiB |
@@ -99,21 +99,6 @@ class ArgumentBuilder {
|
||||
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() {
|
||||
const header = this.headerInput.value.trim();
|
||||
const args = this.getArguments();
|
||||
@@ -142,34 +127,6 @@ class ArgumentBuilder {
|
||||
|
||||
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() {
|
||||
const header = this.headerInput.value.trim();
|
||||
@@ -181,9 +138,8 @@ class ArgumentBuilder {
|
||||
}
|
||||
|
||||
const payload = {
|
||||
header: this.headerInput.value.trim(),
|
||||
arguments: this.getArguments(),
|
||||
subcommands: this.getSubcommands()
|
||||
header: header,
|
||||
arguments: args
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
462
static/js/v2.js
462
static/js/v2.js
@@ -1,31 +1,120 @@
|
||||
class ArgumentBuilder {
|
||||
constructor() {
|
||||
this.arguments = [];
|
||||
this.container = document.getElementById('arguments-container');
|
||||
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() {
|
||||
// Add initial empty row
|
||||
this.addArgumentRow();
|
||||
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;
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
this.generateBtn.addEventListener('click', () => this.generateScript());
|
||||
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();
|
||||
}
|
||||
|
||||
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');
|
||||
row.className = 'argument-row';
|
||||
|
||||
@@ -46,148 +135,287 @@ class ArgumentBuilder {
|
||||
|
||||
const removeBtn = document.createElement('button');
|
||||
removeBtn.className = 'remove-btn';
|
||||
removeBtn.type = 'button';
|
||||
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(commandInput);
|
||||
row.appendChild(helpTextInput);
|
||||
row.appendChild(removeBtn);
|
||||
|
||||
this.container.appendChild(row);
|
||||
container.appendChild(row);
|
||||
this.attachRowHandlers(row, container);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
getArguments() {
|
||||
const rows = this.container.querySelectorAll('.argument-row');
|
||||
const args = [];
|
||||
hydrateSubcommandSection(section, presetArgs) {
|
||||
if (!section) {
|
||||
return;
|
||||
}
|
||||
|
||||
rows.forEach(row => {
|
||||
const inputs = row.querySelectorAll('input');
|
||||
const params = inputs[0].value.trim();
|
||||
const command = inputs[1].value.trim();
|
||||
const helpText = inputs[2].value.trim();
|
||||
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 (command) {
|
||||
args.push({
|
||||
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
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return args;
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
addSubcommand(name = '', description = '') {
|
||||
const subcommandSection = document.createElement('div');
|
||||
subcommandSection.className = 'subcommand-section';
|
||||
getGlobalArguments() {
|
||||
return this.getArgumentsFromContainer(this.globalContainer);
|
||||
}
|
||||
|
||||
const header = this.createSubcommandHeader(name, description);
|
||||
const argsContainer = this.createArgumentsContainer();
|
||||
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);
|
||||
}
|
||||
|
||||
subcommandSection.appendChild(header);
|
||||
subcommandSection.appendChild(argsContainer);
|
||||
|
||||
this.subcommandContainer.appendChild(subcommandSection);
|
||||
|
||||
return subcommandSection;
|
||||
padLabel(label, width = 20) {
|
||||
if (!label) {
|
||||
return ''.padEnd(width, ' ');
|
||||
}
|
||||
if (label.length >= width) {
|
||||
return label + ' ';
|
||||
}
|
||||
return label + ' '.repeat(width - label.length);
|
||||
}
|
||||
|
||||
updateHelpPreview() {
|
||||
const header = this.headerInput.value.trim();
|
||||
const args = this.getArguments();
|
||||
if (!this.helpContent) {
|
||||
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...';
|
||||
return;
|
||||
}
|
||||
|
||||
let helpText = '';
|
||||
const lines = [];
|
||||
|
||||
if (header) {
|
||||
helpText += header + '\n\n';
|
||||
lines.push(header, '');
|
||||
}
|
||||
|
||||
helpText += 'Usage: script.sh [OPTIONS]\n\n';
|
||||
helpText += 'Options:\n';
|
||||
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, '');
|
||||
|
||||
args.forEach(arg => {
|
||||
const params = arg.params || arg.command;
|
||||
const help = arg.helpText || arg.command;
|
||||
helpText += ` ${params.padEnd(20)} ${help}\n`;
|
||||
});
|
||||
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`, '');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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`);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
this.helpContent.textContent = lines.join('\n');
|
||||
}
|
||||
|
||||
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 = {
|
||||
header: this.headerInput.value.trim(),
|
||||
arguments: this.getArguments(),
|
||||
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', {
|
||||
const response = await fetch('/generate/v2', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -200,19 +428,24 @@ class ArgumentBuilder {
|
||||
}
|
||||
|
||||
const scriptCode = await response.text();
|
||||
this.generatedCode.textContent = scriptCode;
|
||||
this.outputSection.style.display = 'block';
|
||||
|
||||
// Smooth scroll to output
|
||||
this.outputSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
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() {
|
||||
const code = this.generatedCode.textContent;
|
||||
if (!this.generatedCode || !this.copyBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const code = this.generatedCode.textContent;
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
this.copyBtn.textContent = 'Copied!';
|
||||
@@ -228,7 +461,6 @@ class ArgumentBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the application when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new ArgumentBuilder();
|
||||
});
|
||||
|
||||
@@ -150,6 +150,7 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/v2.js"></script>
|
||||
<script>
|
||||
// Mode switching
|
||||
document.querySelectorAll('.mode-btn').forEach(btn => {
|
||||
@@ -176,6 +177,7 @@
|
||||
console.log('Add subcommand');
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user