0
0
Fork 0
This repository has been archived on 2026-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
Classroom-Assistant/main.js

380 lines
9.0 KiB
JavaScript

const { app, BrowserWindow, ipcMain, shell } = require('electron')
const path = require('path')
const fs = require('fs')
const ini = require('ini')
if (process.platform === 'win32') {
app.commandLine.appendSwitch('disable-gpu')
app.commandLine.appendSwitch('disable-software-rasterizer')
app.commandLine.appendSwitch('disable-gpu-compositing')
}
let mainWindow
const portableDir = process.env.PORTABLE_EXECUTABLE_DIR
const configDir = portableDir
? path.join(portableDir, 'data')
: path.join(app.getPath('appData'), 'classroom-assistant', 'ca')
const configPath = path.join(configDir, 'memory.ini')
const settingsPath = path.join(configDir, 'setting.ini')
const goodRandomPath = path.join(configDir, 'goodrandom.json')
const defaultConfig = {
Random: {
MaxNumber: 75
},
Time: {
LastSeconds: 300
}
}
const defaultSettings = {
Timer: {
FillMode: 'last',
CustomSeconds: 300
},
Random: {
SkipAnimation: false,
SmartMatch: true
}
}
function ensureConfigDir() {
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true })
}
}
function loadConfig() {
ensureConfigDir()
if (!fs.existsSync(configPath)) {
saveConfig(defaultConfig)
return defaultConfig
}
try {
const content = fs.readFileSync(configPath, 'utf-8')
const config = ini.parse(content)
if (!config.Random) {
config.Random = defaultConfig.Random
} else {
config.Random.MaxNumber = parseInt(config.Random.MaxNumber) || defaultConfig.Random.MaxNumber
}
if (!config.Time) {
config.Time = defaultConfig.Time
} else {
config.Time.LastSeconds = parseInt(config.Time.LastSeconds) || defaultConfig.Time.LastSeconds
}
saveConfig(config)
return config
} catch (e) {
saveConfig(defaultConfig)
return defaultConfig
}
}
function saveConfig(config) {
ensureConfigDir()
fs.writeFileSync(configPath, ini.stringify(config), 'utf-8')
}
function loadSettings() {
ensureConfigDir()
if (!fs.existsSync(settingsPath)) {
saveSettings(defaultSettings)
return defaultSettings
}
try {
const content = fs.readFileSync(settingsPath, 'utf-8')
const settings = ini.parse(content)
if (!settings.Timer) {
settings.Timer = defaultSettings.Timer
} else {
settings.Timer.FillMode = settings.Timer.FillMode || 'last'
settings.Timer.CustomSeconds = parseInt(settings.Timer.CustomSeconds) || defaultSettings.Timer.CustomSeconds
}
if (!settings.Random) {
settings.Random = defaultSettings.Random
} else {
settings.Random.SkipAnimation = settings.Random.SkipAnimation === 'true' || settings.Random.SkipAnimation === true
settings.Random.SmartMatch = settings.Random.SmartMatch === 'true' || settings.Random.SmartMatch === true || settings.Random.SmartMatch === undefined
}
return settings
} catch (e) {
saveSettings(defaultSettings)
return defaultSettings
}
}
function saveSettings(settings) {
ensureConfigDir()
fs.writeFileSync(settingsPath, ini.stringify(settings), 'utf-8')
}
function generateWeights(maxNumber) {
const weights = {}
const base = maxNumber
for (let i = 1; i <= maxNumber; i++) {
weights[i] = parseFloat(base.toFixed(3))
}
return weights
}
function loadGoodRandom() {
ensureConfigDir()
if (!fs.existsSync(goodRandomPath)) {
const config = loadConfig()
const maxNumber = config.Random.MaxNumber
const data = {
maxNumber: maxNumber,
weights: generateWeights(maxNumber)
}
saveGoodRandom(data)
return data
}
try {
const content = fs.readFileSync(goodRandomPath, 'utf-8')
const data = JSON.parse(content)
const config = loadConfig()
const currentMax = config.Random.MaxNumber
if (data.maxNumber !== currentMax) {
data.maxNumber = currentMax
data.weights = generateWeights(currentMax)
saveGoodRandom(data)
}
return data
} catch (e) {
const config = loadConfig()
const maxNumber = config.Random.MaxNumber
const data = {
maxNumber: maxNumber,
weights: generateWeights(maxNumber)
}
saveGoodRandom(data)
return data
}
}
function saveGoodRandom(data) {
ensureConfigDir()
fs.writeFileSync(goodRandomPath, JSON.stringify(data, null, 2), 'utf-8')
}
function createWindow() {
mainWindow = new BrowserWindow({
width: 1000,
height: 700,
minWidth: 800,
minHeight: 600,
title: '课堂小助手',
icon: path.join(__dirname, 'assets/icon.ico'),
frame: false,
transparent: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
},
show: false
})
mainWindow.loadFile('index.html')
mainWindow.once('ready-to-show', () => {
mainWindow.show()
})
mainWindow.on('closed', () => {
mainWindow = null
})
}
function showAbout() {
const { dialog } = require('electron')
dialog.showMessageBox(mainWindow, {
type: 'info',
title: '关于课堂小助手',
message: '课堂小助手 v1.0.0',
detail: '一款专为课堂教学设计的辅助工具\n支持Windows 7及以上系统'
})
}
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
ipcMain.handle('get-app-version', () => {
return app.getVersion()
})
ipcMain.handle('get-app-name', () => {
return app.getName()
})
ipcMain.on('window-minimize', () => {
if (mainWindow) {
mainWindow.minimize()
}
})
ipcMain.on('window-maximize', () => {
if (mainWindow) {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize()
} else {
mainWindow.maximize()
}
}
})
ipcMain.on('window-restore', () => {
if (mainWindow && mainWindow.isMaximized()) {
mainWindow.unmaximize()
}
})
ipcMain.on('window-close', () => {
if (mainWindow) {
mainWindow.close()
}
})
ipcMain.handle('window-is-maximized', () => {
return mainWindow ? mainWindow.isMaximized() : false
})
ipcMain.handle('get-config', () => {
return loadConfig()
})
ipcMain.handle('save-config', (event, config) => {
saveConfig(config)
return true
})
ipcMain.handle('get-random-max-number', () => {
const config = loadConfig()
return config.Random.MaxNumber
})
ipcMain.handle('set-random-max-number', (event, maxNumber) => {
const config = loadConfig()
config.Random.MaxNumber = maxNumber
saveConfig(config)
return true
})
ipcMain.handle('get-time-last-seconds', () => {
const config = loadConfig()
return config.Time.LastSeconds
})
ipcMain.handle('set-time-last-seconds', (event, seconds) => {
const config = loadConfig()
config.Time.LastSeconds = seconds
saveConfig(config)
return true
})
ipcMain.handle('get-time-fill-mode', () => {
const settings = loadSettings()
return settings.Timer.FillMode
})
ipcMain.handle('set-time-fill-mode', (event, mode) => {
const settings = loadSettings()
settings.Timer.FillMode = mode
saveSettings(settings)
return true
})
ipcMain.handle('get-time-custom-seconds', () => {
const settings = loadSettings()
return settings.Timer.CustomSeconds
})
ipcMain.handle('set-time-custom-seconds', (event, seconds) => {
const settings = loadSettings()
settings.Timer.CustomSeconds = seconds
saveSettings(settings)
return true
})
ipcMain.handle('get-random-skip-animation', () => {
const settings = loadSettings()
return settings.Random.SkipAnimation
})
ipcMain.handle('set-random-skip-animation', (event, skip) => {
const settings = loadSettings()
settings.Random.SkipAnimation = skip
saveSettings(settings)
return true
})
ipcMain.handle('get-smart-match', () => {
const settings = loadSettings()
return settings.Random.SmartMatch
})
ipcMain.handle('set-smart-match', (event, enabled) => {
const settings = loadSettings()
settings.Random.SmartMatch = enabled
saveSettings(settings)
return true
})
ipcMain.handle('pick-weighted-random', () => {
const data = loadGoodRandom()
const maxNumber = data.maxNumber
const fixedWeight = parseFloat((maxNumber * 0.3).toFixed(3))
const effectiveWeights = {}
let totalWeight = 0
for (let i = 1; i <= maxNumber; i++) {
effectiveWeights[i] = Math.round(data.weights[i]) ** 2 + fixedWeight
totalWeight += effectiveWeights[i]
}
let rand = Math.random() * totalWeight
let picked = 1
for (let i = 1; i <= maxNumber; i++) {
rand -= effectiveWeights[i]
if (rand <= 0) {
picked = i
break
}
}
for (let i = 1; i <= maxNumber; i++) {
if (i === picked) {
data.weights[i] = 0.5
} else {
data.weights[i] = parseFloat(Math.min(data.weights[i] + 1, maxNumber).toFixed(3))
}
}
saveGoodRandom(data)
return picked
})
ipcMain.handle('reset-smart-match-weights', () => {
const config = loadConfig()
const maxNumber = config.Random.MaxNumber
const data = {
maxNumber: maxNumber,
weights: generateWeights(maxNumber)
}
saveGoodRandom(data)
return true
})
ipcMain.handle('open-external-url', (event, url) => {
shell.openExternal(url)
return true
})