feat(ui): add quick action settings persistence and improve command form handling

- Add localStorage persistence for quick action checkboxes and log level
- Implement collectAllFormValues method for comprehensive form data collection
- Simplify config initialization to create empty config.yml instead of copying sample
- Update command section handling to use all form values for proper saving
This commit is contained in:
bobokun 2025-08-28 12:15:53 -04:00
parent b2ee109dfe
commit 0b540b8d8a
No known key found for this signature in database
GPG key ID: B73932169607D927
5 changed files with 136 additions and 13 deletions

View file

@ -1 +1 @@
4.5.6-develop5
4.5.6-develop6

View file

@ -300,7 +300,7 @@ def ensure_config_dir_initialized(config_dir) -> str:
Ensure the config directory exists and is initialized:
- Creates the config directory
- Creates logs/ and .backups/ subdirectories
- Seeds a default config.yml from bundled config/config.yml.sample if no *.yml/*.yaml present
- Creates an empty config.yml if no *.yml/*.yaml present
Returns the absolute config directory as a string.
"""
p = Path(config_dir).expanduser().resolve()
@ -310,14 +310,12 @@ def ensure_config_dir_initialized(config_dir) -> str:
has_yaml = any(p.glob("*.yml")) or any(p.glob("*.yaml"))
if not has_yaml:
sample = runtime_path("config", "config.yml.sample")
if sample.exists():
dest = p / "config.yml"
try:
shutil.copyfile(sample, dest)
except Exception:
# Non-fatal; if copy fails, user can create a config manually
pass
dest = p / "config.yml"
try:
dest.touch() # Create empty file
except Exception:
# Non-fatal; if creation fails, user can create a config manually
pass
return str(p)

View file

@ -225,8 +225,6 @@ class QbitManageApp {
this.backupConfig();
});
// Theme toggle is handled by ThemeManager
// Undo button
const undoBtn = get('undo-btn');
if (undoBtn) {
@ -442,7 +440,15 @@ class QbitManageApp {
}
try {
const processedData = this.configForm._postprocessDataForSave(this.currentSection, this.configForm.currentData);
// For commands section, collect all form values since commands should override env vars
let dataToProcess;
if (this.currentSection === 'commands') {
dataToProcess = this.configForm.collectAllFormValues(this.currentSection);
} else {
dataToProcess = this.configForm.currentData;
}
const processedData = this.configForm._postprocessDataForSave(this.currentSection, dataToProcess);
const isMultiRoot = this.configForm.schemas[this.currentSection]?.type === 'multi-root-object';
const dataToSave = isMultiRoot ? processedData : { [this.currentSection]: processedData };

View file

@ -141,6 +141,9 @@ class CommandPanel {
</div>
`;
// Load saved quick action values after rendering
this.loadQuickActionValues();
}
bindEvents() {
@ -175,6 +178,9 @@ class CommandPanel {
this.showRunCommandsModal();
}
});
// Bind quick action input change events for persistence
this.bindQuickActionPersistence();
}
async executeQuickCommand(command) {
@ -475,6 +481,74 @@ class CommandPanel {
this.show();
}
}
// Load saved quick action values from localStorage
loadQuickActionValues() {
if (!this.drawerContainer) return;
// Get saved values, defaulting dry run to true if not previously saved
const savedDryRunValue = localStorage.getItem('qbm-quick-dry-run');
const savedDryRun = savedDryRunValue !== null ? savedDryRunValue === 'true' : true; // Default to true
const savedSkipCleanup = localStorage.getItem('qbm-quick-skip-cleanup') === 'true';
const savedSkipQbVersionCheck = localStorage.getItem('qbm-quick-skip-qb-version-check') === 'true';
const savedLogLevel = localStorage.getItem('qbm-quick-log-level') || '';
const dryRunCheckbox = this.drawerContainer.querySelector('#dry-run-checkbox');
const skipCleanupCheckbox = this.drawerContainer.querySelector('#quick-skip-cleanup-checkbox');
const skipQbVersionCheckCheckbox = this.drawerContainer.querySelector('#quick-skip-qb-version-check-checkbox');
const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select');
if (dryRunCheckbox) dryRunCheckbox.checked = savedDryRun;
if (skipCleanupCheckbox) skipCleanupCheckbox.checked = savedSkipCleanup;
if (skipQbVersionCheckCheckbox) skipQbVersionCheckCheckbox.checked = savedSkipQbVersionCheck;
if (logLevelSelect) logLevelSelect.value = savedLogLevel;
// Save the default value if it was set
if (savedDryRunValue === null) {
localStorage.setItem('qbm-quick-dry-run', 'true');
}
}
// Save quick action values to localStorage
saveQuickActionValues() {
if (!this.drawerContainer) return;
const dryRunCheckbox = this.drawerContainer.querySelector('#dry-run-checkbox');
const skipCleanupCheckbox = this.drawerContainer.querySelector('#quick-skip-cleanup-checkbox');
const skipQbVersionCheckCheckbox = this.drawerContainer.querySelector('#quick-skip-qb-version-check-checkbox');
const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select');
const dryRun = dryRunCheckbox ? dryRunCheckbox.checked : false;
const skipCleanup = skipCleanupCheckbox ? skipCleanupCheckbox.checked : false;
const skipQbVersionCheck = skipQbVersionCheckCheckbox ? skipQbVersionCheckCheckbox.checked : false;
const logLevel = logLevelSelect ? logLevelSelect.value : '';
localStorage.setItem('qbm-quick-dry-run', dryRun);
localStorage.setItem('qbm-quick-skip-cleanup', skipCleanup);
localStorage.setItem('qbm-quick-skip-qb-version-check', skipQbVersionCheck);
localStorage.setItem('qbm-quick-log-level', logLevel);
}
// Bind event listeners for quick action persistence
bindQuickActionPersistence() {
if (!this.drawerContainer) return;
// Bind checkbox change events
const checkboxes = this.drawerContainer.querySelectorAll('#dry-run-checkbox, #quick-skip-cleanup-checkbox, #quick-skip-qb-version-check-checkbox');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', () => {
this.saveQuickActionValues();
});
});
// Bind select change event
const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select');
if (logLevelSelect) {
logLevelSelect.addEventListener('change', () => {
this.saveQuickActionValues();
});
}
}
}
export { CommandPanel };

View file

@ -1302,6 +1302,51 @@ class ConfigForm {
* @param {object} obj - The object to clean up.
* @returns {object} The cleaned up object.
*/
/**
* Collects all current form values for a section, not just dirty ones
* This is used for sections like commands where we want to save all values
*/
collectAllFormValues(sectionName) {
const sectionConfig = this.schemas[sectionName];
if (!sectionConfig || !sectionConfig.fields) {
return {};
}
const allValues = {};
// Iterate through all fields in the schema
sectionConfig.fields.forEach(field => {
if (field.name && field.type !== 'documentation' && field.type !== 'section_header') {
// Find the corresponding form input
const input = this.container.querySelector(`[name="${field.name}"]`);
if (input) {
let value;
if (input.type === 'checkbox') {
value = input.checked;
} else if (input.type === 'number') {
value = input.value ? parseFloat(input.value) : null;
} else {
value = input.value;
}
// Handle default values for boolean fields
if (field.type === 'boolean' && value === null) {
value = field.default || false;
}
allValues[field.name] = value;
} else if (field.default !== undefined) {
// Use default value if input not found
allValues[field.name] = field.default;
}
}
});
return allValues;
}
cleanupEmptyValues(obj) {
if (obj === null || obj === undefined) {
return null;