window.ChatbotPresetQuestionsUI = {
/**
* 渲染预设问题区域。
* @param {HTMLElement} parentElement - presetContainer 将被添加到的父元素 (通常是 chatbotWindow 或 mainContentArea)。
* @param {boolean} isCustomModel - 当前是否为自定义模型。
* @param {string} currentDocId - 当前文档ID,用于自动收起逻辑。
* @param {function} updateChatbotUICallback - 用于在按钮点击时触发主UI更新的回调。
* @param {function} handlePresetQuestionCallback - 处理预设问题点击的回调。
* @returns {HTMLElement} 创建的 presetContainer 元素,或在失败时返回 null。
*/
render: function(parentElement, isCustomModel, currentDocId, updateChatbotUICallback, handlePresetQuestionCallback) {
// 确保清除旧的 presetContainer (如果存在)
let existingPresetContainer = document.getElementById('chatbot-preset-container');
if (existingPresetContainer) existingPresetContainer.remove();
const presetContainer = document.createElement('div');
presetContainer.id = 'chatbot-preset-container';
presetContainer.style.position = 'absolute';
presetContainer.style.top = '58px'; // 稍微增加顶部距离,避免过于拥挤 (原53px)
presetContainer.style.left = '0px';
presetContainer.style.right = '0px';
presetContainer.style.zIndex = '5'; // 确保在聊天内容之上但在模态框之内
presetContainer.style.padding = '6px 24px'; // 恢复适度的内边距,增加呼吸感 (原4px 20px)
const newPresetHeader = document.createElement('div');
newPresetHeader.id = 'chatbot-preset-header';
newPresetHeader.style.display = 'flex';
newPresetHeader.style.alignItems = 'center';
newPresetHeader.style.justifyContent = 'space-between';
newPresetHeader.style.marginBottom = '6px'; // 稍微增加底部间距 (原4px)
newPresetHeader.style.padding = '0';
const presetTitle = document.createElement('span');
presetTitle.textContent = '快捷指令';
presetTitle.style.fontWeight = '600';
presetTitle.style.fontSize = '0.9em';
presetTitle.style.color = '#4b5563'; // 深灰色标题
const presetToggleBtn = document.createElement('button');
presetToggleBtn.id = 'chatbot-preset-toggle-btn';
presetToggleBtn.style.background = 'none';
presetToggleBtn.style.border = 'none';
presetToggleBtn.style.cursor = 'pointer';
presetToggleBtn.style.padding = '4px';
presetToggleBtn.style.color = '#4b5563';
presetToggleBtn.innerHTML = window.isPresetQuestionsCollapsed
? '' // 向下箭头表示"显示"
: ''; // 向上箭头表示"隐藏"
presetToggleBtn.title = window.isPresetQuestionsCollapsed ? "展开快捷指令" : "收起快捷指令";
presetToggleBtn.onclick = function() {
window.isPresetQuestionsCollapsed = !window.isPresetQuestionsCollapsed;
updateChatbotUICallback(); // 调用主UI更新
};
const headerLeftGroup = document.createElement('div');
headerLeftGroup.style.display = 'flex';
headerLeftGroup.style.alignItems = 'center';
headerLeftGroup.style.gap = '8px';
headerLeftGroup.appendChild(presetTitle);
headerLeftGroup.appendChild(presetToggleBtn);
newPresetHeader.appendChild(headerLeftGroup);
// 齿轮按钮(模型配置)- 始终显示
const gearBtn = document.createElement('button');
gearBtn.id = 'chatbot-model-gear-btn';
gearBtn.title = '模型配置';
gearBtn.innerHTML = '';
gearBtn.style.display = 'flex';
gearBtn.style.alignItems = 'center';
gearBtn.style.justifyContent = 'center';
gearBtn.style.background = 'none';
gearBtn.style.border = 'none';
gearBtn.style.cursor = 'pointer';
gearBtn.style.padding = '2.5px';
gearBtn.style.borderRadius = '50%';
gearBtn.style.transition = 'background 0.16s, box-shadow 0.16s';
gearBtn.onmouseover = function(){
this.style.background = '#e0f2fe';
this.style.boxShadow = '0 1.5px 6px 0 rgba(59,130,246,0.10)';
};
gearBtn.onmouseout = function(){
this.style.background = 'none';
this.style.boxShadow = 'none';
};
gearBtn.onclick = function(){
// 打开独立的chatbot模型配置弹窗
if (typeof window !== 'undefined' && window.ChatbotModelConfigModal) {
window.ChatbotModelConfigModal.open();
} else {
console.warn('[Chatbot Gear] ChatbotModelConfigModal 不可用,回退到旧版选择器');
window.isModelSelectorOpen = true; // 设置全局状态
updateChatbotUICallback(); // 调用主UI更新
}
};
newPresetHeader.appendChild(gearBtn);
presetContainer.appendChild(newPresetHeader);
const newPresetBody = document.createElement('div');
newPresetBody.id = 'chatbot-preset-body';
newPresetBody.style.display = 'flex';
newPresetBody.style.flexWrap = 'wrap';
newPresetBody.style.gap = '6px 8px';
newPresetBody.style.transition = 'opacity 0.3s ease-out, max-height 0.4s ease-out, margin-bottom 0.4s ease-out, visibility 0.3s ease-out';
newPresetBody.style.overflow = 'hidden';
newPresetBody.style.width = '100%'; // 确保它占据其父容器的全部宽度
presetContainer.appendChild(newPresetBody);
if (!parentElement) {
console.error("ChatbotPresetQuestionsUI: Parent element for presetContainer is not provided or not found.");
return null;
}
// 将 presetContainer 添加到指定的父元素
parentElement.appendChild(presetContainer);
// 填充预设问题按钮
const presetQuestions = (window.ChatbotPreset && window.ChatbotPreset.PRESET_QUESTIONS) ? window.ChatbotPreset.PRESET_QUESTIONS : [
'总结本文', '有哪些关键公式?', '研究背景与意义?', '研究方法及发现?',
'应用与前景?', '用通俗语言解释全文', '生成思维导图🧠', '生成流程图🔄',
'生成更多配图🎨'
];
presetQuestions.forEach(q => {
const button = document.createElement('button');
button.className = 'preset-btn';
// 使用 encodeURIComponent/decodeURIComponent 来处理特殊字符
button.onclick = function() {
const text = decodeURIComponent(encodeURIComponent(q));
// 对“生成更多配图”做特殊处理:只帮用户打出前缀 [加入配图],不直接发送复杂提示
if (text.startsWith('生成更多配图')) {
const input = document.getElementById('chatbot-input');
if (input) {
input.value = '[加入配图] ';
input.focus();
}
return;
}
handlePresetQuestionCallback(text);
};
button.textContent = q;
newPresetBody.appendChild(button);
});
// 自动收起逻辑 (依赖全局状态 window.isPresetQuestionsCollapsed, window.presetAutoCollapseTriggeredForDoc, window.isModelSelectorOpen 和 ChatbotCore)
let userMessageCount = 0;
if (window.ChatbotCore && window.ChatbotCore.chatHistory) {
userMessageCount = window.ChatbotCore.chatHistory.filter(m => m.role === 'user').length;
}
if (userMessageCount >= 3 &&
currentDocId && window.presetAutoCollapseTriggeredForDoc && !window.presetAutoCollapseTriggeredForDoc[currentDocId] &&
!window.isPresetQuestionsCollapsed &&
!window.isModelSelectorOpen) {
window.isPresetQuestionsCollapsed = true;
window.presetAutoCollapseTriggeredForDoc[currentDocId] = true;
// 按钮的图标和标题将在下一次 updateChatbotUICallback 调用时由 presetToggleBtn 自身逻辑更新
// 但为了即时性,如果状态因此改变,可以手动更新一下按钮
presetToggleBtn.innerHTML = '';
presetToggleBtn.title = "展开快捷指令";
}
// 控制 chatbot-preset-body 和 presetContainer 的显隐与动画
// (依赖全局状态 window.isPresetQuestionsCollapsed, window.isModelSelectorOpen)
if (window.isPresetQuestionsCollapsed || window.isModelSelectorOpen) {
newPresetBody.style.opacity = '0';
newPresetBody.style.maxHeight = '0';
newPresetBody.style.marginBottom = '0';
newPresetBody.style.visibility = 'hidden';
presetContainer.style.boxShadow = 'none';
presetContainer.style.background = 'transparent';
presetContainer.style.paddingTop = '0px'; // 折叠时移除垂直内边距
presetContainer.style.paddingBottom = '0px';
} else {
newPresetBody.style.opacity = '1';
newPresetBody.style.maxHeight = '150px'; // 或者一个更合适的计算值
newPresetBody.style.marginBottom = '0px'; // 或者根据需要调整
newPresetBody.style.visibility = 'visible';
presetContainer.style.boxShadow = 'none'; // 可以设置展开时的阴影
presetContainer.style.paddingTop = '6px'; // 恢复垂直内边距
presetContainer.style.paddingBottom = '6px';
// 背景渐变逻辑 (依赖父窗口背景色)
const chatWindowBgElement = document.querySelector('#chatbot-modal .chatbot-window');
let chatWinBg = 'rgb(255,255,255)'; // 默认背景
if (chatWindowBgElement) {
chatWinBg = getComputedStyle(chatWindowBgElement).getPropertyValue('background-color') || 'rgb(255,255,255)';
}
let opaqueBg = chatWinBg;
if (opaqueBg.startsWith('rgba')) { // 转换为不透明的rgb
const parts = opaqueBg.match(/[\d.]+/g);
if (parts && parts.length === 4) { // rgba(r,g,b,a)
opaqueBg = `rgb(${parts[0]}, ${parts[1]}, ${parts[2]})`;
} else if (parts && parts.length === 3) { // 可能已经是 rgb 字符串
opaqueBg = `rgb(${parts[0]}, ${parts[1]}, ${parts[2]})`;
}
} else if (opaqueBg === 'transparent') {
opaqueBg = 'rgb(255,255,255)'; // 透明时的回退
}
presetContainer.style.background = `linear-gradient(to bottom, ${opaqueBg} 0%, ${opaqueBg} 70%, transparent 100%)`;
}
return presetContainer; // 返回创建的容器,主UI函数可以用它来计算布局
}
};