Add files via upload

This commit is contained in:
Cola-Echo
2025-12-22 02:41:32 +08:00
committed by GitHub
commit 1e1bf1bab2
30 changed files with 34640 additions and 0 deletions

214
phone.js Normal file
View File

@@ -0,0 +1,214 @@
/**
* 手机面板:显示/隐藏、自动居中、拖拽定位
*/
import { saveSettingsDebounced } from '../../../../script.js';
import { getSettings } from './config.js';
import { getCurrentTime } from './utils.js';
let phoneAutoCenteringBound = false;
let phoneManuallyPositioned = false;
export function centerPhoneInViewport({ force = false } = {}) {
const phone = document.getElementById('wechat-phone');
if (!phone) return;
if (!force && phone.classList.contains('hidden')) return;
const settings = getSettings();
// 用户手动拖拽后,不再自动居中(除非 force
if (phoneManuallyPositioned && settings.phonePosition && !force) return;
// 有保存位置则优先使用
if (settings.phonePosition && !force) {
phone.style.setProperty('left', `${settings.phonePosition.x}px`, 'important');
phone.style.setProperty('top', `${settings.phonePosition.y}px`, 'important');
phoneManuallyPositioned = true;
return;
}
const viewport = window.visualViewport;
const rawViewportWidth = viewport?.width ?? window.innerWidth;
const rawViewportHeight = viewport?.height ?? window.innerHeight;
const viewportWidth = rawViewportWidth >= 100 ? rawViewportWidth : window.innerWidth;
const viewportHeight = rawViewportHeight >= 100 ? rawViewportHeight : window.innerHeight;
const viewportLeft = viewport?.offsetLeft ?? 0;
const viewportTop = viewport?.offsetTop ?? 0;
const isCoarsePointer = window.matchMedia?.('(pointer: coarse)')?.matches ?? false;
const maxWidth = isCoarsePointer ? 360 : 375;
const maxHeight = isCoarsePointer ? 700 : 667;
const margin = isCoarsePointer ? 8 : 12;
const availableWidth = Math.max(0, Math.floor(viewportWidth - margin * 2));
const availableHeight = Math.max(0, Math.floor(viewportHeight - margin * 2));
const targetWidth = Math.min(maxWidth, availableWidth);
const targetHeight = Math.min(maxHeight, availableHeight);
if (targetWidth > 0) phone.style.setProperty('width', `${targetWidth}px`, 'important');
if (targetHeight > 0) phone.style.setProperty('height', `${targetHeight}px`, 'important');
phone.style.setProperty('max-width', 'none', 'important');
phone.style.setProperty('max-height', 'none', 'important');
const effectiveWidth = targetWidth > 0 ? targetWidth : phone.getBoundingClientRect().width;
const effectiveHeight = targetHeight > 0 ? targetHeight : phone.getBoundingClientRect().height;
const unclampedCenterX = viewportLeft + viewportWidth / 2;
const unclampedCenterY = viewportTop + viewportHeight / 2;
const minCenterX = viewportLeft + margin + effectiveWidth / 2;
const maxCenterX = viewportLeft + viewportWidth - margin - effectiveWidth / 2;
const minCenterY = viewportTop + margin + effectiveHeight / 2;
const maxCenterY = viewportTop + viewportHeight - margin - effectiveHeight / 2;
const centerX = Math.round(Math.min(Math.max(unclampedCenterX, minCenterX), maxCenterX));
const centerY = Math.round(Math.min(Math.max(unclampedCenterY, minCenterY), maxCenterY));
phone.style.setProperty('left', `${centerX}px`, 'important');
phone.style.setProperty('top', `${centerY}px`, 'important');
phone.style.setProperty('right', 'auto', 'important');
phone.style.setProperty('bottom', 'auto', 'important');
}
export function setupPhoneDrag() {
const phone = document.getElementById('wechat-phone');
if (!phone) return;
let isDragging = false;
let startX = 0;
let startY = 0;
let initialX = 0;
let initialY = 0;
const statusbar = phone.querySelector('.wechat-statusbar');
if (!statusbar) return;
statusbar.style.cursor = 'grab';
statusbar.title = '拖拽移动手机位置';
const handleStart = (e) => {
if (e.target.closest('button') || e.target.closest('a')) return;
isDragging = true;
statusbar.style.cursor = 'grabbing';
const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
startX = clientX;
startY = clientY;
const rect = phone.getBoundingClientRect();
initialX = rect.left + rect.width / 2;
initialY = rect.top + rect.height / 2;
e.preventDefault();
};
const handleMove = (e) => {
if (!isDragging) return;
const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
const deltaX = clientX - startX;
const deltaY = clientY - startY;
const newX = initialX + deltaX;
const newY = initialY + deltaY;
phone.style.setProperty('left', `${newX}px`, 'important');
phone.style.setProperty('top', `${newY}px`, 'important');
e.preventDefault();
};
const handleEnd = () => {
if (!isDragging) return;
isDragging = false;
statusbar.style.cursor = 'grab';
phoneManuallyPositioned = true;
const rect = phone.getBoundingClientRect();
const settings = getSettings();
settings.phonePosition = {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2,
};
saveSettingsDebounced();
};
statusbar.addEventListener('mousedown', handleStart);
document.addEventListener('mousemove', handleMove);
document.addEventListener('mouseup', handleEnd);
statusbar.addEventListener('touchstart', handleStart, { passive: false });
document.addEventListener('touchmove', handleMove, { passive: false });
document.addEventListener('touchend', handleEnd);
statusbar.addEventListener('dblclick', () => {
phoneManuallyPositioned = false;
const settings = getSettings();
delete settings.phonePosition;
saveSettingsDebounced();
centerPhoneInViewport({ force: true });
});
}
export function setupPhoneAutoCentering() {
if (phoneAutoCenteringBound) return;
phoneAutoCenteringBound = true;
let rafPending = false;
const handler = () => {
if (rafPending) return;
rafPending = true;
requestAnimationFrame(() => {
rafPending = false;
centerPhoneInViewport();
});
};
window.addEventListener('resize', handler);
window.addEventListener('orientationchange', handler);
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', handler);
window.visualViewport.addEventListener('scroll', handler);
}
const phone = document.getElementById('wechat-phone');
phone?.addEventListener('focusin', () => {
centerPhoneInViewport({ force: true });
setTimeout(() => centerPhoneInViewport({ force: true }), 250);
if (document.activeElement?.id === 'wechat-input') {
const messages = document.getElementById('wechat-chat-messages');
if (messages) messages.scrollTop = messages.scrollHeight;
}
});
phone?.addEventListener('focusout', () => {
setTimeout(() => centerPhoneInViewport({ force: true }), 250);
});
setTimeout(() => centerPhoneInViewport({ force: true }), 0);
}
export function togglePhone() {
const phone = document.getElementById('wechat-phone');
if (!phone) return;
const settings = getSettings();
phone.classList.toggle('hidden');
settings.phoneVisible = !phone.classList.contains('hidden');
saveSettingsDebounced();
if (settings.phoneVisible) {
const timeEl = document.querySelector('.wechat-statusbar-time');
if (timeEl) timeEl.textContent = getCurrentTime();
centerPhoneInViewport();
setTimeout(() => centerPhoneInViewport({ force: true }), 150);
}
}