paper-burner/js/ui/ui-key-manager-modal.js

363 lines
16 KiB
JavaScript
Raw 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.

// js/ui/ui-key-manager-modal.js
// 管理翻译模型自定义源站点与模型 Key 弹窗的交互逻辑。
(function(global) {
'use strict';
// 表单元素创建工具 (NEW - for renderSourceSiteForm)
// ---------------------
/**
* 创建并返回一个包含标签label和输入框input的完整配置项组件。
* 该组件通常用于动态生成的表单中,用于收集用户输入。
*
* @param {string} id - 输入框元素的 HTML `id` 属性,同时用于标签的 `for` 属性。
* @param {string} labelText - 显示在输入框上方的标签文本内容。
* @param {string|number} value - 输入框的初始值。
* @param {string} [type='text'] - 输入框的类型 (例如:`text`, `url`, `number`, `password`)。
* @param {string} [placeholder=''] - 输入框的占位提示文本。
* @param {function} [onChangeCallback] - (可选) 当输入框的值发生改变 (通常是 `change` 或 `input` 事件) 时被调用的回调函数。
* @param {Object} [attributes={}] - (可选) 一个包含额外 HTML 属性的对象,这些属性将被直接设置到输入框元素上。
* 对于 `type='number'`,可以包含 `min`, `max`, `step`。
* @returns {HTMLElement} 返回一个 `div` 元素,该元素包装了创建的标签和输入框。
*/
function createConfigInput(id, labelText, value, type = 'text', placeholder = '', onChangeCallback, attributes = {}) {
const wrapper = document.createElement('div');
wrapper.className = 'mb-3'; // Add some margin for spacing
const label = document.createElement('label');
label.htmlFor = id;
label.className = 'block text-xs font-medium text-gray-600 mb-1';
label.textContent = labelText;
const input = document.createElement('input');
input.type = type;
input.id = id;
input.name = id;
input.value = value;
input.placeholder = placeholder;
input.className = 'w-full px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors';
if (type === 'number') {
if (attributes.min !== undefined) input.min = attributes.min;
if (attributes.max !== undefined) input.max = attributes.max;
if (attributes.step !== undefined) input.step = attributes.step;
}
for (const key in attributes) {
if (key !== 'min' && key !== 'max' && key !== 'step') { // Avoid re-setting handled attributes
input.setAttribute(key, attributes[key]);
}
}
if (onChangeCallback && typeof onChangeCallback === 'function') {
input.addEventListener('change', onChangeCallback);
input.addEventListener('input', onChangeCallback); // For more responsive updates if needed
}
wrapper.appendChild(label);
wrapper.appendChild(input);
return wrapper;
}
/**
* 创建并返回一个包含标签label和下拉选择框select的完整配置项组件。
* 该组件用于提供一组预定义的选项供用户选择。
*
* @param {string} id - 下拉选择框元素的 HTML `id` 属性,同时用于标签的 `for` 属性。
* @param {string} labelText - 显示在下拉选择框上方的标签文本内容。
* @param {string} selectedValue - 需要被预选中的选项的值。
* @param {Array<Object>} optionsArray - 一个对象数组,用于生成下拉选项。每个对象应包含:
* @param {string} optionsArray[].value - 选项的实际值。
* @param {string} optionsArray[].text - 选项的显示文本。
* @param {function} [onChangeCallback] - (可选) 当下拉选择框的值发生改变时被调用的回调函数。
* @returns {HTMLElement} 返回一个 `div` 元素,该元素包装了创建的标签和下拉选择框。
*/
function createConfigSelect(id, labelText, selectedValue, optionsArray, onChangeCallback) {
const wrapper = document.createElement('div');
wrapper.className = 'mb-3';
const label = document.createElement('label');
label.htmlFor = id;
label.className = 'block text-xs font-medium text-gray-600 mb-1';
label.textContent = labelText;
const select = document.createElement('select');
select.id = id;
select.name = id;
select.className = 'w-full px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors';
optionsArray.forEach(opt => {
const optionElement = document.createElement('option');
optionElement.value = opt.value;
optionElement.textContent = opt.text;
if (opt.value === selectedValue) {
optionElement.selected = true;
}
select.appendChild(optionElement);
});
if (onChangeCallback && typeof onChangeCallback === 'function') {
select.addEventListener('change', onChangeCallback);
}
wrapper.appendChild(label);
wrapper.appendChild(select);
return wrapper;
}
global.createConfigInput = createConfigInput;
global.createConfigSelect = createConfigSelect;
// ---------------------
// 自定义源站点下拉列表填充
// ---------------------
function populateCustomSourceSitesDropdown_ui(selectedSiteIdToSet = null) {
const dropdown = global.document.getElementById('customSourceSiteSelect');
if (!dropdown) {
console.warn('populateCustomSourceSitesDropdown_ui: customSourceSiteSelect dropdown not found.');
return;
}
dropdown.innerHTML = '';
dropdown.classList.remove('hidden');
dropdown.disabled = false;
let sites = {};
if (typeof global.loadAllCustomSourceSites === 'function') {
sites = global.loadAllCustomSourceSites();
} else {
console.error('populateCustomSourceSitesDropdown_ui: loadAllCustomSourceSites function is not available.');
const errorOption = document.createElement('option');
errorOption.value = '';
errorOption.textContent = '错误:无法加载源站点';
dropdown.appendChild(errorOption);
dropdown.disabled = true;
return;
}
const siteIds = Object.keys(sites);
if (!selectedSiteIdToSet) {
const settings = typeof global.loadSettings === 'function' ? global.loadSettings() : {};
selectedSiteIdToSet = settings.selectedCustomSourceSiteId || null;
}
if (siteIds.length === 0) {
const noSitesOption = document.createElement('option');
noSitesOption.value = '';
noSitesOption.textContent = '无自定义源站点';
dropdown.appendChild(noSitesOption);
dropdown.disabled = true;
} else {
dropdown.disabled = false;
const placeholderOption = document.createElement('option');
placeholderOption.value = '';
placeholderOption.textContent = '-- 请选择源站点 --';
dropdown.appendChild(placeholderOption);
siteIds.forEach(id => {
const site = sites[id];
const option = document.createElement('option');
option.value = id;
option.textContent = site.displayName || `源站 (ID: ${id.substring(0, 8)}...)`;
dropdown.appendChild(option);
});
if (selectedSiteIdToSet && sites[selectedSiteIdToSet]) {
dropdown.value = selectedSiteIdToSet;
} else {
dropdown.value = '';
}
}
refreshCustomSourceSiteInfo({ autoSelect: !dropdown.value });
}
global.populateCustomSourceSitesDropdown_ui = populateCustomSourceSitesDropdown_ui;
const EMPTY_SOURCE_SITE_HINT = `<div class="p-4 text-center text-red-500 text-sm font-semibold">您还没设置自定义源站点,请您先手动进行设置...<br>如果您已进行设置,请尝试刷新</div>`;
function refreshCustomSourceSiteInfo({ autoSelect = false, persistSelection = true } = {}) {
const select = global.document.getElementById('customSourceSiteSelect');
if (!select) return;
const infoContainer = global.document.getElementById('customSourceSiteInfo');
const manageKeyBtn = global.document.getElementById('manageSourceSiteKeyBtn');
const availableOptions = Array.from(select.options || []).filter(opt => opt.value);
if (availableOptions.length === 0) {
if (infoContainer) {
infoContainer.classList.remove('hidden');
infoContainer.innerHTML = EMPTY_SOURCE_SITE_HINT;
}
if (manageKeyBtn) manageKeyBtn.classList.add('hidden');
return;
}
let selectionChanged = false;
if (!select.value || !availableOptions.some(opt => opt.value === select.value)) {
if (autoSelect) {
select.value = availableOptions[0].value;
selectionChanged = true;
} else {
if (infoContainer) {
infoContainer.classList.remove('hidden');
infoContainer.innerHTML = EMPTY_SOURCE_SITE_HINT;
}
if (manageKeyBtn) manageKeyBtn.classList.add('hidden');
return;
}
}
if (selectionChanged && persistSelection) {
const settings = typeof global.loadSettings === 'function' ? (global.loadSettings() || {}) : {};
settings.selectedCustomSourceSiteId = select.value;
if (typeof global.saveSettings === 'function') {
global.saveSettings(settings);
} else {
(global.localStorage || localStorage).setItem('paperBurnerSettings', JSON.stringify(settings));
}
}
if (typeof global.updateCustomSourceSiteInfo === 'function') {
global.updateCustomSourceSiteInfo(select.value);
}
}
function updateTranslationUIVisibility(isProcessing) {
const translationSelect = global.document.getElementById('translationModel');
const translationModelValue = translationSelect ? translationSelect.value : '';
const sourceSiteContainer = global.document.getElementById('customSourceSiteContainer');
const sourceSiteSelect = global.document.getElementById('customSourceSiteSelect');
if (translationModelValue === 'custom') {
const modelSelector = document.getElementById('customModelId');
const modelInput = document.getElementById('customModelIdInput');
if (modelSelector && modelInput) {
const hasAvailableModels = modelSelector.options.length > 1;
if (hasAvailableModels) {
if (modelSelector.value === 'manual-input') {
modelInput.style.display = 'block';
} else {
modelInput.style.display = 'none';
}
} else {
modelSelector.style.display = 'none';
modelInput.style.display = 'block';
}
}
}
if (sourceSiteContainer && sourceSiteSelect) {
const detailsWrapper = global.document.getElementById('customSourceSite');
const toggleIconEl = global.document.getElementById('customSourceSiteToggleIcon');
if (translationModelValue === 'custom') {
sourceSiteContainer.classList.remove('hidden');
sourceSiteSelect.disabled = false;
sourceSiteSelect.classList.remove('hidden');
if (detailsWrapper && detailsWrapper.classList.contains('hidden')) {
detailsWrapper.classList.remove('hidden');
}
if (toggleIconEl) {
toggleIconEl.setAttribute('icon', 'carbon:chevron-up');
}
if (typeof global.populateCustomSourceSitesDropdown_ui === 'function') {
global.populateCustomSourceSitesDropdown_ui();
} else {
console.warn('populateCustomSourceSitesDropdown_ui function not found on window.');
sourceSiteSelect.innerHTML = '<option value="">加载函数错误</option>';
}
refreshCustomSourceSiteInfo({ autoSelect: !sourceSiteSelect.value });
setTimeout(() => {
const selectEl = global.document.getElementById('customSourceSiteSelect');
if (!selectEl) return;
const hasUsableOption = Array.from(selectEl.options || []).some(opt => opt.value);
if (!hasUsableOption) {
const sites = typeof global.loadAllCustomSourceSites === 'function' ? global.loadAllCustomSourceSites() : {};
const storedSettings = typeof global.loadSettings === 'function' ? global.loadSettings() : {};
if (sites && Object.keys(sites).length > 0) {
global.populateCustomSourceSitesDropdown_ui(storedSettings ? storedSettings.selectedCustomSourceSiteId || null : null);
}
}
refreshCustomSourceSiteInfo({ autoSelect: !selectEl.value });
}, 250);
} else {
sourceSiteContainer.classList.add('hidden');
sourceSiteSelect.innerHTML = '';
sourceSiteSelect.disabled = true;
sourceSiteSelect.classList.add('hidden');
const infoContainer = document.getElementById('customSourceSiteInfo');
const manageKeyBtn = document.getElementById('manageSourceSiteKeyBtn');
if (infoContainer) infoContainer.classList.add('hidden');
if (manageKeyBtn) manageKeyBtn.classList.add('hidden');
if (detailsWrapper && !detailsWrapper.classList.contains('hidden')) {
detailsWrapper.classList.add('hidden');
}
if (toggleIconEl) {
toggleIconEl.setAttribute('icon', 'carbon:chevron-down');
}
}
}
const glossarySection = document.getElementById('glossaryManagerSection');
if (glossarySection) {
if (translationModelValue === 'none') {
glossarySection.classList.add('hidden');
} else {
glossarySection.classList.remove('hidden');
}
}
const togglePanel = (elementId, shouldShow, renderer) => {
const el = document.getElementById(elementId);
if (!el) return;
if (shouldShow) {
el.classList.remove('hidden');
if (renderer) {
try { renderer(); } catch (e) { console.error(e); }
}
} else {
el.classList.add('hidden');
}
};
togglePanel('geminiModelInfo', translationModelValue === 'gemini', global.renderGeminiInfoPanel);
togglePanel('deepseekModelInfo', translationModelValue === 'deepseek', global.renderDeepseekInfoPanel);
togglePanel('tongyiModelInfo', translationModelValue === 'tongyi', global.renderTongyiInfoPanel);
togglePanel('volcanoModelInfo', translationModelValue === 'volcano', global.renderVolcanoInfoPanel);
togglePanel('deeplxModelInfo', translationModelValue === 'deeplx', global.renderDeeplxInfoPanel);
}
global.updateTranslationUIVisibility = updateTranslationUIVisibility;
global.ui = global.ui || {};
global.ui.createConfigInput = createConfigInput;
global.ui.createConfigSelect = createConfigSelect;
global.ui.populateCustomSourceSitesDropdown_ui = populateCustomSourceSitesDropdown_ui;
global.ui.updateTranslationUIVisibility = updateTranslationUIVisibility;
global.refreshCustomSourceSiteInfo = refreshCustomSourceSiteInfo;
global.ui.refreshCustomSourceSiteInfo = refreshCustomSourceSiteInfo;
function tryPopulateCustomSourceSitesDropdown() {
const settings = typeof global.loadSettings === 'function' ? global.loadSettings() : {};
const presetId = settings ? settings.selectedCustomSourceSiteId || null : null;
if (typeof global.populateCustomSourceSitesDropdown_ui === 'function') {
global.populateCustomSourceSitesDropdown_ui(presetId);
}
refreshCustomSourceSiteInfo({ autoSelect: true });
setTimeout(() => {
const select = global.document.getElementById('customSourceSiteSelect');
if (!select) return;
const hasOption = Array.from(select.options || []).some(opt => opt.value);
if (hasOption) {
refreshCustomSourceSiteInfo({ autoSelect: true });
}
}, 200);
}
global.tryPopulateCustomSourceSitesDropdown = tryPopulateCustomSourceSitesDropdown;
global.ui.tryPopulateCustomSourceSitesDropdown = tryPopulateCustomSourceSitesDropdown;
})(window);