paper-burner/js/chatbot/ui/chatbot-preset-questions-ui.js

203 lines
11 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.

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
? '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>' // 向下箭头表示"显示"
: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>'; // 向上箭头表示"隐藏"
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 = '<i class="fa-solid fa-gear" style="color:#2563eb;font-size:16px;display:block;"></i>';
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 = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';
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函数可以用它来计算布局
}
};