init: AI Grading Assistant MVP
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import { BrowserWindow, desktopCapturer, screen } from 'electron'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import log from 'electron-log'
|
||||
|
||||
let screenshotWindow: BrowserWindow | null = null
|
||||
let capturedImage: Buffer | null = null
|
||||
let capturedBase64: string | null = null
|
||||
let isScreenshotActive = false
|
||||
let lastScreenshotTime = 0
|
||||
const SCREENSHOT_COOLDOWN = 2000
|
||||
|
||||
export function startScreenshot(mainWin: BrowserWindow): void {
|
||||
const now = Date.now()
|
||||
if (now - lastScreenshotTime < SCREENSHOT_COOLDOWN) {
|
||||
log.warn('Screenshot on cooldown, ignoring')
|
||||
return
|
||||
}
|
||||
if (isScreenshotActive) {
|
||||
log.warn('Screenshot already active, ignoring')
|
||||
return
|
||||
}
|
||||
isScreenshotActive = true
|
||||
lastScreenshotTime = now
|
||||
require('./shortcut').disableShortcut()
|
||||
mainWin.hide()
|
||||
|
||||
const cursor = screen.getCursorScreenPoint()
|
||||
const displays = screen.getAllDisplays()
|
||||
const targetDisplay = displays.find(d => {
|
||||
return cursor.x >= d.bounds.x && cursor.x <= d.bounds.x + d.bounds.width &&
|
||||
cursor.y >= d.bounds.y && cursor.y <= d.bounds.y + d.bounds.height
|
||||
}) || displays[0]
|
||||
|
||||
const bounds = targetDisplay.bounds
|
||||
|
||||
screenshotWindow = new BrowserWindow({
|
||||
x: bounds.x,
|
||||
y: bounds.y,
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
frame: false,
|
||||
transparent: true,
|
||||
alwaysOnTop: true,
|
||||
skipTaskbar: true,
|
||||
resizable: false,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false
|
||||
}
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV === 'development' || process.argv.includes('--dev')) {
|
||||
screenshotWindow.loadURL(`http://localhost:5173/#/screenshot`)
|
||||
} else {
|
||||
screenshotWindow.loadFile(path.join(__dirname, '../../dist/index.html'), { hash: '/screenshot' })
|
||||
}
|
||||
|
||||
screenshotWindow.on('closed', () => {
|
||||
isScreenshotActive = false
|
||||
screenshotWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
export async function captureScreen(region: { x: number; y: number; width: number; height: number }): Promise<string | null> {
|
||||
try {
|
||||
const displays = screen.getAllDisplays()
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ['screen'],
|
||||
thumbnailSize: { width: 4096, height: 2160 }
|
||||
})
|
||||
|
||||
if (sources.length === 0) return null
|
||||
|
||||
const cursor = screen.getCursorScreenPoint()
|
||||
const targetDisplay = displays.find(d => {
|
||||
return cursor.x >= d.bounds.x && cursor.x <= d.bounds.x + d.bounds.width &&
|
||||
cursor.y >= d.bounds.y && cursor.y <= d.bounds.y + d.bounds.height
|
||||
}) || displays[0]
|
||||
|
||||
const sourceIndex = displays.findIndex(d => d.id === targetDisplay.id)
|
||||
const source = sources[Math.min(sourceIndex, sources.length - 1)]
|
||||
|
||||
const fullImage = source.thumbnail
|
||||
const displayWidth = targetDisplay.bounds.width
|
||||
const displayHeight = targetDisplay.bounds.height
|
||||
const scaleX = fullImage.getSize().width / displayWidth
|
||||
const scaleY = fullImage.getSize().height / displayHeight
|
||||
|
||||
const cropX = Math.round(region.x * scaleX)
|
||||
const cropY = Math.round(region.y * scaleY)
|
||||
const cropW = Math.round(region.width * scaleX)
|
||||
const cropH = Math.round(region.height * scaleY)
|
||||
|
||||
const cropped = fullImage.crop({
|
||||
x: Math.max(0, cropX),
|
||||
y: Math.max(0, cropY),
|
||||
width: Math.min(cropW, fullImage.getSize().width - cropX),
|
||||
height: Math.min(cropH, fullImage.getSize().height - cropY)
|
||||
})
|
||||
|
||||
const pngBuffer = cropped.toPNG()
|
||||
const base64 = pngBuffer.toString('base64')
|
||||
capturedImage = pngBuffer
|
||||
capturedBase64 = base64
|
||||
|
||||
try {
|
||||
const Store = require('electron-store').default
|
||||
const store = new Store()
|
||||
const savePath = store.get('screenshotSavePath', '')
|
||||
if (savePath) {
|
||||
const timestamp = Date.now()
|
||||
const filename = `screenshot_${timestamp}.png`
|
||||
const fullPath = path.join(savePath, filename)
|
||||
fs.writeFileSync(fullPath, pngBuffer)
|
||||
log.info('Screenshot saved to', fullPath)
|
||||
}
|
||||
} catch (e) {
|
||||
log.error('Failed to save screenshot:', e)
|
||||
}
|
||||
|
||||
log.info('Screenshot captured', { width: region.width, height: region.height })
|
||||
return base64
|
||||
} catch (err) {
|
||||
log.error('Screenshot capture error:', err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function closeScreenshot(): void {
|
||||
if (screenshotWindow && !screenshotWindow.isDestroyed()) {
|
||||
screenshotWindow.close()
|
||||
if (!screenshotWindow.isDestroyed()) {
|
||||
screenshotWindow.destroy()
|
||||
}
|
||||
}
|
||||
screenshotWindow = null
|
||||
isScreenshotActive = false
|
||||
}
|
||||
|
||||
export function getCapturedImage(): Buffer | null {
|
||||
return capturedImage
|
||||
}
|
||||
|
||||
export function getCapturedBase64(): string | null {
|
||||
return capturedBase64
|
||||
}
|
||||
|
||||
export function clearCapturedImage(): void {
|
||||
capturedImage = null
|
||||
capturedBase64 = null
|
||||
}
|
||||
Reference in New Issue
Block a user