优化截图界面

This commit is contained in:
Gnoloah
2025-09-12 18:08:42 +08:00
parent 4921d98d0a
commit 43e760c213
3 changed files with 272 additions and 57 deletions

View File

@@ -40,6 +40,7 @@ class SnapSolver {
// Crop elements
this.cropCancel = document.getElementById('cropCancel');
this.cropConfirm = document.getElementById('cropConfirm');
this.cropSendToAI = document.getElementById('cropSendToAI');
// 初始化应用
this.initialize();
@@ -65,6 +66,7 @@ class SnapSolver {
this.cropContainer = document.getElementById('cropContainer');
this.cropCancel = document.getElementById('cropCancel');
this.cropConfirm = document.getElementById('cropConfirm');
this.cropSendToAI = document.getElementById('cropSendToAI');
this.stopGenerationBtn = document.getElementById('stopGenerationBtn');
// 处理按钮事件
@@ -981,13 +983,38 @@ class SnapSolver {
}
setupCropEvents() {
// 移除裁剪按钮的点击事件监听
// 防止重复绑定事件监听
if (this.cropConfirm) {
const newCropConfirm = this.cropConfirm.cloneNode(true);
this.cropConfirm.parentNode.replaceChild(newCropConfirm, this.cropConfirm);
this.cropConfirm = newCropConfirm;
}
if (this.cropCancel) {
const newCropCancel = this.cropCancel.cloneNode(true);
this.cropCancel.parentNode.replaceChild(newCropCancel, this.cropCancel);
this.cropCancel = newCropCancel;
}
const cropResetElement = document.getElementById('cropReset');
if (cropResetElement) {
const newCropReset = cropResetElement.cloneNode(true);
cropResetElement.parentNode.replaceChild(newCropReset, cropResetElement);
}
if (this.cropSendToAI) {
const newCropSendToAI = this.cropSendToAI.cloneNode(true);
this.cropSendToAI.parentNode.replaceChild(newCropSendToAI, this.cropSendToAI);
this.cropSendToAI = newCropSendToAI;
}
console.log('DEBUG: 已清除裁剪按钮上的事件监听器,防止重复绑定');
// 存储裁剪框数据
this.lastCropBoxData = null;
// Crop confirm button
document.getElementById('cropConfirm').addEventListener('click', () => {
this.cropConfirm.addEventListener('click', () => {
if (!this.checkConnectionBeforeAction()) return;
if (this.cropper) {
@@ -1058,21 +1085,7 @@ class SnapSolver {
window.uiManager.showToast('裁剪成功');
// 获取当前模型信息
const settings = window.settingsManager.getSettings();
const supportsMultimodal = settings.modelInfo?.supportsMultimodal || false;
// 如果模型支持多模态自动发送至AI
if (supportsMultimodal) {
setTimeout(() => {
// 显示Claude分析面板
this.claudePanel.classList.remove('hidden');
this.emptyState.classList.add('hidden');
// 发送图像到Claude进行分析
this.sendImageToClaude(this.croppedImage);
}, 500); // 短暂延迟以确保UI更新
}
// 不再自动发送至AI由用户手动选择
} catch (error) {
console.error('Cropping error details:', {
message: error.message,
@@ -1091,7 +1104,7 @@ class SnapSolver {
});
// Crop cancel button
document.getElementById('cropCancel').addEventListener('click', () => {
this.cropCancel.addEventListener('click', () => {
if (this.cropper) {
this.cropper.destroy();
this.cropper = null;
@@ -1103,22 +1116,163 @@ class SnapSolver {
});
// Crop reset button
document.getElementById('cropReset').addEventListener('click', () => {
const cropResetBtn = document.getElementById('cropReset');
if (cropResetBtn) {
cropResetBtn.addEventListener('click', () => {
if (this.cropper) {
// 重置裁剪区域到默认状态
this.cropper.reset();
window.uiManager.showToast('已重置裁剪区域');
}
});
}
// Crop send to AI button
this.cropSendToAI.addEventListener('click', () => {
if (!this.checkConnectionBeforeAction()) return;
// 如果有裁剪器,尝试获取裁剪结果;否则使用原始图片
if (this.cropper) {
// 重置裁剪区域到默认状态
this.cropper.reset();
window.uiManager.showToast('已重置裁剪区域');
try {
console.log('Starting crop and send operation...');
// Validate cropper instance
if (!this.cropper) {
throw new Error('Cropper not initialized');
}
// Get and validate crop box data
const cropBoxData = this.cropper.getCropBoxData();
console.log('Crop box data:', cropBoxData);
if (!cropBoxData || typeof cropBoxData.width !== 'number' || typeof cropBoxData.height !== 'number') {
throw new Error('Invalid crop box data');
}
if (cropBoxData.width < 10 || cropBoxData.height < 10) {
throw new Error('Crop area is too small. Please select a larger area (minimum 10x10 pixels).');
}
// Get cropped canvas
console.log('Getting cropped canvas...');
const canvas = this.cropper.getCroppedCanvas({
maxWidth: 2560,
maxHeight: 1440,
fillColor: '#fff',
imageSmoothingEnabled: true,
imageSmoothingQuality: 'high',
});
if (!canvas) {
throw new Error('Failed to create cropped canvas');
}
console.log('Canvas created successfully');
// Convert to data URL
console.log('Converting to data URL...');
try {
this.croppedImage = canvas.toDataURL('image/png');
console.log('Data URL conversion successful');
} catch (dataUrlError) {
console.error('Data URL conversion error:', dataUrlError);
throw new Error('Failed to process cropped image. The image might be too large or memory insufficient.');
}
// Clean up cropper and update UI
this.cropper.destroy();
this.cropper = null;
this.cropContainer.classList.add('hidden');
document.querySelector('.crop-area').innerHTML = '';
// Update the screenshot image with the cropped version
this.screenshotImg.src = this.croppedImage;
this.imagePreview.classList.remove('hidden');
// 根据当前选择的模型类型决定显示哪些按钮
this.updateImageActionButtons();
// 显示Claude分析面板
this.claudePanel.classList.remove('hidden');
this.emptyState.classList.add('hidden');
// 发送图像到Claude进行分析
this.sendImageToClaude(this.croppedImage);
window.uiManager.showToast('正在发送至AI分析...');
} catch (error) {
console.error('Crop and send error details:', {
message: error.message,
stack: error.stack,
cropperState: this.cropper ? 'initialized' : 'not initialized'
});
window.uiManager.showToast(error.message || '处理图像时出错', 'error');
// Clean up on error
if (this.cropper) {
this.cropper.destroy();
this.cropper = null;
}
this.cropContainer.classList.add('hidden');
document.querySelector('.crop-area').innerHTML = '';
}
} else if (this.originalImage) {
// 如果没有裁剪器但有原始图片,直接发送原始图片
try {
// 隐藏裁剪容器
this.cropContainer.classList.add('hidden');
// 显示Claude分析面板
this.claudePanel.classList.remove('hidden');
this.emptyState.classList.add('hidden');
// 发送原始图像到Claude进行分析
this.sendImageToClaude(this.originalImage);
window.uiManager.showToast('正在发送至AI分析...');
} catch (error) {
console.error('Send original image error:', error);
window.uiManager.showToast('发送图片失败: ' + error.message, 'error');
}
} else {
window.uiManager.showToast('请先截图', 'error');
}
});
}
setupAnalysisEvents() {
// 防止重复绑定事件监听器
if (this.extractTextBtn) {
const newExtractBtn = this.extractTextBtn.cloneNode(true);
this.extractTextBtn.parentNode.replaceChild(newExtractBtn, this.extractTextBtn);
this.extractTextBtn = newExtractBtn;
}
if (this.sendExtractedTextBtn) {
const newSendBtn = this.sendExtractedTextBtn.cloneNode(true);
this.sendExtractedTextBtn.parentNode.replaceChild(newSendBtn, this.sendExtractedTextBtn);
this.sendExtractedTextBtn = newSendBtn;
}
if (this.sendToClaudeBtn) {
const newClaudeBtn = this.sendToClaudeBtn.cloneNode(true);
this.sendToClaudeBtn.parentNode.replaceChild(newClaudeBtn, this.sendToClaudeBtn);
this.sendToClaudeBtn = newClaudeBtn;
}
console.log('DEBUG: 已清除分析按钮上的事件监听器,防止重复绑定');
// Extract Text button
this.extractTextBtn.addEventListener('click', () => {
if (!this.checkConnectionBeforeAction()) return;
if (!this.croppedImage) {
window.uiManager.showToast('请先裁剪图片', 'error');
// 优先使用裁剪后的图片,如果没有则使用原始截图
const imageToExtract = this.croppedImage || this.originalImage;
if (!imageToExtract) {
window.uiManager.showToast('请先截图', 'error');
return;
}
@@ -1171,7 +1325,7 @@ class SnapSolver {
try {
this.socket.emit('extract_text', {
image: this.croppedImage.split(',')[1],
image: imageToExtract.split(',')[1],
settings: {
ocrSource: settings.ocrSource || 'auto'
}
@@ -1216,13 +1370,6 @@ class SnapSolver {
console.log("Debug - 发送文本分析API密钥:", apiKeys);
// 清空之前的结果
this.responseContent.innerHTML = '';
this.thinkingContent.innerHTML = '';
// 显示Claude分析面板
this.claudePanel.classList.remove('hidden');
// 禁用按钮防止重复点击
this.sendExtractedTextBtn.disabled = true;
@@ -1266,6 +1413,7 @@ class SnapSolver {
return;
}
// 只发送裁剪后的图片,如果没有裁剪过则提示用户先裁剪
if (this.croppedImage) {
try {
// 清空之前的结果
@@ -1467,7 +1615,6 @@ class SnapSolver {
// 初始化UI元素和事件处理
this.initializeElements();
this.setupSocketEventHandlers();
// 设置所有事件监听器注意setupEventListeners内部已经调用了setupCaptureEvents不需要重复调用
this.setupEventListeners();
@@ -1648,10 +1795,13 @@ class SnapSolver {
this.updateConnectionStatus('重连失败', false);
window.uiManager.showToast('连接服务器失败,请刷新页面重试', 'error');
});
// 设置socket事件处理器
this.setupSocketEventHandlers();
}
isConnected() {
return this.connectionStatus && this.connectionStatus.classList.contains('connected');
return this.socket && this.socket.connected;
}
checkConnectionBeforeAction(action) {

View File

@@ -1709,18 +1709,50 @@ button:disabled {
z-index: 10;
background-color: rgba(0, 0, 0, 0.7);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
gap: 1rem;
}
.crop-actions-top {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.7);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
.crop-actions-top button {
min-height: 44px;
white-space: nowrap;
}
.crop-bottom-buttons {
display: flex;
gap: 0;
min-width: 120px;
max-width: 150px;
}
.crop-half-btn {
flex: 1;
border-radius: 0;
margin: 0;
white-space: nowrap;
min-height: 44px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
}
.crop-half-btn:first-child {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
border-right: 1px solid rgba(255, 255, 255, 0.2);
}
.crop-half-btn:last-child {
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
/* Animations */
@keyframes toast-in {
from {
@@ -5692,3 +5724,38 @@ textarea,
font-weight: normal;
margin-left: 5px;
}
/* 移动设备横屏模式优化 */
@media screen and (max-height: 500px) and (orientation: landscape) {
.crop-actions-top {
padding: 0.5rem;
gap: 0.5rem;
}
.crop-actions-top button {
padding: 0.5rem 0.75rem;
font-size: 0.85rem;
}
.crop-actions-top button span {
display: none;
}
.crop-actions-top button i {
margin: 0;
}
}
/* 针对极小高度的横屏设备 */
@media screen and (max-height: 400px) and (orientation: landscape) {
.crop-actions-top {
padding: 0.25rem;
gap: 0.25rem;
}
.crop-actions-top button {
padding: 0.25rem 0.5rem;
font-size: 0.8rem;
min-height: auto;
}
}

View File

@@ -808,27 +808,25 @@
</main>
<div id="cropContainer" class="crop-container hidden">
<div class="crop-actions crop-actions-top">
<div class="crop-actions crop-actions-top">
<button id="cropCancel" class="btn-secondary">
<i class="fas fa-times"></i>
<span>取消</span>
</button>
<button id="cropReset" class="btn-secondary">
<i class="fas fa-undo"></i>
<span>重置</span>
<div class="crop-bottom-buttons">
<button id="cropReset" class="btn-secondary crop-half-btn">
<i class="fas fa-undo"></i>
<span>重置</span>
</button>
<button id="cropConfirm" class="btn-secondary crop-half-btn">
<i class="fas fa-check"></i>
<span>确认</span>
</button>
</div>
<button id="cropSendToAI" class="btn-primary">
<i class="fas fa-paper-plane"></i>
<span>发送</span>
</button>
<button id="cropReset" class="btn-secondary">
<i class="fas fa-undo"></i>
<span>重置</span>
</button>
<button id="cropConfirm" class="btn-primary">
<i class="fas fa-check"></i>
<span>确认</span>
</button>
</div>
<div class="crop-wrapper">
<div class="crop-area"></div>
</div>
<div class="crop-wrapper">
<div class="crop-area"></div>