feat(chatbot): 重构输入区与高级选项栏

This commit is contained in:
肖应宇 2026-04-13 17:55:59 +08:00
parent 3890cca114
commit 6a25d6aa3c
4 changed files with 414 additions and 198 deletions

View File

@ -22,7 +22,7 @@
/* ==================== Chatbot Modal Window (High-end Minimalist) ==================== */
.chatbot-window {
background: var(--color-bg-base); /* Pure white background */
max-width: 720px;
width: 92vw;
min-height: 520px;
@ -30,7 +30,6 @@
border-radius: 24px;
/* The Shadow Fix: Using variable for consistency and lightness */
box-shadow: var(--shadow-modal);
border: 1px solid var(--color-border-light); /* Very subtle border */
position: absolute;
@ -86,6 +85,7 @@
scroll-behavior: smooth;
position: relative;
z-index: 0;
margin-bottom: 20px;
}
/*
@ -291,13 +291,13 @@ body.dark #chatbot-toast {
/* 用户消息容器 */
.user-message-container {
justify-content: flex-end;
padding-left: 20%;
/* padding-left: 20%; */
}
/* 助手消息容器 */
.assistant-message-container {
justify-content: flex-start;
padding-right: 10%; /* 减少右侧留白,利用更多空间 */
/* padding-right: 10%; */
/*
IMPORTANT: No top padding here anymore.
@ -344,6 +344,7 @@ body.dark #chatbot-toast {
.chat-bubble.assistant {
background: transparent;
color: var(--color-text-primary);
width: 100%;
border-radius: 0;
border: none;
box-shadow: none;
@ -493,9 +494,14 @@ body.dark #chatbot-toast {
/* 输入区容器 */
.chatbot-input-container {
padding: 16px 24px 20px 24px;
height: 160px;
display: flex;
margin: 0 20px 20px;
border-radius: 10px;
flex-direction: column;
padding: 10px;
border-top: 1px solid rgba(0, 0, 0, 0.03);
background: rgba(255, 255, 255, 0.8); /* 稍微降低不透明度以增强毛玻璃感 */
background: #F8F9FA; /* 稍微降低不透明度以增强毛玻璃感 */
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
flex-shrink: 0;
@ -506,9 +512,9 @@ body.dark #chatbot-toast {
/* 输入区内部 Wrapper */
.chatbot-input-wrapper {
display: flex;
align-items: flex-end;
gap: 10px;
background: rgba(249, 250, 251, 0.8); /* var(--slate-50) with opacity */
flex: 1;
border: 1px solid transparent;
border-radius: 16px;
padding: 8px 8px 8px 14px;
@ -517,14 +523,107 @@ body.dark #chatbot-toast {
}
.chatbot-input-wrapper:focus-within {
background: white;
border-color: var(--slate-200); /* 柔和的边框颜色,避免刺眼的蓝线 */
/* background: white; */
/* border-color: var(--slate-200); 柔和的边框颜色,避免刺眼的蓝线 */
/*
使用更柔和的扩散阴影而不是实心光晕
原先的 0 0 0 3px 会产生类似边框的实心线效果
改为 0 0 0 4px rgba(...) 并降低透明度使其更像光晕
*/
box-shadow: 0 0 0 4px rgba(224, 231, 255, 0.4);
/* box-shadow: 0 0 0 4px rgba(224, 231, 255, 0.4); */
}
/* ==================== Floating Options (Switch/Selector) ==================== */
#chatbot-floating-options .chatbot-floating-option {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 2px 6px;
border-radius: 8px;
user-select: none;
}
#chatbot-floating-options .chatbot-floating-option-label {
font-size: 11px;
color: #4b5563;
}
#chatbot-floating-options .chatbot-floating-option.is-active {
background: rgba(0, 15, 51, 0.08);
}
#chatbot-floating-options .chatbot-floating-option.is-active .chatbot-floating-option-label {
color: #000F33;
font-weight: 600;
}
/* Switch */
#chatbot-floating-options .chatbot-floating-option-switch {
cursor: pointer;
}
#chatbot-floating-options .chatbot-switch {
position: relative;
width: 32px;
height: 18px;
flex-shrink: 0;
}
#chatbot-floating-options .chatbot-switch-input {
position: absolute;
inset: 0;
margin: 0;
opacity: 0;
cursor: pointer;
}
#chatbot-floating-options .chatbot-switch-track {
position: absolute;
inset: 0;
background: rgba(148, 163, 184, 0.55);
border-radius: 999px;
transition: background 0.2s ease;
}
#chatbot-floating-options .chatbot-switch-thumb {
position: absolute;
top: 2px;
left: 2px;
width: 14px;
height: 14px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
transition: transform 0.2s ease;
}
#chatbot-floating-options .chatbot-switch-input:checked + .chatbot-switch-track {
background: #000F33;
}
#chatbot-floating-options .chatbot-switch-input:checked + .chatbot-switch-track .chatbot-switch-thumb {
transform: translateX(14px);
}
/* Selector */
#chatbot-floating-options .chatbot-floating-option-select {
cursor: default;
}
#chatbot-floating-options .chatbot-floating-selector {
font-size: 11px;
height: 22px;
border-radius: 8px;
border: 1px solid rgba(148, 163, 184, 0.35);
background: rgba(255, 255, 255, 0.75);
color: #334155;
padding: 0 8px;
outline: none;
}
#chatbot-floating-options .chatbot-floating-option.is-active .chatbot-floating-selector {
border-color: #000F33;
}
/* 输入框 */
@ -785,4 +884,4 @@ body.dark #chatbot-toast {
.markdown-content table::-webkit-scrollbar-thumb {
background: var(--slate-300);
border-radius: 2px;
}
}

View File

@ -14,11 +14,8 @@ if (typeof window.ChatbotFloatingOptionsScriptLoaded === 'undefined') {
const _chatbotOptionsConfig = [
{ key: 'semanticGroups', texts: ['意群'], title: '查看/搜索意群', activeStyleColor: '#059669', isAction: true },
{ key: 'useContext', texts: ['上下文:关', '上下文:开'], values: [false, true], title: '切换是否使用对话历史', activeStyleColor: '#1d4ed8' },
{ key: 'useReActMode', texts: ['ReAct'], activeStyleColor: '#9ca3af', isDisabled: true, title: 'ReAct框架开发中推理+工具调用交织,智能动态构建上下文' },
{ key: 'multiHopRetrieval', texts: ['检索Agent:关', '检索Agent:开'], values: [false, true], defaultKey: false, title: '开启后自动启用:多轮取材+流式显示+意群分析+向量搜索+重排', activeStyleColor: '#059669' },
{ key: 'summarySource', texts: ['提供全文:OCR', '提供全文:无', '提供全文:翻译'], values: ['ocr', 'none', 'translation'], defaultKey: 'ocr', title: '切换总结时使用的文本源 (OCR/不使用文档内容/翻译)', activeStyleColor: '#1d4ed8' },
{ key: 'interestPointsActive', texts: ['兴趣点'], activeStyleColor: '#059669', isPlaceholder: true, title: '兴趣点功能 (待实现)' },
{ key: 'memoryManagementActive', texts: ['记忆管理'], activeStyleColor: '#059669', isPlaceholder: true, title: '记忆管理功能 (待实现)' }
{ key: 'summarySource', texts: ['提供全文:OCR', '提供全文:无', '提供全文:翻译'], values: ['ocr', 'none', 'translation'], defaultKey: 'ocr', title: '切换总结时使用的文本源 (OCR/不使用文档内容/翻译)', activeStyleColor: '#1d4ed8' }
];
/**
@ -26,16 +23,16 @@ if (typeof window.ChatbotFloatingOptionsScriptLoaded === 'undefined') {
* @param {HTMLElement} parentElement - 选项栏将被添加到的父容器
* @param {function} globalUpdateUICallback - 全局UI更新回调函数例如 window.ChatbotUI.updateChatbotUI
*/
function _createFloatingOptionsBar(parentElement, globalUpdateUICallback) {
if (!parentElement || document.getElementById('chatbot-floating-options')) {
return; // 如果父元素不存在或选项栏已存在,则不重复创建
}
function _createFloatingOptionsBar(parentElement, globalUpdateUICallback) {
if (!parentElement || document.getElementById('chatbot-floating-options')) {
return; // 如果父元素不存在或选项栏已存在,则不重复创建
}
const floatingOptionsContainer = document.createElement('div');
floatingOptionsContainer.id = 'chatbot-floating-options';
floatingOptionsContainer.style.cssText = `
display: flex;
justify-content: center;
justify-content: space-between;
align-items: center;
padding: 4px 0 8px 0;
gap: 5px;
@ -44,115 +41,197 @@ if (typeof window.ChatbotFloatingOptionsScriptLoaded === 'undefined') {
flex-wrap: wrap;
`;
_chatbotOptionsConfig.forEach((optConf, index) => {
const optionButton = document.createElement('button');
optionButton.id = `chatbot-option-${optConf.key}`;
optionButton.style.cssText = `
background: none;
border: none;
color: #4b5563;
cursor: pointer;
padding: 2px 4px;
font-size: 11px;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
`;
optionButton.title = optConf.title;
_chatbotOptionsConfig.forEach((optConf, index) => {
const optionEl = (function createOptionElement() {
// useContextswitch 组件(替代“上下文:开/关”文字)
if (optConf.key === 'useContext') {
const wrapper = document.createElement('label');
wrapper.id = `chatbot-option-${optConf.key}`;
wrapper.className = 'chatbot-floating-option chatbot-floating-option-switch';
wrapper.title = optConf.title;
const labelSpan = document.createElement('span');
labelSpan.className = 'chatbot-floating-option-label';
labelSpan.textContent = '上下文';
const switchWrap = document.createElement('span');
switchWrap.className = 'chatbot-switch';
const input = document.createElement('input');
input.type = 'checkbox';
input.id = 'chatbot-use-context-switch';
input.className = 'chatbot-switch-input';
window.chatbotActiveOptions = window.chatbotActiveOptions || {};
input.checked = !!window.chatbotActiveOptions.useContext;
wrapper.classList.toggle('is-active', !!window.chatbotActiveOptions.useContext);
const track = document.createElement('span');
track.className = 'chatbot-switch-track';
const thumb = document.createElement('span');
thumb.className = 'chatbot-switch-thumb';
track.appendChild(thumb);
switchWrap.appendChild(input);
switchWrap.appendChild(track);
wrapper.appendChild(labelSpan);
wrapper.appendChild(switchWrap);
input.addEventListener('change', function() {
window.chatbotActiveOptions = window.chatbotActiveOptions || {};
window.chatbotActiveOptions.useContext = !!input.checked;
wrapper.classList.toggle('is-active', !!input.checked);
if (typeof globalUpdateUICallback === 'function') globalUpdateUICallback();
else console.error("ChatbotFloatingOptionsUI: globalUpdateUICallback is not provided or not a function.");
});
return wrapper;
}
// summarySourceselector 组件(替代“提供全文:xxx”循环切换按钮
if (optConf.key === 'summarySource') {
const wrapper = document.createElement('div');
wrapper.id = `chatbot-option-${optConf.key}`;
wrapper.className = 'chatbot-floating-option chatbot-floating-option-select';
wrapper.title = optConf.title;
const labelSpan = document.createElement('span');
labelSpan.className = 'chatbot-floating-option-label';
labelSpan.textContent = '提供全文';
const select = document.createElement('select');
select.id = 'chatbot-summary-source-select';
select.className = 'chatbot-floating-selector';
optConf.values.forEach((val, idx) => {
const opt = document.createElement('option');
opt.value = val;
const rawText = optConf.texts[idx] || String(val);
opt.textContent = rawText.replace(/^提供全文:/, '');
select.appendChild(opt);
});
window.chatbotActiveOptions = window.chatbotActiveOptions || {};
select.value = window.chatbotActiveOptions.summarySource || optConf.defaultKey || optConf.values[0];
wrapper.classList.toggle('is-active', select.value !== optConf.defaultKey);
select.addEventListener('change', function() {
window.chatbotActiveOptions = window.chatbotActiveOptions || {};
window.chatbotActiveOptions.summarySource = select.value;
wrapper.classList.toggle('is-active', select.value !== optConf.defaultKey);
if (typeof globalUpdateUICallback === 'function') globalUpdateUICallback();
else console.error("ChatbotFloatingOptionsUI: globalUpdateUICallback is not provided or not a function.");
});
wrapper.appendChild(labelSpan);
wrapper.appendChild(select);
return wrapper;
}
// 默认:沿用按钮
const optionButton = document.createElement('button');
optionButton.id = `chatbot-option-${optConf.key}`;
optionButton.style.cssText = `
background: none;
border: none;
color: #4b5563;
cursor: pointer;
padding: 2px 4px;
font-size: 11px;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
`;
optionButton.title = optConf.title;
optionButton.onclick = function() {
window.chatbotActiveOptions = window.chatbotActiveOptions || {};
if (optConf.isDisabled) {
console.log(`${optConf.key} clicked, but is disabled.`);
if (typeof ChatbotUtils !== 'undefined' && ChatbotUtils.showToast) {
ChatbotUtils.showToast(`${optConf.texts[0]} 功能开发中,暂不可用。`, 'info', 2000);
} else {
alert(`${optConf.texts[0]} 功能开发中,暂不可用。`);
}
return; // 阻止后续逻辑执行
}
if (optConf.isPlaceholder) {
console.log(`${optConf.key} clicked, placeholder for future feature.`);
if (typeof ChatbotUtils !== 'undefined' && ChatbotUtils.showToast) {
ChatbotUtils.showToast(`${optConf.texts[0]} 功能正在开发中。`, 'info', 2000);
} else {
alert(`${optConf.texts[0]} 功能正在开发中。`);
}
} else if (optConf.isAction && optConf.key === 'semanticGroups') {
if (window.SemanticGroupsUI && typeof window.SemanticGroupsUI.toggle === 'function') {
window.SemanticGroupsUI.toggle();
} else if (typeof ChatbotUtils !== 'undefined' && ChatbotUtils.showToast) {
ChatbotUtils.showToast('意群面板未就绪', 'warning', 2000);
} else {
alert('意群面板未就绪');
}
} else {
const currentValue = window.chatbotActiveOptions[optConf.key];
if (optConf.key === 'contentLengthStrategy') {
window.chatbotActiveOptions.contentLengthStrategy = currentValue === optConf.values[0] ? optConf.values[1] : optConf.values[0];
} else if (optConf.key === 'multiHopRetrieval') {
window.chatbotActiveOptions.multiHopRetrieval = !currentValue;
// 传统多轮检索开启时自动关闭ReAct模式避免冲突
if (window.chatbotActiveOptions.multiHopRetrieval) {
window.chatbotActiveOptions.useReActMode = false;
}
} else if (optConf.key === 'streamingRetrieval') {
window.chatbotActiveOptions.streamingRetrieval = !currentValue;
}
}
if (typeof globalUpdateUICallback === 'function') {
globalUpdateUICallback(); // 更新UI以反映选项变化
} else {
console.error("ChatbotFloatingOptionsUI: globalUpdateUICallback is not provided or not a function.");
}
};
return optionButton;
})();
floatingOptionsContainer.appendChild(optionEl);
if (index < _chatbotOptionsConfig.length - 1) {
const nextOptConf = _chatbotOptionsConfig[index + 1];
const separator = document.createElement('span');
separator.id = `chatbot-separator-${nextOptConf.key}`;
separator.textContent = '丨';
separator.style.color = '#cbd5e1';
separator.style.margin = '0 2px';
floatingOptionsContainer.appendChild(separator);
}
});
optionButton.onclick = function() {
if (optConf.isDisabled) {
console.log(`${optConf.key} clicked, but is disabled.`);
if (typeof ChatbotUtils !== 'undefined' && ChatbotUtils.showToast) {
ChatbotUtils.showToast(`${optConf.texts[0]} 功能开发中,暂不可用。`, 'info', 2000);
} else {
alert(`${optConf.texts[0]} 功能开发中,暂不可用。`);
}
return; // 阻止后续逻辑执行
}
if (optConf.isPlaceholder) {
console.log(`${optConf.key} clicked, placeholder for future feature.`);
if (typeof ChatbotUtils !== 'undefined' && ChatbotUtils.showToast) {
ChatbotUtils.showToast(`${optConf.texts[0]} 功能正在开发中。`, 'info', 2000);
} else {
alert(`${optConf.texts[0]} 功能正在开发中。`);
}
} else if (optConf.isAction && optConf.key === 'semanticGroups') {
if (window.SemanticGroupsUI && typeof window.SemanticGroupsUI.toggle === 'function') {
window.SemanticGroupsUI.toggle();
} else if (typeof ChatbotUtils !== 'undefined' && ChatbotUtils.showToast) {
ChatbotUtils.showToast('意群面板未就绪', 'warning', 2000);
} else {
alert('意群面板未就绪');
}
} else {
const currentValue = window.chatbotActiveOptions[optConf.key];
if (optConf.key === 'useContext') {
window.chatbotActiveOptions.useContext = !currentValue;
} else if (optConf.key === 'useReActMode') {
window.chatbotActiveOptions.useReActMode = !currentValue;
// ReAct模式开启时自动关闭传统多轮检索避免冲突
if (window.chatbotActiveOptions.useReActMode) {
window.chatbotActiveOptions.multiHopRetrieval = false;
}
} else if (optConf.key === 'contentLengthStrategy') {
window.chatbotActiveOptions.contentLengthStrategy = currentValue === optConf.values[0] ? optConf.values[1] : optConf.values[0];
} else if (optConf.key === 'multiHopRetrieval') {
window.chatbotActiveOptions.multiHopRetrieval = !currentValue;
// 传统多轮检索开启时自动关闭ReAct模式避免冲突
if (window.chatbotActiveOptions.multiHopRetrieval) {
window.chatbotActiveOptions.useReActMode = false;
}
} else if (optConf.key === 'streamingRetrieval') {
window.chatbotActiveOptions.streamingRetrieval = !currentValue;
} else if (optConf.key === 'summarySource') {
const currentIndex = optConf.values.indexOf(currentValue);
const nextIndex = (currentIndex + 1) % optConf.values.length;
window.chatbotActiveOptions.summarySource = optConf.values[nextIndex];
}
}
if (typeof globalUpdateUICallback === 'function') {
globalUpdateUICallback(); // 更新UI以反映选项变化
} else {
console.error("ChatbotFloatingOptionsUI: globalUpdateUICallback is not provided or not a function.");
}
};
floatingOptionsContainer.appendChild(optionButton);
if (index < _chatbotOptionsConfig.length - 1) {
const nextOptConf = _chatbotOptionsConfig[index + 1];
const separator = document.createElement('span');
separator.id = `chatbot-separator-${nextOptConf.key}`;
separator.textContent = '丨';
separator.style.color = '#cbd5e1';
separator.style.margin = '0 2px';
floatingOptionsContainer.appendChild(separator);
}
});
// 将创建的选项栏插入到父容器的合适位置
const selectedImagesPreview = parentElement.querySelector('#chatbot-selected-images-preview');
if (selectedImagesPreview) {
parentElement.insertBefore(floatingOptionsContainer, selectedImagesPreview);
// 将创建的选项栏插入到父容器的合适位置放在输入框容器chatbot-input-wrapper后面
const inputWrapper = parentElement.querySelector('#chatbot-input-wrapper, .chatbot-input-wrapper');
if (inputWrapper && inputWrapper.parentNode === parentElement) {
parentElement.insertBefore(floatingOptionsContainer, inputWrapper.nextSibling);
} else if (inputWrapper && inputWrapper.parentNode) {
inputWrapper.parentNode.insertBefore(floatingOptionsContainer, inputWrapper.nextSibling);
} else {
const mainInputDiv = parentElement.querySelector('div[style*="display:flex;align-items:center;gap:12px;"]');
if (mainInputDiv) {
parentElement.insertBefore(floatingOptionsContainer, mainInputDiv);
} else {
parentElement.appendChild(floatingOptionsContainer); // Fallback
}
parentElement.appendChild(floatingOptionsContainer); // Fallback
}
}
/**
* 更新浮动高级选项栏中各个按钮的显示状态文本样式可见性
*/
function _updateFloatingOptionsDisplay() {
const floatingOptionsContainer = document.getElementById('chatbot-floating-options');
if (!floatingOptionsContainer) return;
function _updateFloatingOptionsDisplay() {
const floatingOptionsContainer = document.getElementById('chatbot-floating-options');
if (!floatingOptionsContainer) return;
window.chatbotActiveOptions = window.chatbotActiveOptions || {};
_chatbotOptionsConfig.forEach(optConf => {
const button = document.getElementById(`chatbot-option-${optConf.key}`);
// 注意分隔符ID是基于 *下一个* 选项的key来创建的所以查找ID为 chatbot-separator-NEXT_KEY
_chatbotOptionsConfig.forEach(optConf => {
const button = document.getElementById(`chatbot-option-${optConf.key}`);
// 注意分隔符ID是基于 *下一个* 选项的key来创建的所以查找ID为 chatbot-separator-NEXT_KEY
// 但是其显示与否是基于 *当前* 按钮的显示状态
if (button) {
@ -238,42 +317,42 @@ if (typeof window.ChatbotFloatingOptionsScriptLoaded === 'undefined') {
}
});
// 第三遍:更新按钮文本和样式
_chatbotOptionsConfig.forEach(optConf => {
const button = document.getElementById(`chatbot-option-${optConf.key}`);
// 第三遍:更新按钮文本和样式
_chatbotOptionsConfig.forEach(optConf => {
const button = document.getElementById(`chatbot-option-${optConf.key}`);
if (!button || button.style.display === 'none') {
return; // 跳过不可见的按钮
}
if (!button || button.style.display === 'none') {
return; // 跳过不可见的按钮
}
const currentOptionValue = window.chatbotActiveOptions[optConf.key];
let currentText = '';
let color = '#4b5563';
let fontWeight = 'normal';
const currentOptionValue = window.chatbotActiveOptions[optConf.key];
let currentText = '';
let color = '#4b5563';
let fontWeight = 'normal';
let isActiveStyle = false;
if (optConf.isAction && optConf.key === 'semanticGroups') {
const count = (window.data && Array.isArray(window.data.semanticGroups)) ? window.data.semanticGroups.length : 0;
currentText = count > 0 ? `意群(${count})` : '意群';
// 显示为激活风格以便更醒目(当有意群时)
if (count > 0) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.isPlaceholder) {
currentText = optConf.texts[0];
} else if (optConf.isDisabled) {
// 禁用状态:固定显示灰色,无法切换
currentText = optConf.texts[0];
if (optConf.isAction && optConf.key === 'semanticGroups') {
const count = (window.data && Array.isArray(window.data.semanticGroups)) ? window.data.semanticGroups.length : 0;
currentText = count > 0 ? `意群(${count})` : '意群';
// 显示为激活风格以便更醒目(当有意群时)
if (count > 0) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.key === 'useContext') {
const isOn = !!currentOptionValue;
const switchInput = document.getElementById('chatbot-use-context-switch');
if (switchInput) switchInput.checked = isOn;
button.classList.toggle('is-active', isOn);
return;
} else if (optConf.isPlaceholder) {
currentText = optConf.texts[0];
} else if (optConf.isDisabled) {
// 禁用状态:固定显示灰色,无法切换
currentText = optConf.texts[0];
color = '#9ca3af'; // 灰色
fontWeight = 'normal';
isActiveStyle = false;
button.style.opacity = '0.6'; // 降低透明度
button.style.cursor = 'not-allowed'; // 禁用光标
} else if (optConf.key === 'useContext') {
currentText = currentOptionValue ? optConf.texts[1] : optConf.texts[0];
if (currentOptionValue) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.key === 'useReActMode') {
currentText = currentOptionValue ? optConf.texts[1] : optConf.texts[0];
if (currentOptionValue) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.key === 'contentLengthStrategy') {
} else if (optConf.key === 'contentLengthStrategy') {
currentText = currentOptionValue === optConf.defaultKey ? optConf.texts[0] : optConf.texts[1];
if (currentOptionValue !== optConf.defaultKey) {
color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true;
@ -281,20 +360,19 @@ if (typeof window.ChatbotFloatingOptionsScriptLoaded === 'undefined') {
} else if (optConf.key === 'multiHopRetrieval') {
currentText = currentOptionValue ? optConf.texts[1] : optConf.texts[0];
if (currentOptionValue) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.key === 'streamingRetrieval') {
currentText = currentOptionValue ? optConf.texts[1] : optConf.texts[0];
if (currentOptionValue) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.key === 'summarySource') {
const currentIndex = optConf.values.indexOf(currentOptionValue);
currentText = optConf.texts[currentIndex] || optConf.texts[0];
// 对于 summarySource, 'ocr' (index 0) 是默认状态,'none' (index 1) 和 'translation' (index 2) 算作激活状态
if (currentOptionValue !== optConf.defaultKey) {
color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true;
}
}
button.textContent = currentText;
button.style.color = color;
button.style.fontWeight = fontWeight;
} else if (optConf.key === 'streamingRetrieval') {
currentText = currentOptionValue ? optConf.texts[1] : optConf.texts[0];
if (currentOptionValue) { color = optConf.activeStyleColor; fontWeight = '600'; isActiveStyle = true; }
} else if (optConf.key === 'summarySource') {
const select = document.getElementById('chatbot-summary-source-select');
const desiredValue = currentOptionValue || optConf.defaultKey || optConf.values[0];
if (select && select.value !== desiredValue) select.value = desiredValue;
button.classList.toggle('is-active', desiredValue !== optConf.defaultKey);
return;
}
button.textContent = currentText;
button.style.color = color;
button.style.fontWeight = fontWeight;
if (isActiveStyle) {
button.style.backgroundColor = 'rgba(59, 130, 246, 0.1)'; // 淡蓝色背景表示激活

View File

@ -56,9 +56,9 @@ window.ChatbotPresetQuestionsUI = {
headerLeftGroup.style.display = 'flex';
headerLeftGroup.style.alignItems = 'center';
headerLeftGroup.style.gap = '8px';
headerLeftGroup.appendChild(presetTitle);
headerLeftGroup.appendChild(presetToggleBtn);
newPresetHeader.appendChild(headerLeftGroup);
// headerLeftGroup.appendChild(presetTitle);
// headerLeftGroup.appendChild(presetToggleBtn);
// newPresetHeader.appendChild(headerLeftGroup);
// 齿轮按钮(模型配置)- 始终显示
const gearBtn = document.createElement('button');
@ -92,8 +92,8 @@ window.ChatbotPresetQuestionsUI = {
updateChatbotUICallback(); // 调用主UI更新
}
};
newPresetHeader.appendChild(gearBtn);
presetContainer.appendChild(newPresetHeader);
// newPresetHeader.appendChild(gearBtn);
// presetContainer.appendChild(newPresetHeader);
const newPresetBody = document.createElement('div');
newPresetBody.id = 'chatbot-preset-body';
@ -114,31 +114,31 @@ window.ChatbotPresetQuestionsUI = {
// 填充预设问题按钮
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);
});
// 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;

View File

@ -1190,7 +1190,7 @@ function initChatbotUI() {
</button>
</div>
<!-- 标题栏 (可拖拽移动窗口) -->
<div id="chatbot-title-bar" class="chatbot-draggable-header" style="padding:12px 24px;display:flex;align-items:center;gap:8px;border-bottom:1px dashed rgba(0,0,0,0.1);flex-shrink:0;">
<div id="chatbot-title-bar" class="chatbot-draggable-header" style="padding:12px 24px;display:flex;justify-content:center;align-items:center;gap:8px;border-bottom:1px dashed rgba(0,0,0,0.1);flex-shrink:0;">
<div style="width:32px;height:32px;border-radius:16px;background:linear-gradient(135deg,#3b82f6,#1d4ed8);display:flex;align-items:center;justify-content:center;display:none;">
<i class="fa-solid fa-robot" style="font-size: 14px; color: white;"></i>
</div>
@ -1203,7 +1203,7 @@ function initChatbotUI() {
</div>
<!-- 输入区域容器 (Refactored) -->
<div id="chatbot-input-container" class="chatbot-input-container">
<!-- 浮动高级选项将由JS插入此处 -->
<!-- 浮动高级选项将由JS插入chatbot-input-wrapper 后面 -->
<!-- 已选图片预览区 -->
<div id="chatbot-selected-images-preview" class="chatbot-image-preview-area">
{/* 图片预览由 ChatbotImageUtils.updateSelectedImagesPreview 更新 */}
@ -1223,10 +1223,10 @@ function initChatbotUI() {
<iconify-icon icon="carbon:document-pdf" width="20"></iconify-icon>
</button>
<!-- 文本输入框 -->
<input id="chatbot-input" type="text" placeholder="请输入问题..."
<textarea id="chatbot-input" type="text" placeholder="请输入问题..."
class="chatbot-input-field"
onkeydown="if(event.key==='Enter'){window.handleChatbotSend();}"
/>
></textarea>
<!-- 发送按钮 -->
<button id="chatbot-send-btn"
class="chatbot-input-btn chatbot-send-btn"
@ -1250,12 +1250,13 @@ function initChatbotUI() {
</button>
</div>
<!-- 免责声明 -->
<div class="chatbot-disclaimer">
<p style="margin:0;">AI助手可能会犯错请核实重要信息</p>
</div>
</div>
</div>
`;
// <div class="chatbot-disclaimer">
// <p style="margin:0;">AI助手可能会犯错。请核实重要信息。</p>
// </div>
document.body.appendChild(modal);
}
@ -1265,6 +1266,44 @@ function initChatbotUI() {
window.ChatbotFloatingOptionsUI.createBar(inputContainerElement, updateChatbotUI);
}
// 将输入栏的核心按钮移动到浮动选项栏中
// 目标容器由 ChatbotFloatingOptionsUI.createBar 创建:#chatbot-floating-options
(function movePrimaryButtonsIntoFloatingOptions() {
const floatingOptionsContainer = document.getElementById('chatbot-floating-options');
if (!floatingOptionsContainer) return;
const addImageBtn = document.getElementById('chatbot-add-image-btn');
const providePdfBtn = document.getElementById('chatbot-provide-pdf-btn');
const sendBtn = document.getElementById('chatbot-send-btn');
// 1) 先把“添加图片/提供PDF”放到浮动选项栏最前面
const prefixFragment = document.createDocumentFragment();
for (const el of [addImageBtn, providePdfBtn]) {
if (!el) continue;
if (el.parentElement === floatingOptionsContainer) continue;
prefixFragment.appendChild(el);
}
if (prefixFragment.childNodes.length) {
floatingOptionsContainer.insertBefore(prefixFragment, floatingOptionsContainer.firstChild);
}
// 2) 再把“发送”放到 chatbot-option-summarySource 后面
if (!sendBtn) return;
if (sendBtn.parentElement === floatingOptionsContainer) {
// 仍然允许重新定位appendChild 会自动移动节点)
}
const summarySourceOptionBtn = document.getElementById('chatbot-option-summarySource');
if (summarySourceOptionBtn && summarySourceOptionBtn.parentElement === floatingOptionsContainer) {
if (summarySourceOptionBtn.nextSibling) {
floatingOptionsContainer.insertBefore(sendBtn, summarySourceOptionBtn.nextSibling);
} else {
floatingOptionsContainer.appendChild(sendBtn);
}
} else {
floatingOptionsContainer.appendChild(sendBtn);
}
})();
// --- 核心控制按钮事件绑定 ---
// 浮动模式切换按钮点击事件
document.getElementById('chatbot-float-toggle-btn').onclick = function() {