Files
ST-Amily2-Chat-Optimisation/PresetSettings/draggable.js
Jenkins CI 2c3072a3d8 release: v2.2.2 [2026-05-27 11:10:55]
### 新功能
- **Function Call 填表模式**:在填表设置中新增独立开关,启用后支持通过 OpenAI 兼容接口(DeepSeek / OpenRouter / 各类中转等)直接返回结构化操作列表,绕过 `<Amily2Edit>` 文本解析路径,填表更稳定
  - 遇到不支持 `tool_choice` 的接口时自动降级重试
  - 对思考模型注入强制调用指令,防止绕过工具直接输出文本
  - 全部走 ST 后端代理,修复 CSP 拦截直连外部 URL 的问题
- **主界面新增提示词链编辑器入口**,同时调换了记忆管理与角色世界书的按钮位置
- **规则中心**新增"自动排除用户楼层"选项
### 修复
- 提示词链按钮点击无响应(改为事件委托方式绑定)
- 拖拽组件微抖误触发(加 5px 移动阈值过滤)
- 填表检查窗若干问题修复;翰林院(批量回填)修复;防抖逻辑落地
- 角色世界书入口添加使用警告弹窗(强制 10 秒倒计时),提示该功能长期未维护
- ApiProfile `fakeStream` 字段保存丢失问题
- 正文优化默认改为关闭状态
- NGMS / NCCS API 配置槽位标签修正(NGMS→总结,NCCS→填表)
- API Profile 面板选择逻辑统一重构,修复多处旧字段覆盖新配置的问题
- 世界书控制参数兼容性修复(排除递归、插入位置、扫描深度等,适配 ST 1.17.0+)
2026-05-27 11:10:55 +08:00

166 lines
4.9 KiB
JavaScript

export function makeDraggable($element, onClick, storageKey) {
let isDragging = false;
let hasDragged = false;
let startPos = { x: 0, y: 0 };
let elementStartPos = { x: 0, y: 0 };
const getEventCoords = (e) => {
if (e.touches && e.touches.length > 0) {
return { x: e.touches[0].clientX, y: e.touches[0].clientY };
} else if (e.changedTouches && e.changedTouches.length > 0) {
return { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY };
}
return { x: e.clientX, y: e.clientY };
};
const keepInBounds = ($elem) => {
const windowWidth = $(window).width();
const windowHeight = $(window).height();
const elemWidth = $elem.outerWidth();
const elemHeight = $elem.outerHeight();
let currentPos = $elem.offset();
let newLeft = Math.max(0, Math.min(currentPos.left, windowWidth - elemWidth));
let newTop = Math.max(0, Math.min(currentPos.top, windowHeight - elemHeight));
$elem.css({
left: newLeft + 'px',
top: newTop + 'px',
transform: 'none'
});
if (storageKey) {
localStorage.setItem(storageKey, JSON.stringify({
left: newLeft + 'px',
top: newTop + 'px'
}));
}
};
const dragStart = (e) => {
e.preventDefault();
isDragging = true;
hasDragged = false;
const coords = getEventCoords(e.originalEvent || e);
startPos = { x: coords.x, y: coords.y };
const offset = $element.offset();
elementStartPos = { x: offset.left, y: offset.top };
$element.css({
'cursor': 'grabbing',
'transition': 'none'
});
$('body').css({
'user-select': 'none',
'-webkit-user-select': 'none',
'overflow': 'hidden'
});
};
const DRAG_THRESHOLD = 5;
const dragMove = (e) => {
if (!isDragging) return;
e.preventDefault();
if (!hasDragged) {
const coords = getEventCoords(e.originalEvent || e);
const dist = Math.abs(coords.x - startPos.x) + Math.abs(coords.y - startPos.y);
if (dist < DRAG_THRESHOLD) return;
}
hasDragged = true;
const coords = getEventCoords(e.originalEvent || e);
const deltaX = coords.x - startPos.x;
const deltaY = coords.y - startPos.y;
let newLeft = elementStartPos.x + deltaX;
let newTop = elementStartPos.y + deltaY;
const windowWidth = $(window).width();
const windowHeight = $(window).height();
const elemWidth = $element.outerWidth();
const elemHeight = $element.outerHeight();
newLeft = Math.max(0, Math.min(newLeft, windowWidth - elemWidth));
newTop = Math.max(0, Math.min(newTop, windowHeight - elemHeight));
$element.css({
left: newLeft + 'px',
top: newTop + 'px',
transform: 'none'
});
};
const dragEnd = (e) => {
if (!isDragging) return;
isDragging = false;
$element.css({
'cursor': 'grab',
'transition': 'transform 0.2s ease, box-shadow 0.2s ease'
});
$('body').css({
'user-select': 'auto',
'-webkit-user-select': 'auto',
'overflow': 'auto'
});
keepInBounds($element);
if (!hasDragged && onClick) {
if (e.type === 'touchend') {
e.preventDefault();
setTimeout(onClick, 10);
} else {
onClick();
}
}
};
$element.on('mousedown', dragStart);
$element.on('touchstart', dragStart);
const namespace = '.draggable' + Date.now();
$(document).on(`mousemove${namespace}`, dragMove);
$(document).on(`touchmove${namespace}`, dragMove);
$(document).on(`mouseup${namespace}`, dragEnd);
$(document).on(`touchend${namespace}`, dragEnd);
$element.on('click', (e) => {
if (hasDragged) {
e.preventDefault();
e.stopPropagation();
}
});
$(window).on(`resize${namespace}`, () => {
if ($element.length) {
keepInBounds($element);
}
});
$element.css({
'cursor': 'grab',
'user-select': 'none',
'-webkit-user-select': 'none'
});
if (storageKey) {
const savedPos = localStorage.getItem(storageKey);
if (savedPos) {
$element.css(JSON.parse(savedPos));
setTimeout(() => keepInBounds($element), 0);
}
}
return () => {
$element.off('mousedown touchstart click');
$(document).off(namespace);
$(window).off(namespace);
};
}