Files
arcum42-ComfyUI_SageUtils/js/shared/datasetTextGeneration.js
T

192 lines
6.0 KiB
JavaScript

/**
* LLM Generation Panel Component
* Builds the preset selector, append/overwrite mode, and action buttons.
* Delegates generation work to callbacks provided by the caller.
*/
import { createButton, BUTTON_VARIANTS } from '../components/buttons.js';
import { createSelect, createRadioGroup } from '../components/formElements.js';
import { notifications } from './notifications.js';
import { getPresets } from '../llm/llmPresetAPI.js';
import { confirmDialog } from '../components/dialogManager.js';
import { createCollapsiblePanel } from '../components/collapsiblePanel.js';
/**
* Create the LLM generation panel
* @param {Object} options
* @param {Function} options.onGenerateCurrent - async (presetId: string, isAppend: boolean) => Promise<void>
* @param {Function} options.onGenerateAll - async (presetId: string, isAppend: boolean) => Promise<void>
* @returns {HTMLElement} Panel element
*/
export function createLLMGenerationPanel({ onGenerateCurrent, onGenerateAll, batchCount = null } = {}) {
const llmPanel = document.createElement('div');
llmPanel.style.cssText = `
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 15px;
background: #3a3a3a;
padding: 10px;
border-radius: 4px;
border: 1px solid #555;
`;
// Use shared collapsible panel (Unicode carets explicitly enabled per user override)
const section = createCollapsiblePanel({ titleText: 'AI Generate Description', defaultExpanded: true, useUnicodeCarets: true });
// NOTE: Using Unicode caret glyphs (▾/▸) here by explicit override of the ASCII-only rule.
// Some search tools may not work properly around these characters; edit with care.
const content = section.contentEl;
// Preset selection row
const presetRow = document.createElement('div');
presetRow.style.cssText = `
display: flex;
gap: 10px;
align-items: center;
`;
const presetLabel = document.createElement('label');
presetLabel.textContent = 'Preset:';
presetLabel.style.cssText = `
color: #fff;
font-size: 12px;
min-width: 50px;
`;
const presetSelect = createSelect({
className: 'llm-preset-select-dataset',
items: [{ value: '', text: 'Loading presets...' }],
style: { flex: '1', background: '#555', border: '1px solid #666', padding: '6px' }
});
// Load presets
(async () => {
try {
const presets = await getPresets();
presetSelect.innerHTML = '';
const placeholderOption = document.createElement('option');
placeholderOption.value = '';
placeholderOption.textContent = 'Select a preset...';
presetSelect.appendChild(placeholderOption);
for (const [id, preset] of Object.entries(presets)) {
if (preset.model) {
const option = document.createElement('option');
option.value = id;
option.textContent = `${preset.name} (${preset.model})`;
presetSelect.appendChild(option);
}
}
} catch (error) {
console.error('Error loading presets:', error);
presetSelect.innerHTML = '<option value="">Error loading presets</option>';
}
})();
presetRow.appendChild(presetLabel);
presetRow.appendChild(presetSelect);
// Mode selection (Append/Overwrite)
const modeRow = document.createElement('div');
modeRow.style.cssText = `
display: flex;
gap: 15px;
align-items: center;
`;
const modeLabel = document.createElement('label');
modeLabel.textContent = 'Mode:';
modeLabel.style.cssText = `
color: #fff;
font-size: 12px;
min-width: 50px;
`;
const { container: radioGroup, radios } = createRadioGroup('llm-mode', [
{ value: 'append', label: 'Append', checked: true },
{ value: 'overwrite', label: 'Overwrite' }
], {
layout: 'horizontal'
});
const appendRadio = radios[0].radio;
modeRow.appendChild(modeLabel);
modeRow.appendChild(radioGroup);
// Buttons row
const llmButtonsRow = document.createElement('div');
llmButtonsRow.style.cssText = `
display: flex;
gap: 8px;
`;
const generateCurrentBtn = createButton('Generate for Current Image', {
variant: BUTTON_VARIANTS.PRIMARY,
size: 'medium',
style: { flex: '1', marginTop: '0' }
});
const generateAllBtn = createButton('Generate for All Images', {
variant: BUTTON_VARIANTS.WARNING,
size: 'medium',
style: { flex: '1', marginTop: '0' }
});
llmButtonsRow.appendChild(generateCurrentBtn);
llmButtonsRow.appendChild(generateAllBtn);
llmPanel.appendChild(section.container);
content.appendChild(presetRow);
content.appendChild(modeRow);
content.appendChild(llmButtonsRow);
// Event handlers
generateCurrentBtn.addEventListener('click', async () => {
const presetId = presetSelect.value;
if (!presetId) {
notifications.warning('Please select a preset first.');
return;
}
const isAppend = appendRadio.checked;
generateCurrentBtn.disabled = true;
generateCurrentBtn.textContent = 'Generating...';
try {
if (onGenerateCurrent) {
await onGenerateCurrent(presetId, isAppend);
}
notifications.success('Description generated successfully!');
} catch (error) {
notifications.error(`Generation failed: ${error.message}`);
} finally {
generateCurrentBtn.disabled = false;
generateCurrentBtn.textContent = 'Generate for Current Image';
}
});
generateAllBtn.addEventListener('click', async () => {
const presetId = presetSelect.value;
if (!presetId) {
notifications.warning('Please select a preset first.');
return;
}
const isAppend = appendRadio.checked;
const message = batchCount !== null
? `Generate descriptions for ${batchCount} images using the selected preset? This may take a while.`
: 'Generate descriptions for all images using the selected preset? This may take a while.';
const confirmed = await confirmDialog(message, 'Confirm Batch Generation');
if (!confirmed) return;
if (onGenerateAll) {
await onGenerateAll(presetId, isAppend);
}
});
// Toggle behavior handled by shared component
return llmPanel;
}