|
|
@ -96,24 +96,8 @@ var ChatbotSDK = (function () { |
|
|
}, |
|
|
}, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
/** 默认悬浮按钮 SVG 图标(赛博机器人:对话气泡 + 机器人面孔 + 天线能量灯 + 眨眼动效) */ |
|
|
|
|
|
const DEFAULT_LAUNCHER_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32" fill="none">
|
|
|
|
|
|
<defs> |
|
|
|
|
|
<linearGradient id="csk-ico-grad" x1="6" y1="8" x2="26" y2="28" gradientUnits="userSpaceOnUse"> |
|
|
|
|
|
<stop offset="0%" stop-color="#fff" stop-opacity="1"/> |
|
|
|
|
|
<stop offset="100%" stop-color="#fff" stop-opacity="0.82"/> |
|
|
|
|
|
</linearGradient> |
|
|
|
|
|
</defs> |
|
|
|
|
|
<rect x="5" y="8" width="22" height="16" rx="4" fill="url(#csk-ico-grad)"/> |
|
|
|
|
|
<path d="M9 24L6 28.5l6-4.5z" fill="url(#csk-ico-grad)"/> |
|
|
|
|
|
<line x1="16" y1="8" x2="16" y2="4" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/> |
|
|
|
|
|
<circle cx="16" cy="3" r="1.5" fill="#fff" class="csk-ico-antenna"/> |
|
|
|
|
|
<rect x="9.5" y="13" width="4" height="3.5" rx="1.2" fill="rgba(99,102,241,0.55)" class="csk-ico-eye"/> |
|
|
|
|
|
<rect x="18.5" y="13" width="4" height="3.5" rx="1.2" fill="rgba(99,102,241,0.55)" class="csk-ico-eye"/> |
|
|
|
|
|
<circle cx="11" cy="14.2" r="0.7" fill="#fff" opacity="0.9"/> |
|
|
|
|
|
<circle cx="20" cy="14.2" r="0.7" fill="#fff" opacity="0.9"/> |
|
|
|
|
|
<path d="M12.5 20 Q16 22.5 19.5 20" stroke="rgba(99,102,241,0.4)" stroke-width="1.2" fill="none" stroke-linecap="round"/> |
|
|
|
|
|
</svg>`; |
|
|
|
|
|
|
|
|
/** 默认悬浮按钮图标 — C3 三点脉冲序列:极光旋转渐变底 + 三颗顺序脉冲白点 */ |
|
|
|
|
|
const DEFAULT_LAUNCHER_ICON = `<span class="csk-ico-pulse-dot csk-ico-pulse-dot--1"></span><span class="csk-ico-pulse-dot csk-ico-pulse-dot--2"></span><span class="csk-ico-pulse-dot csk-ico-pulse-dot--3"></span>`; |
|
|
// ==================== 马卡龙色系玻璃质感图标 ====================
|
|
|
// ==================== 马卡龙色系玻璃质感图标 ====================
|
|
|
/** |
|
|
/** |
|
|
* 生成玻璃质感机器人 SVG 图标 |
|
|
* 生成玻璃质感机器人 SVG 图标 |
|
|
@ -186,7 +170,7 @@ var ChatbotSDK = (function () { |
|
|
* 解析并校验用户传入的配置,填充默认值 |
|
|
* 解析并校验用户传入的配置,填充默认值 |
|
|
*/ |
|
|
*/ |
|
|
function parseConfig(raw) { |
|
|
function parseConfig(raw) { |
|
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; |
|
|
|
|
|
|
|
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; |
|
|
// 校验必传参数:integrateId(对应后端 roleId)
|
|
|
// 校验必传参数:integrateId(对应后端 roleId)
|
|
|
if (!raw.integrateId || (typeof raw.integrateId !== 'string' && typeof raw.integrateId !== 'number') |
|
|
if (!raw.integrateId || (typeof raw.integrateId !== 'string' && typeof raw.integrateId !== 'number') |
|
|
|| (typeof raw.integrateId === 'string' && raw.integrateId.trim() === '')) { |
|
|
|| (typeof raw.integrateId === 'string' && raw.integrateId.trim() === '')) { |
|
|
@ -202,7 +186,7 @@ var ChatbotSDK = (function () { |
|
|
try { |
|
|
try { |
|
|
new URL(raw.requestDomain); |
|
|
new URL(raw.requestDomain); |
|
|
} |
|
|
} |
|
|
catch (_m) { |
|
|
|
|
|
|
|
|
catch (_o) { |
|
|
logger.error(`requestDomain 不是合法的 URL 格式:${raw.requestDomain}。请提供完整的域名,如 https://api.example.com`); |
|
|
logger.error(`requestDomain 不是合法的 URL 格式:${raw.requestDomain}。请提供完整的域名,如 https://api.example.com`); |
|
|
return null; |
|
|
return null; |
|
|
} |
|
|
} |
|
|
@ -248,13 +232,14 @@ var ChatbotSDK = (function () { |
|
|
theme: raw.theme === 'dark' ? 'dark' : 'light', |
|
|
theme: raw.theme === 'dark' ? 'dark' : 'light', |
|
|
showTeaser: (_f = raw.showTeaser) !== null && _f !== void 0 ? _f : true, |
|
|
showTeaser: (_f = raw.showTeaser) !== null && _f !== void 0 ? _f : true, |
|
|
teaserText: (typeof raw.teaserText === 'string' && raw.teaserText.trim()) || '', |
|
|
teaserText: (typeof raw.teaserText === 'string' && raw.teaserText.trim()) || '', |
|
|
streaming: (_g = raw.streaming) !== null && _g !== void 0 ? _g : true, |
|
|
|
|
|
enableRag: (_h = raw.enableRag) !== null && _h !== void 0 ? _h : true, |
|
|
|
|
|
|
|
|
resizable: (_g = raw.resizable) !== null && _g !== void 0 ? _g : true, |
|
|
|
|
|
streaming: (_h = raw.streaming) !== null && _h !== void 0 ? _h : true, |
|
|
|
|
|
enableRag: (_j = raw.enableRag) !== null && _j !== void 0 ? _j : true, |
|
|
rewriteStrategy: raw.rewriteStrategy || 'REWRITE', |
|
|
rewriteStrategy: raw.rewriteStrategy || 'REWRITE', |
|
|
locale: raw.locale || 'zh-CN', |
|
|
locale: raw.locale || 'zh-CN', |
|
|
debug: (_j = raw.debug) !== null && _j !== void 0 ? _j : true, |
|
|
|
|
|
sound: (_k = raw.sound) !== null && _k !== void 0 ? _k : false, |
|
|
|
|
|
notification: (_l = raw.notification) !== null && _l !== void 0 ? _l : false, |
|
|
|
|
|
|
|
|
debug: (_k = raw.debug) !== null && _k !== void 0 ? _k : true, |
|
|
|
|
|
sound: (_l = raw.sound) !== null && _l !== void 0 ? _l : false, |
|
|
|
|
|
notification: (_m = raw.notification) !== null && _m !== void 0 ? _m : false, |
|
|
onError: typeof raw.onError === 'function' ? raw.onError : undefined, |
|
|
onError: typeof raw.onError === 'function' ? raw.onError : undefined, |
|
|
onReady: typeof raw.onReady === 'function' ? raw.onReady : undefined, |
|
|
onReady: typeof raw.onReady === 'function' ? raw.onReady : undefined, |
|
|
onMessage: typeof raw.onMessage === 'function' ? raw.onMessage : undefined, |
|
|
onMessage: typeof raw.onMessage === 'function' ? raw.onMessage : undefined, |
|
|
@ -322,6 +307,7 @@ var ChatbotSDK = (function () { |
|
|
// 提示气泡
|
|
|
// 提示气泡
|
|
|
teaser_text: '有什么可以帮你的吗?', |
|
|
teaser_text: '有什么可以帮你的吗?', |
|
|
new_msg_announce: '收到新消息', |
|
|
new_msg_announce: '收到新消息', |
|
|
|
|
|
resize: '拖拽缩放窗口', |
|
|
// 错误提示
|
|
|
// 错误提示
|
|
|
error_network: '网络连接失败,请检查网络', |
|
|
error_network: '网络连接失败,请检查网络', |
|
|
error_timeout: '请求超时,请稍后重试', |
|
|
error_timeout: '请求超时,请稍后重试', |
|
|
@ -387,6 +373,7 @@ var ChatbotSDK = (function () { |
|
|
// Teaser
|
|
|
// Teaser
|
|
|
teaser_text: 'How can I help you?', |
|
|
teaser_text: 'How can I help you?', |
|
|
new_msg_announce: 'New message received', |
|
|
new_msg_announce: 'New message received', |
|
|
|
|
|
resize: 'Resize window', |
|
|
// Errors
|
|
|
// Errors
|
|
|
error_network: 'Network connection failed', |
|
|
error_network: 'Network connection failed', |
|
|
error_timeout: 'Request timed out, please try again', |
|
|
error_timeout: 'Request timed out, please try again', |
|
|
@ -1063,7 +1050,13 @@ var ChatbotSDK = (function () { |
|
|
width: 60px; |
|
|
width: 60px; |
|
|
height: 60px; |
|
|
height: 60px; |
|
|
border-radius: 50%; |
|
|
border-radius: 50%; |
|
|
background: var(--csk-bg-user); |
|
|
|
|
|
|
|
|
/* 极光旋转渐变底 — 默认蓝紫系,由 primaryColor 驱动的 conic-gradient */ |
|
|
|
|
|
background: conic-gradient(from 0deg, |
|
|
|
|
|
var(--csk-primary-light, #06b6d4), |
|
|
|
|
|
var(--csk-primary, #3b82f6), |
|
|
|
|
|
var(--csk-primary-hover, #8b5cf6), |
|
|
|
|
|
var(--csk-primary-light, #06b6d4) |
|
|
|
|
|
); |
|
|
display: flex; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
justify-content: center; |
|
|
@ -1083,7 +1076,9 @@ var ChatbotSDK = (function () { |
|
|
transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), |
|
|
transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), |
|
|
box-shadow 0.25s ease, |
|
|
box-shadow 0.25s ease, |
|
|
border-color 0.25s ease; |
|
|
border-color 0.25s ease; |
|
|
animation: csk-launcher-in 0.55s cubic-bezier(0.22, 1, 0.36, 1) both; |
|
|
|
|
|
|
|
|
animation: |
|
|
|
|
|
csk-aurora-spin 4s linear infinite, |
|
|
|
|
|
csk-launcher-in 0.55s cubic-bezier(0.22, 1, 0.36, 1) both; |
|
|
} |
|
|
} |
|
|
.csk-launcher--right { right: 24px; } |
|
|
.csk-launcher--right { right: 24px; } |
|
|
.csk-launcher--left { left: 24px; } |
|
|
.csk-launcher--left { left: 24px; } |
|
|
@ -1147,6 +1142,64 @@ var ChatbotSDK = (function () { |
|
|
60% { transform: scale(1.06) translateY(-2px); } |
|
|
60% { transform: scale(1.06) translateY(-2px); } |
|
|
100% { opacity: 1; transform: scale(1) translateY(0); } |
|
|
100% { opacity: 1; transform: scale(1) translateY(0); } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ========== 三点脉冲序列图标 ========== */ |
|
|
|
|
|
/* 极光旋转 */ |
|
|
|
|
|
@keyframes csk-aurora-spin { |
|
|
|
|
|
to { transform: rotate(360deg); } |
|
|
|
|
|
} |
|
|
|
|
|
/* 脉冲点 */ |
|
|
|
|
|
.csk-ico-pulse-dot { |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
width: 6px; |
|
|
|
|
|
height: 6px; |
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
background: #fff; |
|
|
|
|
|
z-index: 5; |
|
|
|
|
|
animation: csk-seq-pulse 1.2s ease-in-out infinite; |
|
|
|
|
|
box-shadow: 0 0 6px 1px rgba(255, 255, 255, 0.5); |
|
|
|
|
|
} |
|
|
|
|
|
.csk-ico-pulse-dot--1 { top: 4px; left: 50%; margin-left: -3px; } |
|
|
|
|
|
.csk-ico-pulse-dot--2 { top: 50%; right: 5px; margin-top: -3px; animation-delay: 0.3s; } |
|
|
|
|
|
.csk-ico-pulse-dot--3 { bottom: 5px; left: 50%; margin-left: -3px; animation-delay: 0.6s; } |
|
|
|
|
|
@keyframes csk-seq-pulse { |
|
|
|
|
|
0%, 100% { transform: scale(0.4); opacity: 0.3; box-shadow: 0 0 4px 1px rgba(255, 255, 255, 0.3); } |
|
|
|
|
|
35% { transform: scale(1.8); opacity: 1; box-shadow: 0 0 12px 4px rgba(255, 255, 255, 0.8); } |
|
|
|
|
|
70% { transform: scale(0.4); opacity: 0.3; box-shadow: 0 0 4px 1px rgba(255, 255, 255, 0.3); } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 移除旧的 SVG 图标相关样式(已不再使用) */ |
|
|
|
|
|
|
|
|
|
|
|
/* ========== 高光扫过动效 ========== */ |
|
|
|
|
|
.csk-launcher::before { |
|
|
|
|
|
content: ''; |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
top: -50%; |
|
|
|
|
|
left: -50%; |
|
|
|
|
|
width: 40%; |
|
|
|
|
|
height: 200%; |
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
background: linear-gradient( |
|
|
|
|
|
105deg, |
|
|
|
|
|
transparent 30%, |
|
|
|
|
|
rgba(255, 255, 255, 0.28) 48%, |
|
|
|
|
|
rgba(255, 255, 255, 0.1) 52%, |
|
|
|
|
|
transparent 70% |
|
|
|
|
|
); |
|
|
|
|
|
transform: rotate(25deg); |
|
|
|
|
|
animation: csk-shimmer 4s ease-in-out infinite; |
|
|
|
|
|
pointer-events: none; |
|
|
|
|
|
} |
|
|
|
|
|
/* 呼吸色晕 — 配合接地阴影,强化白底上的立体存在感 */ |
|
|
|
|
|
.csk-launcher::after { |
|
|
|
|
|
content: ''; |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
inset: -18px; |
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
background: radial-gradient(circle, rgba(var(--csk-primary-rgb), 0.18) 0%, rgba(var(--csk-primary-rgb), 0.05) 45%, transparent 68%); |
|
|
|
|
|
animation: csk-breathe 3.5s ease-in-out infinite; |
|
|
|
|
|
pointer-events: none; |
|
|
|
|
|
} |
|
|
@keyframes csk-shimmer { |
|
|
@keyframes csk-shimmer { |
|
|
0%, 100% { left: -60%; opacity: 0; } |
|
|
0%, 100% { left: -60%; opacity: 0; } |
|
|
20% { opacity: 1; } |
|
|
20% { opacity: 1; } |
|
|
@ -1158,25 +1211,11 @@ var ChatbotSDK = (function () { |
|
|
50% { transform: scale(1.15); opacity: 0.35; } |
|
|
50% { transform: scale(1.15); opacity: 0.35; } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* SVG 图标内部动效:眨眼 */ |
|
|
|
|
|
.csk-launcher .csk-ico-eye { |
|
|
|
|
|
animation: csk-eye-blink 4s ease-in-out infinite; |
|
|
|
|
|
} |
|
|
|
|
|
@keyframes csk-eye-blink { |
|
|
|
|
|
0%, 42%, 48%, 100% { opacity: 1; } |
|
|
|
|
|
45% { opacity: 0.1; } |
|
|
|
|
|
} |
|
|
|
|
|
/* SVG 图标内部动效:天线能量闪烁 */ |
|
|
|
|
|
.csk-launcher .csk-ico-antenna { |
|
|
|
|
|
animation: csk-antenna-glow 2s ease-in-out infinite; |
|
|
|
|
|
} |
|
|
|
|
|
@keyframes csk-antenna-glow { |
|
|
|
|
|
0%, 100% { opacity: 0.5; } |
|
|
|
|
|
50% { opacity: 1; } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ========== 磨砂玻璃质感悬浮按钮 ========== */ |
|
|
/* ========== 磨砂玻璃质感悬浮按钮 ========== */ |
|
|
.csk-launcher--glass { |
|
|
.csk-launcher--glass { |
|
|
|
|
|
/* 覆盖旋转动画为静止渐变 */ |
|
|
|
|
|
animation: |
|
|
|
|
|
csk-launcher-in 0.55s cubic-bezier(0.22, 1, 0.36, 1) both !important; |
|
|
background: linear-gradient( |
|
|
background: linear-gradient( |
|
|
135deg, |
|
|
135deg, |
|
|
var(--csk-launcher-grad-1, var(--csk-primary)) 0%, |
|
|
var(--csk-launcher-grad-1, var(--csk-primary)) 0%, |
|
|
@ -1237,18 +1276,7 @@ var ChatbotSDK = (function () { |
|
|
70% { left: 130%; opacity: 0; } |
|
|
70% { left: 130%; opacity: 0; } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* SVG 内部动效:跳动气泡(3 个小圆点交错弹跳) */ |
|
|
|
|
|
.csk-launcher .csk-ico-bubble { |
|
|
|
|
|
animation: csk-bubble-jump 1.4s ease-in-out infinite; |
|
|
|
|
|
} |
|
|
|
|
|
.csk-launcher .csk-ico-bubble--1 { animation-delay: 0s; } |
|
|
|
|
|
.csk-launcher .csk-ico-bubble--2 { animation-delay: 0.18s; } |
|
|
|
|
|
.csk-launcher .csk-ico-bubble--3 { animation-delay: 0.36s; } |
|
|
|
|
|
@keyframes csk-bubble-jump { |
|
|
|
|
|
0%, 100% { transform: translateY(0); } |
|
|
|
|
|
40% { transform: translateY(-3.5px); } |
|
|
|
|
|
60% { transform: translateY(-1px); } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* 三点脉冲图标在玻璃主题下保持可见 */ |
|
|
|
|
|
|
|
|
/* ========== 聊天弹窗 ========== */ |
|
|
/* ========== 聊天弹窗 ========== */ |
|
|
.csk-window { |
|
|
.csk-window { |
|
|
@ -2105,6 +2133,94 @@ var ChatbotSDK = (function () { |
|
|
box-shadow: 0 5px 14px rgba(239, 68, 68, 0.45) !important; |
|
|
box-shadow: 0 5px 14px rgba(239, 68, 68, 0.45) !important; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ========== 窗口缩放拖拽手柄 ========== */ |
|
|
|
|
|
.csk-resize-handle { |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
right: 4px; |
|
|
|
|
|
bottom: 4px; |
|
|
|
|
|
width: 20px; |
|
|
|
|
|
height: 20px; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: flex-end; |
|
|
|
|
|
justify-content: flex-end; |
|
|
|
|
|
cursor: nwse-resize; |
|
|
|
|
|
z-index: 5; |
|
|
|
|
|
user-select: none; |
|
|
|
|
|
-webkit-user-select: none; |
|
|
|
|
|
/* 圆形半透明背景,与整体圆润风格一致 */ |
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
background: transparent; |
|
|
|
|
|
transition: background 0.2s ease; |
|
|
|
|
|
} |
|
|
|
|
|
.csk-resize-handle:hover { |
|
|
|
|
|
background: rgba(var(--csk-primary-rgb), 0.08); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 三斜线抓手 — 纯 CSS 绘制,用两条间距微调的线,视觉上是三条 */ |
|
|
|
|
|
.csk-resize-grip { |
|
|
|
|
|
display: block; |
|
|
|
|
|
width: 10px; |
|
|
|
|
|
height: 10px; |
|
|
|
|
|
background: |
|
|
|
|
|
linear-gradient(135deg, |
|
|
|
|
|
transparent 0%, transparent 38%, |
|
|
|
|
|
#C8CCD2 38%, #C8CCD2 42%, |
|
|
|
|
|
transparent 42%, transparent 54%, |
|
|
|
|
|
#C8CCD2 54%, #C8CCD2 58%, |
|
|
|
|
|
transparent 58%, transparent 70%, |
|
|
|
|
|
#C8CCD2 70%, #C8CCD2 74%, |
|
|
|
|
|
transparent 74% |
|
|
|
|
|
); |
|
|
|
|
|
transition: background 0.2s ease; |
|
|
|
|
|
border-radius: 2px; |
|
|
|
|
|
} |
|
|
|
|
|
/* hover 时线条变为主色 */ |
|
|
|
|
|
.csk-resize-handle:hover .csk-resize-grip { |
|
|
|
|
|
background: |
|
|
|
|
|
linear-gradient(135deg, |
|
|
|
|
|
transparent 0%, transparent 38%, |
|
|
|
|
|
var(--csk-primary) 38%, var(--csk-primary) 42%, |
|
|
|
|
|
transparent 42%, transparent 54%, |
|
|
|
|
|
var(--csk-primary) 54%, var(--csk-primary) 58%, |
|
|
|
|
|
transparent 58%, transparent 70%, |
|
|
|
|
|
var(--csk-primary) 70%, var(--csk-primary) 74%, |
|
|
|
|
|
transparent 74% |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 暗色模式 */ |
|
|
|
|
|
.csk-dark .csk-resize-grip { |
|
|
|
|
|
background: |
|
|
|
|
|
linear-gradient(135deg, |
|
|
|
|
|
transparent 0%, transparent 38%, |
|
|
|
|
|
#5B6070 38%, #5B6070 42%, |
|
|
|
|
|
transparent 42%, transparent 54%, |
|
|
|
|
|
#5B6070 54%, #5B6070 58%, |
|
|
|
|
|
transparent 58%, transparent 70%, |
|
|
|
|
|
#5B6070 70%, #5B6070 74%, |
|
|
|
|
|
transparent 74% |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
.csk-dark .csk-resize-handle:hover .csk-resize-grip { |
|
|
|
|
|
background: |
|
|
|
|
|
linear-gradient(135deg, |
|
|
|
|
|
transparent 0%, transparent 38%, |
|
|
|
|
|
var(--csk-primary-light) 38%, var(--csk-primary-light) 42%, |
|
|
|
|
|
transparent 42%, transparent 54%, |
|
|
|
|
|
var(--csk-primary-light) 54%, var(--csk-primary-light) 58%, |
|
|
|
|
|
transparent 58%, transparent 70%, |
|
|
|
|
|
var(--csk-primary-light) 70%, var(--csk-primary-light) 74%, |
|
|
|
|
|
transparent 74% |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 缩放拖拽中:禁用过渡动画 + 全局光标 */ |
|
|
|
|
|
.csk-window--resizing { |
|
|
|
|
|
transition: none !important; |
|
|
|
|
|
user-select: none; |
|
|
|
|
|
} |
|
|
|
|
|
.csk-window--resizing * { cursor: nwse-resize !important; } |
|
|
|
|
|
|
|
|
/* ========== 暗色模式 ========== */ |
|
|
/* ========== 暗色模式 ========== */ |
|
|
.csk-root.csk-dark { |
|
|
.csk-root.csk-dark { |
|
|
--csk-bg-ai: #1E1E2E; |
|
|
--csk-bg-ai: #1E1E2E; |
|
|
@ -2372,6 +2488,7 @@ var ChatbotSDK = (function () { |
|
|
left: 0 !important; |
|
|
left: 0 !important; |
|
|
border-radius: 0; |
|
|
border-radius: 0; |
|
|
} |
|
|
} |
|
|
|
|
|
.csk-resize-handle { display: none !important; } |
|
|
.csk-window--hidden { transform: translateY(100%); } |
|
|
.csk-window--hidden { transform: translateY(100%); } |
|
|
/* 移动端默认位置,仅在未被 JS 拖拽覆盖时生效 */ |
|
|
/* 移动端默认位置,仅在未被 JS 拖拽覆盖时生效 */ |
|
|
.csk-launcher:not([style*="bottom"]) { bottom: 20px; } |
|
|
.csk-launcher:not([style*="bottom"]) { bottom: 20px; } |
|
|
@ -2719,6 +2836,13 @@ var ChatbotSDK = (function () { |
|
|
<button class="csk-teaser__close" aria-label="${t('close')}" type="button">×</button> |
|
|
<button class="csk-teaser__close" aria-label="${t('close')}" type="button">×</button> |
|
|
<span>${config.teaserText || t('teaser_text')}</span> |
|
|
<span>${config.teaserText || t('teaser_text')}</span> |
|
|
`;
|
|
|
`;
|
|
|
|
|
|
// === 缩放拖拽手柄(右下角) ===
|
|
|
|
|
|
const resizeHandle = document.createElement('div'); |
|
|
|
|
|
resizeHandle.className = 'csk-resize-handle'; |
|
|
|
|
|
resizeHandle.setAttribute('aria-label', t('resize')); |
|
|
|
|
|
// 纯 CSS 渲染三条斜线,无需 SVG
|
|
|
|
|
|
resizeHandle.innerHTML = '<span class="csk-resize-grip"></span>'; |
|
|
|
|
|
windowEl.appendChild(resizeHandle); |
|
|
return { |
|
|
return { |
|
|
window: windowEl, |
|
|
window: windowEl, |
|
|
messagesContainer, |
|
|
messagesContainer, |
|
|
@ -2730,6 +2854,7 @@ var ChatbotSDK = (function () { |
|
|
welcomeEl, |
|
|
welcomeEl, |
|
|
newMsgBtn, |
|
|
newMsgBtn, |
|
|
teaserEl, |
|
|
teaserEl, |
|
|
|
|
|
resizeHandle, |
|
|
searchInput, |
|
|
searchInput, |
|
|
ariaLiveEl, |
|
|
ariaLiveEl, |
|
|
showLoading, |
|
|
showLoading, |
|
|
@ -2939,6 +3064,100 @@ var ChatbotSDK = (function () { |
|
|
document.removeEventListener('touchend', onTouchEnd); |
|
|
document.removeEventListener('touchend', onTouchEnd); |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
// ==================== 窗口缩放支持 ====================
|
|
|
|
|
|
/** 窗口最小尺寸 */ |
|
|
|
|
|
const MIN_WIDTH = 300; |
|
|
|
|
|
const MIN_HEIGHT = 300; |
|
|
|
|
|
/** |
|
|
|
|
|
* 启用窗口右下角拖拽缩放 |
|
|
|
|
|
* @param handleEl 缩放手柄元素 |
|
|
|
|
|
* @param windowEl 聊天窗口元素 |
|
|
|
|
|
* @param onResizeEnd 缩放结束回调,返回最终宽高用于持久化 |
|
|
|
|
|
* @returns 清理函数 |
|
|
|
|
|
*/ |
|
|
|
|
|
function enableResize(handleEl, windowEl, onResizeEnd) { |
|
|
|
|
|
let resizing = false; |
|
|
|
|
|
let startX = 0; |
|
|
|
|
|
let startY = 0; |
|
|
|
|
|
let startWidth = 0; |
|
|
|
|
|
let startHeight = 0; |
|
|
|
|
|
const onPointerDown = (clientX, clientY) => { |
|
|
|
|
|
resizing = true; |
|
|
|
|
|
startX = clientX; |
|
|
|
|
|
startY = clientY; |
|
|
|
|
|
const rect = windowEl.getBoundingClientRect(); |
|
|
|
|
|
startWidth = rect.width; |
|
|
|
|
|
startHeight = rect.height; |
|
|
|
|
|
windowEl.classList.add('csk-window--resizing'); |
|
|
|
|
|
document.addEventListener('mousemove', onMouseMove); |
|
|
|
|
|
document.addEventListener('mouseup', onMouseUp); |
|
|
|
|
|
document.addEventListener('touchmove', onTouchMove, { passive: false }); |
|
|
|
|
|
document.addEventListener('touchend', onTouchEnd); |
|
|
|
|
|
}; |
|
|
|
|
|
const onPointerMove = (clientX, clientY, e) => { |
|
|
|
|
|
if (!resizing) |
|
|
|
|
|
return; |
|
|
|
|
|
if (e) |
|
|
|
|
|
e.preventDefault(); |
|
|
|
|
|
const dx = clientX - startX; |
|
|
|
|
|
const dy = clientY - startY; |
|
|
|
|
|
const newWidth = Math.max(MIN_WIDTH, Math.min(startWidth + dx, window.innerWidth - 24)); |
|
|
|
|
|
const newHeight = Math.max(MIN_HEIGHT, Math.min(startHeight + dy, window.innerHeight - 24)); |
|
|
|
|
|
windowEl.style.width = `${newWidth}px`; |
|
|
|
|
|
windowEl.style.height = `${newHeight}px`; |
|
|
|
|
|
// 同步更新 CSS 变量(供内部布局使用)
|
|
|
|
|
|
windowEl.style.setProperty('--csk-window-width', `${newWidth}px`); |
|
|
|
|
|
windowEl.style.setProperty('--csk-window-height', `${newHeight}px`); |
|
|
|
|
|
// 拖拽缩放后清除 bottom/right 定位,避免与固定定位冲突
|
|
|
|
|
|
windowEl.style.bottom = ''; |
|
|
|
|
|
windowEl.style.right = ''; |
|
|
|
|
|
}; |
|
|
|
|
|
const onPointerUp = () => { |
|
|
|
|
|
if (!resizing) |
|
|
|
|
|
return; |
|
|
|
|
|
resizing = false; |
|
|
|
|
|
windowEl.classList.remove('csk-window--resizing'); |
|
|
|
|
|
document.removeEventListener('mousemove', onMouseMove); |
|
|
|
|
|
document.removeEventListener('mouseup', onMouseUp); |
|
|
|
|
|
document.removeEventListener('touchmove', onTouchMove); |
|
|
|
|
|
document.removeEventListener('touchend', onTouchEnd); |
|
|
|
|
|
const rect = windowEl.getBoundingClientRect(); |
|
|
|
|
|
if (onResizeEnd) { |
|
|
|
|
|
onResizeEnd({ width: rect.width, height: rect.height }); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
// 鼠标事件(DOM 事件监听器包装)
|
|
|
|
|
|
function onMouseMove(e) { onPointerMove(e.clientX, e.clientY, e); } |
|
|
|
|
|
function onMouseUp() { onPointerUp(); } |
|
|
|
|
|
// 鼠标事件
|
|
|
|
|
|
function onMouseDown(e) { |
|
|
|
|
|
e.preventDefault(); |
|
|
|
|
|
e.stopPropagation(); |
|
|
|
|
|
onPointerDown(e.clientX, e.clientY); |
|
|
|
|
|
} |
|
|
|
|
|
// 触摸事件
|
|
|
|
|
|
function onTouchStart(e) { |
|
|
|
|
|
e.preventDefault(); |
|
|
|
|
|
e.stopPropagation(); |
|
|
|
|
|
if (e.touches.length === 1) |
|
|
|
|
|
onPointerDown(e.touches[0].clientX, e.touches[0].clientY); |
|
|
|
|
|
} |
|
|
|
|
|
function onTouchMove(e) { |
|
|
|
|
|
if (e.touches.length === 1) |
|
|
|
|
|
onPointerMove(e.touches[0].clientX, e.touches[0].clientY, e); |
|
|
|
|
|
} |
|
|
|
|
|
function onTouchEnd() { onPointerUp(); } |
|
|
|
|
|
handleEl.addEventListener('mousedown', onMouseDown); |
|
|
|
|
|
handleEl.addEventListener('touchstart', onTouchStart, { passive: false }); |
|
|
|
|
|
return () => { |
|
|
|
|
|
handleEl.removeEventListener('mousedown', onMouseDown); |
|
|
|
|
|
handleEl.removeEventListener('touchstart', onTouchStart); |
|
|
|
|
|
document.removeEventListener('mousemove', onMouseMove); |
|
|
|
|
|
document.removeEventListener('mouseup', onMouseUp); |
|
|
|
|
|
document.removeEventListener('touchmove', onTouchMove); |
|
|
|
|
|
document.removeEventListener('touchend', onTouchEnd); |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
// ==================== 消息渲染 ====================
|
|
|
// ==================== 消息渲染 ====================
|
|
|
/** 渲染用户消息气泡 */ |
|
|
/** 渲染用户消息气泡 */ |
|
|
function renderUserBubble(container, text, timestamp) { |
|
|
function renderUserBubble(container, text, timestamp) { |
|
|
@ -4388,6 +4607,7 @@ var ChatbotSDK = (function () { |
|
|
let hideLoadingFn = null; |
|
|
let hideLoadingFn = null; |
|
|
let dragCleanup = null; |
|
|
let dragCleanup = null; |
|
|
let launcherDragCleanup = null; |
|
|
let launcherDragCleanup = null; |
|
|
|
|
|
let resizeCleanup = null; |
|
|
/** Launcher 位置记忆状态(拖拽后更新) */ |
|
|
/** Launcher 位置记忆状态(拖拽后更新) */ |
|
|
let launcherPos = null; |
|
|
let launcherPos = null; |
|
|
/** 提示气泡元素 */ |
|
|
/** 提示气泡元素 */ |
|
|
@ -4468,6 +4688,15 @@ var ChatbotSDK = (function () { |
|
|
} |
|
|
} |
|
|
// 9.1 恢复上次拖拽位置
|
|
|
// 9.1 恢复上次拖拽位置
|
|
|
restoreWindowPosition(config.integrateId, windowEl); |
|
|
restoreWindowPosition(config.integrateId, windowEl); |
|
|
|
|
|
// 9.2 启用缩放(resizable 配置控制)
|
|
|
|
|
|
if (config.resizable) { |
|
|
|
|
|
const resizeHandle = dom.resizeHandle; |
|
|
|
|
|
resizeCleanup = enableResize(resizeHandle, windowEl, (size) => { |
|
|
|
|
|
saveWindowSize(config.integrateId, size); |
|
|
|
|
|
}); |
|
|
|
|
|
// 恢复上次缩放的尺寸
|
|
|
|
|
|
restoreWindowSize(config.integrateId, windowEl); |
|
|
|
|
|
} |
|
|
// 10. 初始化对话模块
|
|
|
// 10. 初始化对话模块
|
|
|
initChat(config, { |
|
|
initChat(config, { |
|
|
messagesContainer, |
|
|
messagesContainer, |
|
|
@ -4686,6 +4915,10 @@ var ChatbotSDK = (function () { |
|
|
launcherDragCleanup(); |
|
|
launcherDragCleanup(); |
|
|
launcherDragCleanup = null; |
|
|
launcherDragCleanup = null; |
|
|
} |
|
|
} |
|
|
|
|
|
if (resizeCleanup) { |
|
|
|
|
|
resizeCleanup(); |
|
|
|
|
|
resizeCleanup = null; |
|
|
|
|
|
} |
|
|
launcherPos = null; |
|
|
launcherPos = null; |
|
|
removeStyles(); |
|
|
removeStyles(); |
|
|
document.removeEventListener('keydown', onKeyDown); |
|
|
document.removeEventListener('keydown', onKeyDown); |
|
|
@ -4741,6 +4974,36 @@ var ChatbotSDK = (function () { |
|
|
clearMessages(config.integrateId); |
|
|
clearMessages(config.integrateId); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// ==================== 窗口尺寸记忆 ====================
|
|
|
|
|
|
/** localStorage key:窗口尺寸 */ |
|
|
|
|
|
function sizeKey(integrateId) { |
|
|
|
|
|
return `csk_size_${integrateId}`; |
|
|
|
|
|
} |
|
|
|
|
|
/** 保存窗口缩放的尺寸到 localStorage */ |
|
|
|
|
|
function saveWindowSize(integrateId, size) { |
|
|
|
|
|
try { |
|
|
|
|
|
localStorage.setItem(sizeKey(integrateId), JSON.stringify(size)); |
|
|
|
|
|
} |
|
|
|
|
|
catch ( /* 忽略 */_a) { /* 忽略 */ } |
|
|
|
|
|
} |
|
|
|
|
|
/** 恢复窗口缩放尺寸 */ |
|
|
|
|
|
function restoreWindowSize(integrateId, winEl) { |
|
|
|
|
|
try { |
|
|
|
|
|
const raw = localStorage.getItem(sizeKey(integrateId)); |
|
|
|
|
|
if (!raw) |
|
|
|
|
|
return; |
|
|
|
|
|
const size = JSON.parse(raw); |
|
|
|
|
|
if (typeof size.width !== 'number' || typeof size.height !== 'number') |
|
|
|
|
|
return; |
|
|
|
|
|
const w = Math.max(300, Math.min(size.width, window.innerWidth - 24)); |
|
|
|
|
|
const h = Math.max(300, Math.min(size.height, window.innerHeight - 24)); |
|
|
|
|
|
winEl.style.width = `${w}px`; |
|
|
|
|
|
winEl.style.height = `${h}px`; |
|
|
|
|
|
winEl.style.setProperty('--csk-window-width', `${w}px`); |
|
|
|
|
|
winEl.style.setProperty('--csk-window-height', `${h}px`); |
|
|
|
|
|
} |
|
|
|
|
|
catch ( /* 忽略 */_a) { /* 忽略 */ } |
|
|
|
|
|
} |
|
|
// ==================== 窗口位置记忆 ====================
|
|
|
// ==================== 窗口位置记忆 ====================
|
|
|
/** localStorage key:窗口位置 */ |
|
|
/** localStorage key:窗口位置 */ |
|
|
function positionKey(integrateId) { |
|
|
function positionKey(integrateId) { |
|
|
|