Archived
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a36930411 | ||
|
|
b124e57cdb |
No files matched your search
@@ -1,12 +1,17 @@
|
|||||||
# FMCRAFT Chat 桌面版
|
# FMCRAFT Chat Desktop
|
||||||
|
|
||||||
Windows 7 及以上可用的 FMCRAFT Chat 桌面客户端(当前提供 x64 便携版)。使用 Electron 22(最后一个支持 Windows 7/8/8.1 的 Electron 版本)直接嵌入网页。
|
Windows 7 及以上可用的 FMCRAFT Chat Desktop 桌面客户端,提供 x64 / ia32 便携版。使用 Electron 22(最后一个支持 Windows 7/8/8.1 的 Electron 版本)直接嵌入网页。
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
- 直接加载 `config.ini` 中配置的网页地址
|
- 直接加载 `config.ini` 中配置的网页地址
|
||||||
- Windows 消息通知
|
- Windows 消息通知
|
||||||
- 通过 Windows 托盘气泡发送,兼容 Windows 7/8/8.1/10/11
|
- Windows 10/11 使用系统 Toast,Windows 7/8/8.1 使用托盘气泡
|
||||||
|
- 通知顶部应用名显示为 FMCRAFT Chat Desktop
|
||||||
|
- 通知左侧显示发送人头像
|
||||||
|
- 通知标题显示会话名称,正文显示消息内容
|
||||||
|
- 群聊消息正文显示发送人,例如 `DHEnry:测试`
|
||||||
|
- 点击通知会打开窗口并跳转到对应会话
|
||||||
- 任务栏闪烁
|
- 任务栏闪烁
|
||||||
- 本地音效播放(不依赖网页音频权限)
|
- 本地音效播放(不依赖网页音频权限)
|
||||||
- 三项通知开关均可在设置中关闭,默认全部开启
|
- 三项通知开关均可在设置中关闭,默认全部开启
|
||||||
@@ -26,7 +31,7 @@ flash=true
|
|||||||
sound=true
|
sound=true
|
||||||
```
|
```
|
||||||
|
|
||||||
程序会优先读写可执行文件同目录下的 `config.ini`。如果该目录不可写(例如安装在 `Program Files`),会自动回退到 `%APPDATA%\FMCRAFT Chat\config.ini`,设置页会显示实际使用的路径。
|
程序会优先读写可执行文件同目录下的 `config.ini`。如果该目录不可写(例如安装在 `Program Files`),会自动回退到 `%APPDATA%\FMCRAFT Chat Desktop\config.ini`,设置页会显示实际使用的路径。
|
||||||
|
|
||||||
## 开发运行
|
## 开发运行
|
||||||
|
|
||||||
@@ -38,10 +43,17 @@ npm start
|
|||||||
## 打包
|
## 打包
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# 64 位
|
||||||
npm run dist:x64
|
npm run dist:x64
|
||||||
|
|
||||||
|
# 32 位(会自动下载并准备 ia32 Electron)
|
||||||
|
npm run dist:ia32
|
||||||
```
|
```
|
||||||
|
|
||||||
生成文件在 `release/` 目录,可直接分发 `FMCRAFT-Chat-1.0.0-x64.exe` 便携版。
|
生成文件在 `release/` 目录:
|
||||||
|
|
||||||
|
- `FMCRAFT-Chat-1.0.0-x64.exe`
|
||||||
|
- `FMCRAFT-Chat-1.0.0-ia32.exe`
|
||||||
|
|
||||||
## 通知开关
|
## 通知开关
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@@ -10,9 +10,12 @@ const {
|
|||||||
nativeImage,
|
nativeImage,
|
||||||
} = require('electron')
|
} = require('electron')
|
||||||
const fs = require('node:fs')
|
const fs = require('node:fs')
|
||||||
|
const os = require('node:os')
|
||||||
const path = require('node:path')
|
const path = require('node:path')
|
||||||
|
|
||||||
const APP_ID = 'top.fmcraft.chat.desktop'
|
// 没有开始菜单快捷方式时,Windows Toast 会直接显示 AUMID。
|
||||||
|
// 使用可读名称作为 AUMID,确保通知顶部显示 FMCRAFT Chat Desktop。
|
||||||
|
const APP_ID = 'FMCRAFT Chat Desktop'
|
||||||
const DEFAULT_URL = 'https://chat.fmcraft.top'
|
const DEFAULT_URL = 'https://chat.fmcraft.top'
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
url: DEFAULT_URL,
|
url: DEFAULT_URL,
|
||||||
@@ -35,7 +38,7 @@ let pendingSound = false
|
|||||||
let appIcon = null
|
let appIcon = null
|
||||||
|
|
||||||
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required')
|
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required')
|
||||||
app.setName('FMCRAFT Chat')
|
app.setName('FMCRAFT Chat Desktop')
|
||||||
|
|
||||||
function defaultConfigText() {
|
function defaultConfigText() {
|
||||||
return [
|
return [
|
||||||
@@ -150,7 +153,7 @@ function createMainWindow() {
|
|||||||
height: 820,
|
height: 820,
|
||||||
minWidth: 960,
|
minWidth: 960,
|
||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
title: 'FMCRAFT Chat',
|
title: 'FMCRAFT Chat Desktop',
|
||||||
icon: appIcon || iconPath,
|
icon: appIcon || iconPath,
|
||||||
backgroundColor: '#0b0e14',
|
backgroundColor: '#0b0e14',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
@@ -164,7 +167,7 @@ function createMainWindow() {
|
|||||||
|
|
||||||
mainWindow.loadURL(config.url).catch((err) => {
|
mainWindow.loadURL(config.url).catch((err) => {
|
||||||
dialog.showErrorBox(
|
dialog.showErrorBox(
|
||||||
'无法打开 FMCRAFT Chat',
|
'无法打开 FMCRAFT Chat Desktop',
|
||||||
`请检查 config.ini 中的页面地址是否可访问。\n\n${String(err && err.message ? err.message : err)}`
|
`请检查 config.ini 中的页面地址是否可访问。\n\n${String(err && err.message ? err.message : err)}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -230,36 +233,85 @@ function playSound(enabled = config.sound) {
|
|||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWindowsNotification(title, body) {
|
const activeNotifications = new Set()
|
||||||
// Windows 7/8 没有 Toast;便携版在 Windows 10/11 上也不一定有开始菜单快捷方式。
|
let lastBalloonConversationId = null
|
||||||
// 统一使用托盘气泡,保证 Windows 7 及以上都能收到提醒。
|
|
||||||
if (process.platform === 'win32' && tray) {
|
function isLegacyWindows() {
|
||||||
|
return process.platform === 'win32' && /^6\./.test(os.release())
|
||||||
|
}
|
||||||
|
|
||||||
|
function useTrayBalloon() {
|
||||||
|
return process.platform === 'win32' && (process.env.FMCRAFT_FORCE_BALLOON === '1' || isLegacyWindows())
|
||||||
|
}
|
||||||
|
|
||||||
|
function openConversationFromNotification(conversationId) {
|
||||||
|
showMainWindow()
|
||||||
|
if (!conversationId || !mainWindow || mainWindow.isDestroyed()) return
|
||||||
|
const run = () => {
|
||||||
|
if (!mainWindow || mainWindow.isDestroyed()) return
|
||||||
|
const id = JSON.stringify(String(conversationId))
|
||||||
|
mainWindow.webContents
|
||||||
|
.executeJavaScript(`window.desktopOpenConversation && window.desktopOpenConversation(${id})`)
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
if (mainWindow.webContents.isLoading()) {
|
||||||
|
mainWindow.webContents.once('did-finish-load', run)
|
||||||
|
} else {
|
||||||
|
setTimeout(run, 80)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWindowsNotification({ title, body, icon, conversationId }) {
|
||||||
|
const iconImage = icon || appIcon || iconPath
|
||||||
|
if (useTrayBalloon() && tray) {
|
||||||
|
lastBalloonConversationId = conversationId || null
|
||||||
tray.displayBalloon({
|
tray.displayBalloon({
|
||||||
title,
|
title,
|
||||||
content: body,
|
content: body,
|
||||||
icon: appIcon || iconPath,
|
icon: iconImage,
|
||||||
noSound: true,
|
noSound: true,
|
||||||
respectQuietTime: true,
|
respectQuietTime: true,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!Notification.isSupported()) return
|
if (!Notification.isSupported()) {
|
||||||
|
if (tray) {
|
||||||
|
lastBalloonConversationId = conversationId || null
|
||||||
|
tray.displayBalloon({
|
||||||
|
title,
|
||||||
|
content: body,
|
||||||
|
icon: iconImage,
|
||||||
|
noSound: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
icon: appIcon || iconPath,
|
icon: iconImage,
|
||||||
silent: true,
|
silent: true,
|
||||||
})
|
})
|
||||||
notification.on('click', showMainWindow)
|
activeNotifications.add(notification)
|
||||||
|
notification.on('click', () => openConversationFromNotification(conversationId))
|
||||||
|
notification.on('close', () => activeNotifications.delete(notification))
|
||||||
notification.show()
|
notification.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDesktopNotification(payload, override) {
|
function handleDesktopNotification(payload, override) {
|
||||||
const options = override ? { ...config, ...override } : config
|
const options = override ? { ...config, ...override } : config
|
||||||
const title = String(payload && payload.title ? payload.title : 'FMCRAFT Chat').slice(0, 120)
|
const title = String(payload && payload.title ? payload.title : 'FMCRAFT Chat Desktop').slice(0, 120)
|
||||||
const body = String(payload && payload.body ? payload.body : '').slice(0, 500)
|
const body = String(payload && payload.body ? payload.body : '').slice(0, 500)
|
||||||
|
const conversationId =
|
||||||
|
payload && typeof payload.conversationId === 'string' ? payload.conversationId : null
|
||||||
|
|
||||||
if (options.windows) showWindowsNotification(title, body)
|
let icon = null
|
||||||
|
if (payload && typeof payload.avatarUrl === 'string' && payload.avatarUrl.startsWith('data:image/')) {
|
||||||
|
icon = nativeImage.createFromDataURL(payload.avatarUrl)
|
||||||
|
if (icon.isEmpty()) icon = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.windows) showWindowsNotification({ title, body, icon, conversationId })
|
||||||
if (options.flash && mainWindow && !mainWindow.isDestroyed() && !mainWindow.isFocused()) {
|
if (options.flash && mainWindow && !mainWindow.isDestroyed() && !mainWindow.isFocused()) {
|
||||||
mainWindow.flashFrame(true)
|
mainWindow.flashFrame(true)
|
||||||
}
|
}
|
||||||
@@ -270,7 +322,7 @@ function updateTrayMenu() {
|
|||||||
if (!tray || tray.isDestroyed()) return
|
if (!tray || tray.isDestroyed()) return
|
||||||
tray.setContextMenu(
|
tray.setContextMenu(
|
||||||
Menu.buildFromTemplate([
|
Menu.buildFromTemplate([
|
||||||
{ label: '显示 FMCRAFT Chat', click: showMainWindow },
|
{ label: '显示 FMCRAFT Chat Desktop', click: showMainWindow },
|
||||||
{ label: '通知设置…', click: openSettingsWindow },
|
{ label: '通知设置…', click: openSettingsWindow },
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
@@ -310,10 +362,10 @@ function createTray() {
|
|||||||
try {
|
try {
|
||||||
const image = nativeImage.createFromPath(trayIconPath)
|
const image = nativeImage.createFromPath(trayIconPath)
|
||||||
tray = new Tray(image)
|
tray = new Tray(image)
|
||||||
tray.setToolTip('FMCRAFT Chat')
|
tray.setToolTip('FMCRAFT Chat Desktop')
|
||||||
tray.on('double-click', showMainWindow)
|
tray.on('double-click', showMainWindow)
|
||||||
tray.on('click', showMainWindow)
|
tray.on('click', showMainWindow)
|
||||||
tray.on('balloon-click', showMainWindow)
|
tray.on('balloon-click', () => openConversationFromNotification(lastBalloonConversationId))
|
||||||
updateTrayMenu()
|
updateTrayMenu()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('无法创建托盘图标:', err)
|
console.warn('无法创建托盘图标:', err)
|
||||||
@@ -355,12 +407,12 @@ function createMenu() {
|
|||||||
label: '帮助',
|
label: '帮助',
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: '关于 FMCRAFT Chat',
|
label: '关于 FMCRAFT Chat Desktop',
|
||||||
click: () => {
|
click: () => {
|
||||||
dialog.showMessageBox({
|
dialog.showMessageBox({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: '关于 FMCRAFT Chat',
|
title: '关于 FMCRAFT Chat Desktop',
|
||||||
message: 'FMCRAFT Chat 桌面版',
|
message: 'FMCRAFT Chat Desktop',
|
||||||
detail: `版本 ${app.getVersion()}\n适用于 Windows 7 及以上。\n\n配置文件:${configPath}`,
|
detail: `版本 ${app.getVersion()}\n适用于 Windows 7 及以上。\n\n配置文件:${configPath}`,
|
||||||
buttons: ['确定'],
|
buttons: ['确定'],
|
||||||
})
|
})
|
||||||
@@ -386,7 +438,7 @@ function openSettingsWindow() {
|
|||||||
resizable: true,
|
resizable: true,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
title: 'FMCRAFT Chat 通知设置',
|
title: 'FMCRAFT Chat Desktop 通知设置',
|
||||||
icon: appIcon || iconPath,
|
icon: appIcon || iconPath,
|
||||||
parent: mainWindow || undefined,
|
parent: mainWindow || undefined,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
@@ -427,8 +479,8 @@ function registerIpc() {
|
|||||||
const options = values && typeof values === 'object' ? values : config
|
const options = values && typeof values === 'object' ? values : config
|
||||||
handleDesktopNotification(
|
handleDesktopNotification(
|
||||||
{
|
{
|
||||||
title: 'FMCRAFT Chat 通知测试',
|
title: 'FMCRAFT Chat Desktop',
|
||||||
body: '如果你看到这条通知,说明桌面通知工作正常。',
|
body: '通知测试:如果你看到这条消息,说明桌面通知工作正常。',
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
+70
-1
@@ -10,7 +10,8 @@
|
|||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "22.3.27",
|
"electron": "22.3.27",
|
||||||
"electron-builder": "24.13.3"
|
"electron-builder": "24.13.3",
|
||||||
|
"rcedit": "^5.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@develar/schema-utils": {
|
"node_modules/@develar/schema-utils": {
|
||||||
@@ -1291,6 +1292,31 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-spawn-windows-exe": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/malept"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@malept/cross-spawn-promise": "^1.1.0",
|
||||||
|
"is-wsl": "^2.2.0",
|
||||||
|
"which": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -2300,6 +2326,22 @@
|
|||||||
"is-ci": "bin.js"
|
"is-ci": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-docker": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"is-docker": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-fullwidth-code-point": {
|
"node_modules/is-fullwidth-code-point": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -2308,6 +2350,19 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-wsl": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-docker": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/isarray": {
|
"node_modules/isarray": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -2820,6 +2875,20 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rcedit": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rcedit/-/rcedit-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-dgysxaeXZ4snLpPjn8aVtHvZDCx+aRcvZbaWBgl1poU6OPustMvOkj9a9ZqASQ6i5Y5szJ13LSvglEOwrmgUxA==",
|
||||||
|
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn-windows-exe": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-config-file": {
|
"node_modules/read-config-file": {
|
||||||
"version": "6.3.2",
|
"version": "6.3.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|||||||
+7
-4
@@ -8,16 +8,19 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
"dist": "electron-builder --win portable --x64",
|
"dist": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/build-win.ps1 -Arch x64",
|
||||||
"dist:x64": "electron-builder --win portable --x64"
|
"dist:x64": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/build-win.ps1 -Arch x64",
|
||||||
|
"prepare:ia32": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/prepare-ia32.ps1",
|
||||||
|
"dist:ia32": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/build-win.ps1 -Arch ia32"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "22.3.27",
|
"electron": "22.3.27",
|
||||||
"electron-builder": "24.13.3"
|
"electron-builder": "24.13.3",
|
||||||
|
"rcedit": "^5.0.2"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "top.fmcraft.chat.desktop",
|
"appId": "top.fmcraft.chat.desktop",
|
||||||
"productName": "FMCRAFT Chat",
|
"productName": "FMCRAFT Chat Desktop",
|
||||||
"electronDist": "node_modules/electron/dist",
|
"electronDist": "node_modules/electron/dist",
|
||||||
"npmRebuild": false,
|
"npmRebuild": false,
|
||||||
"compression": "normal",
|
"compression": "normal",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ contextBridge.exposeInMainWorld('desktopBridge', {
|
|||||||
body: typeof payload.body === 'string' ? payload.body : '',
|
body: typeof payload.body === 'string' ? payload.body : '',
|
||||||
conversationId:
|
conversationId:
|
||||||
typeof payload.conversationId === 'string' ? payload.conversationId : undefined,
|
typeof payload.conversationId === 'string' ? payload.conversationId : undefined,
|
||||||
|
avatarUrl: typeof payload.avatarUrl === 'string' ? payload.avatarUrl : undefined,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import path from 'node:path'
|
||||||
|
import { rcedit } from 'rcedit'
|
||||||
|
|
||||||
|
const exePath = process.argv[2]
|
||||||
|
if (!exePath) {
|
||||||
|
console.error('Usage: node scripts/brand-exe.mjs <path-to-exe>')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
await rcedit(exePath, {
|
||||||
|
icon: path.resolve('assets/icon.ico'),
|
||||||
|
'version-string': {
|
||||||
|
CompanyName: 'FMCRAFT',
|
||||||
|
FileDescription: 'FMCRAFT Chat Desktop',
|
||||||
|
InternalName: 'FMCRAFT Chat Desktop',
|
||||||
|
OriginalFilename: path.basename(exePath),
|
||||||
|
ProductName: 'FMCRAFT Chat Desktop',
|
||||||
|
LegalCopyright: 'Copyright © FMCRAFT',
|
||||||
|
},
|
||||||
|
'file-version': '1.0.0',
|
||||||
|
'product-version': '1.0.0',
|
||||||
|
})
|
||||||
|
console.log(`branded ${exePath}`)
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
param(
|
||||||
|
[ValidateSet('x64', 'ia32')]
|
||||||
|
[string]$Arch = 'x64'
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$desktop = Split-Path -Parent $PSScriptRoot
|
||||||
|
Set-Location $desktop
|
||||||
|
|
||||||
|
if (-not $env:ELECTRON_MIRROR) {
|
||||||
|
$env:ELECTRON_MIRROR = 'https://npmmirror.com/mirrors/electron/'
|
||||||
|
}
|
||||||
|
if (-not $env:ELECTRON_BUILDER_BINARIES_MIRROR) {
|
||||||
|
$env:ELECTRON_BUILDER_BINARIES_MIRROR = 'https://npmmirror.com/mirrors/electron-builder-binaries/'
|
||||||
|
}
|
||||||
|
$env:CSC_IDENTITY_AUTO_DISCOVERY = 'false'
|
||||||
|
|
||||||
|
if ($Arch -eq 'ia32') {
|
||||||
|
& (Join-Path $PSScriptRoot 'prepare-ia32.ps1')
|
||||||
|
$electronDist = 'node_modules/electron-ia32/dist'
|
||||||
|
$outDir = 'release/win-ia32-unpacked'
|
||||||
|
} else {
|
||||||
|
$electronDist = 'node_modules/electron/dist'
|
||||||
|
$outDir = 'release/win-unpacked'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Building unpacked app ($Arch)..."
|
||||||
|
& .\node_modules\.bin\electron-builder.cmd --win --$Arch --dir --config.electronDist=$electronDist
|
||||||
|
|
||||||
|
$exe = Join-Path $outDir 'FMCRAFT Chat Desktop.exe'
|
||||||
|
if (!(Test-Path $exe)) {
|
||||||
|
throw "未找到打包后的 exe:$exe"
|
||||||
|
}
|
||||||
|
node scripts\brand-exe.mjs $exe
|
||||||
|
|
||||||
|
Write-Host "Building portable exe ($Arch)..."
|
||||||
|
& .\node_modules\.bin\electron-builder.cmd --win portable --$Arch --prepackaged $outDir
|
||||||
|
|
||||||
|
Write-Host "Done: release\FMCRAFT-Chat-1.0.0-$Arch.exe"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$version = '22.3.27'
|
||||||
|
$url = "https://npmmirror.com/mirrors/electron/$version/electron-v$version-win32-ia32.zip"
|
||||||
|
$zip = Join-Path $env:TEMP "electron-v$version-win32-ia32.zip"
|
||||||
|
$desktop = Split-Path -Parent $PSScriptRoot
|
||||||
|
$target = Join-Path $desktop 'node_modules\electron-ia32'
|
||||||
|
$dist = Join-Path $target 'dist'
|
||||||
|
|
||||||
|
if (!(Test-Path $zip) -or (Get-Item $zip).Length -lt 90000000) {
|
||||||
|
Write-Host "Downloading Electron $version ia32..."
|
||||||
|
curl.exe -L --fail --retry 3 -o $zip $url
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $dist) { Remove-Item $dist -Recurse -Force }
|
||||||
|
New-Item -ItemType Directory -Path $dist -Force | Out-Null
|
||||||
|
Write-Host "Extracting Electron ia32..."
|
||||||
|
Expand-Archive -Path $zip -DestinationPath $dist -Force
|
||||||
|
|
||||||
|
Set-Content -Path (Join-Path $target 'path.txt') -Value 'electron.exe' -NoNewline -Encoding ascii
|
||||||
|
Set-Content -Path (Join-Path $dist 'version') -Value $version -NoNewline -Encoding ascii
|
||||||
|
Write-Host "Electron ia32 ready: $dist"
|
||||||
+2
-2
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>FMCRAFT Chat 通知设置</title>
|
<title>FMCRAFT Chat Desktop 通知设置</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<input type="checkbox" id="windows" />
|
<input type="checkbox" id="windows" />
|
||||||
<span>
|
<span>
|
||||||
<strong>Windows 消息通知</strong>
|
<strong>Windows 消息通知</strong>
|
||||||
<span>通过 Windows 托盘气泡提醒新消息,兼容 Windows 7 及以上。</span>
|
<span>Windows 10/11 使用系统通知,Windows 7/8 使用托盘气泡;显示发送人头像和会话名称。</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user