Major refactoring to improve code maintainability: - Reduced main widget file from 1677→900 lines (46% reduction) - Extracted UI builders into separate modules: * widget-ui-builder.js (237 lines) - History, Character, Language, Validation sections * widget-parameter-section.js (237 lines) - Parameter controls with dynamic inputs * widget-preset-system.js (102 lines) - Preset management - All original functionality preserved with identical behavior - Parameter tag insertion now working with full event handler - Note: Language selector currently only displays, doesn't auto-add to tags (design limitation) Modules are properly organized with clear separation of concerns while maintaining all original logic and event handling.
238 lines
9.6 KiB
JavaScript
238 lines
9.6 KiB
JavaScript
/**
|
|
* 🏷️ Widget Parameter Section
|
|
* Builds the parameter control section with dynamic input types
|
|
* Extracted for modularity - preserves 100% of original logic
|
|
*/
|
|
|
|
import { TagUtilities } from "./tag-utilities.js";
|
|
|
|
export function buildParameterSection(state, storageKey, getPlainText, setEditorText, getCaretPos, setCaretPos, widget, historyStatus) {
|
|
const paramSection = document.createElement("div");
|
|
paramSection.style.marginBottom = "8px";
|
|
paramSection.style.paddingBottom = "8px";
|
|
paramSection.style.borderBottom = "1px solid #444";
|
|
|
|
const paramLabel = document.createElement("div");
|
|
paramLabel.textContent = "Add Parameter";
|
|
paramLabel.style.fontWeight = "bold";
|
|
paramLabel.style.marginBottom = "5px";
|
|
paramLabel.style.fontSize = "11px";
|
|
|
|
const paramTypeSelect = document.createElement("select");
|
|
paramTypeSelect.style.width = "100%";
|
|
paramTypeSelect.style.marginBottom = "5px";
|
|
paramTypeSelect.style.padding = "3px";
|
|
paramTypeSelect.style.fontSize = "10px";
|
|
paramTypeSelect.style.background = "#2a2a2a";
|
|
paramTypeSelect.style.color = "#eee";
|
|
paramTypeSelect.style.border = "1px solid #444";
|
|
paramTypeSelect.innerHTML = `
|
|
<option value="">Select parameter...</option>
|
|
<optgroup label="Universal">
|
|
<option value="seed">Seed</option>
|
|
<option value="temperature">Temperature</option>
|
|
</optgroup>
|
|
<optgroup label="ChatterBox">
|
|
<option value="cfg">CFG Weight</option>
|
|
<option value="exaggeration">Exaggeration</option>
|
|
</optgroup>
|
|
<optgroup label="F5-TTS / Higgs">
|
|
<option value="speed">Speed</option>
|
|
</optgroup>
|
|
<optgroup label="Higgs / VibeVoice / IndexTTS">
|
|
<option value="top_p">Top P</option>
|
|
<option value="top_k">Top K</option>
|
|
</optgroup>
|
|
<optgroup label="VibeVoice / IndexTTS">
|
|
<option value="steps">Inference Steps</option>
|
|
</optgroup>
|
|
<optgroup label="IndexTTS">
|
|
<option value="emotion_alpha">Emotion Alpha</option>
|
|
</optgroup>
|
|
`;
|
|
|
|
const paramInputWrapper = document.createElement("div");
|
|
paramInputWrapper.style.marginBottom = "5px";
|
|
|
|
const paramConfig = {
|
|
seed: { type: "number", min: 0, max: 4294967295, step: 1, default: 0 },
|
|
temperature: { type: "slider", min: 0.1, max: 2.0, step: 0.1, default: 0.7, label: "Temp" },
|
|
cfg: { type: "slider", min: 0.0, max: 20.0, step: 0.1, default: 7.0, label: "CFG" },
|
|
speed: { type: "slider", min: 0.5, max: 2.0, step: 0.1, default: 1.0, label: "Speed" },
|
|
exaggeration: { type: "slider", min: 0.0, max: 2.0, step: 0.1, default: 1.0, label: "Exag" },
|
|
top_p: { type: "slider", min: 0.0, max: 1.0, step: 0.01, default: 0.95, label: "Top P" },
|
|
top_k: { type: "number", min: 1, max: 100, step: 1, default: 50 },
|
|
steps: { type: "number", min: 1, max: 100, step: 1, default: 30 },
|
|
emotion_alpha: { type: "slider", min: 0.0, max: 1.0, step: 0.05, default: 0.5, label: "Emotion" }
|
|
};
|
|
|
|
const createParamInput = (type) => {
|
|
const wrapper = document.createElement("div");
|
|
const config = paramConfig[type] || { type: "text" };
|
|
|
|
if (config.type === "number") {
|
|
const input = document.createElement("input");
|
|
input.type = "number";
|
|
input.min = config.min;
|
|
input.max = config.max;
|
|
input.step = config.step;
|
|
input.placeholder = config.default.toString();
|
|
input.value = config.default;
|
|
input.style.width = "100%";
|
|
input.style.padding = "3px";
|
|
input.style.fontSize = "10px";
|
|
input.style.background = "#2a2a2a";
|
|
input.style.color = "#eee";
|
|
input.style.border = "1px solid #444";
|
|
input.addEventListener("change", () => {
|
|
state[`last${type.charAt(0).toUpperCase() + type.slice(1)}`] = input.value;
|
|
state.saveToLocalStorage(storageKey);
|
|
});
|
|
wrapper.appendChild(input);
|
|
wrapper.getValue = () => input.value;
|
|
return wrapper;
|
|
} else if (config.type === "slider") {
|
|
const label = document.createElement("div");
|
|
label.style.fontSize = "9px";
|
|
label.style.marginBottom = "2px";
|
|
label.style.color = "#999";
|
|
label.textContent = `${config.label}: ${config.default}`;
|
|
|
|
const slider = document.createElement("input");
|
|
slider.type = "range";
|
|
slider.min = config.min;
|
|
slider.max = config.max;
|
|
slider.step = config.step;
|
|
slider.value = config.default;
|
|
slider.style.width = "100%";
|
|
slider.addEventListener("input", () => {
|
|
label.textContent = `${config.label}: ${slider.value}`;
|
|
state[`last${type.charAt(0).toUpperCase() + type.slice(1)}`] = slider.value;
|
|
state.saveToLocalStorage(storageKey);
|
|
});
|
|
|
|
wrapper.appendChild(label);
|
|
wrapper.appendChild(slider);
|
|
wrapper.getValue = () => slider.value;
|
|
return wrapper;
|
|
} else {
|
|
const input = document.createElement("input");
|
|
input.type = "text";
|
|
input.placeholder = `${type} value`;
|
|
input.style.width = "100%";
|
|
input.style.padding = "3px";
|
|
input.style.fontSize = "10px";
|
|
input.style.background = "#2a2a2a";
|
|
input.style.color = "#eee";
|
|
input.style.border = "1px solid #444";
|
|
wrapper.appendChild(input);
|
|
wrapper.getValue = () => input.value;
|
|
return wrapper;
|
|
}
|
|
};
|
|
|
|
let currentParamInput = null;
|
|
|
|
paramTypeSelect.addEventListener("change", () => {
|
|
paramInputWrapper.innerHTML = "";
|
|
if (paramTypeSelect.value) {
|
|
currentParamInput = createParamInput(paramTypeSelect.value);
|
|
paramInputWrapper.appendChild(currentParamInput);
|
|
state.lastParameterType = paramTypeSelect.value;
|
|
state.saveToLocalStorage(storageKey);
|
|
} else {
|
|
currentParamInput = null;
|
|
}
|
|
});
|
|
|
|
if (state.lastParameterType) {
|
|
paramTypeSelect.value = state.lastParameterType;
|
|
const changeEvent = new Event("change");
|
|
paramTypeSelect.dispatchEvent(changeEvent);
|
|
}
|
|
|
|
const addParamBtn = document.createElement("button");
|
|
addParamBtn.textContent = "Add to Tag";
|
|
addParamBtn.title = "Add selected parameter to tag at cursor or create new parameter tag";
|
|
addParamBtn.style.width = "100%";
|
|
addParamBtn.style.padding = "4px";
|
|
addParamBtn.style.cursor = "pointer";
|
|
addParamBtn.style.fontSize = "10px";
|
|
addParamBtn.style.background = "#3a3a3a";
|
|
addParamBtn.style.color = "#eee";
|
|
addParamBtn.style.border = "1px solid #555";
|
|
addParamBtn.style.borderRadius = "2px";
|
|
|
|
// Add parameter button click handler
|
|
addParamBtn.addEventListener("click", () => {
|
|
if (!paramTypeSelect.value || !currentParamInput) {
|
|
return;
|
|
}
|
|
|
|
const paramValue = currentParamInput.getValue();
|
|
if (!paramValue) {
|
|
return;
|
|
}
|
|
|
|
const paramStr = `${paramTypeSelect.value}:${paramValue}`;
|
|
const text = getPlainText();
|
|
const caretPos = getCaretPos();
|
|
|
|
// Try to modify existing tag
|
|
const result = TagUtilities.modifyTagContent(text, caretPos, (tagContent) => {
|
|
const paramType = paramTypeSelect.value;
|
|
const paramRegex = new RegExp(`${paramType}:[^|\\]]+`);
|
|
|
|
if (paramRegex.test(tagContent)) {
|
|
// Replace existing parameter
|
|
return tagContent.replace(paramRegex, paramStr);
|
|
} else {
|
|
// Add new parameter
|
|
return `${tagContent}|${paramStr}`;
|
|
}
|
|
});
|
|
|
|
if (result) {
|
|
// Modified existing tag
|
|
setEditorText(result.newText);
|
|
setTimeout(() => {
|
|
setCaretPos(result.newCaretPos);
|
|
state.addToHistory(result.newText, result.newCaretPos);
|
|
state.saveToLocalStorage(storageKey);
|
|
}, 0);
|
|
} else {
|
|
// Create new parameter tag at caret position
|
|
const paramTag = `[${paramStr}]`;
|
|
const newText = text.substring(0, caretPos) + paramTag + " " + text.substring(caretPos);
|
|
// Position caret right after ] so next parameter add detects this tag
|
|
const newCaretPos = caretPos + paramTag.length;
|
|
|
|
setEditorText(newText);
|
|
setTimeout(() => {
|
|
setCaretPos(newCaretPos);
|
|
state.addToHistory(newText, newCaretPos);
|
|
state.saveToLocalStorage(storageKey);
|
|
}, 0);
|
|
}
|
|
widget.callback?.(widget.value);
|
|
if (historyStatus) {
|
|
historyStatus.textContent = state.getHistoryStatus();
|
|
}
|
|
});
|
|
|
|
paramSection.appendChild(paramLabel);
|
|
paramSection.appendChild(paramTypeSelect);
|
|
paramSection.appendChild(paramInputWrapper);
|
|
paramSection.appendChild(addParamBtn);
|
|
|
|
return {
|
|
paramSection,
|
|
paramTypeSelect,
|
|
paramInputWrapper,
|
|
addParamBtn,
|
|
createParamInput,
|
|
getCurrentParamInput: () => currentParamInput,
|
|
setCurrentParamInput: (input) => { currentParamInput = input; }
|
|
};
|
|
}
|