mirror of
https://github.com/Kvan7/Exiled-Exchange-2.git
synced 2025-12-12 19:17:45 +00:00
Added MacOS support (#403)
This commit is contained in:
43
DEVELOPING.md
Normal file
43
DEVELOPING.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# How this works
|
||||||
|
|
||||||
|
There are 2 main parts of the app:
|
||||||
|
|
||||||
|
1. renderer: this is the HTML/Javascript-based UI rendered within the Electron container. This runs Vue.js, a React-like Javascript framework for rendering front-end.
|
||||||
|
2. main: includes the main app (written in Electron). Handles keyboard shortcuts, brings up the UI and overlays.
|
||||||
|
|
||||||
|
Note that these 2 both depend on each other, and one cannot run without the other.
|
||||||
|
|
||||||
|
# How to develop
|
||||||
|
|
||||||
|
The most up-to-date instructions can always be derived from CI:
|
||||||
|
|
||||||
|
[.github/workflows/main.yml](https://github.com/SnosMe/awakened-poe-trade/blob/master/.github/workflows/main.yml)
|
||||||
|
|
||||||
|
Here's what that looks like as of 2023-12-03.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd renderer
|
||||||
|
yarn install
|
||||||
|
yarn make-index-files
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
# In a second shell
|
||||||
|
cd main
|
||||||
|
yarn install
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
# How to build
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd renderer
|
||||||
|
yarn install
|
||||||
|
yarn make-index-files
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
cd ../main
|
||||||
|
yarn build
|
||||||
|
# We want to sign with a distribution certificate to ensure other users can
|
||||||
|
# install without errors
|
||||||
|
CSC_NAME="Certificate name in Keychain" yarn package
|
||||||
|
```
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
Follow instructions similar to CI [.github/workflows/main.yml](https://github.com/SnosMe/awakened-poe-trade/blob/master/.github/workflows/main.yml)
|
See [DEVELOPING.md](./DEVELOPING.md)
|
||||||
|
|
||||||
### Acknowledgments
|
### Acknowledgments
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,9 @@ export const KeyToElectron = {
|
|||||||
Backslash: '\\',
|
Backslash: '\\',
|
||||||
BracketRight: ']',
|
BracketRight: ']',
|
||||||
Quote: "'",
|
Quote: "'",
|
||||||
Ctrl: 'CmdOrCtrl',
|
// Do not change Ctrl to CmdOrCtrl. It causes registered shortcuts to
|
||||||
|
// often not work on Mac for unknown reasons.
|
||||||
|
Ctrl: 'Ctrl',
|
||||||
Alt: 'Alt',
|
Alt: 'Alt',
|
||||||
Shift: 'Shift'
|
Shift: 'Shift'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ win:
|
|||||||
linux:
|
linux:
|
||||||
target:
|
target:
|
||||||
- "AppImage"
|
- "AppImage"
|
||||||
|
mac:
|
||||||
|
target:
|
||||||
|
- target: default
|
||||||
|
arch:
|
||||||
|
- universal
|
||||||
|
# MacOS apps can only be run on other systems if signed
|
||||||
|
forceCodeSigning: true
|
||||||
appImage:
|
appImage:
|
||||||
executableArgs:
|
executableArgs:
|
||||||
- "--sandbox"
|
- "--sandbox"
|
||||||
|
|||||||
@@ -8,9 +8,21 @@ export class AppTray {
|
|||||||
serverPort = 0
|
serverPort = 0
|
||||||
|
|
||||||
constructor (server: ServerEvents) {
|
constructor (server: ServerEvents) {
|
||||||
this.tray = new Tray(
|
let trayImage = nativeImage.createFromPath(
|
||||||
nativeImage.createFromPath(path.join(__dirname, process.env.STATIC!, process.platform === 'win32' ? 'icon.ico' : 'icon.png'))
|
path.join(
|
||||||
|
__dirname,
|
||||||
|
process.env.STATIC!,
|
||||||
|
process.platform === "win32" ? "icon.ico" : "icon.png"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
// Mac image size needs to be smaller, or else it looks huge. Size
|
||||||
|
// guideline is from https://iconhandbook.co.uk/reference/chart/osx/
|
||||||
|
trayImage = trayImage.resize({ width: 22, height: 22 })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tray = new Tray(trayImage)
|
||||||
this.tray.setToolTip(`Awakened PoE Trade v${app.getVersion()}`)
|
this.tray.setToolTip(`Awakened PoE Trade v${app.getVersion()}`)
|
||||||
this.rebuildMenu()
|
this.rebuildMenu()
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import fs from 'fs/promises'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import ini from 'ini'
|
import ini from 'ini'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
import process from 'process';
|
||||||
import { hotkeyToString, CodeToKey } from '../../../ipc/KeyToCode'
|
import { hotkeyToString, CodeToKey } from '../../../ipc/KeyToCode'
|
||||||
import type { Logger } from '../RemoteLogger'
|
import type { Logger } from '../RemoteLogger'
|
||||||
import type { ServerEvents } from '../server'
|
import type { ServerEvents } from '../server'
|
||||||
@@ -21,7 +22,12 @@ export class GameConfig {
|
|||||||
if (this.filePath === filePath) return
|
if (this.filePath === filePath) return
|
||||||
|
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
filePath = path.join(app.getPath('documents'), 'My Games', 'Path of Exile', 'production_Config.ini')
|
if (process.platform === 'darwin') {
|
||||||
|
filePath = path.join(app.getPath('appData'), 'Path of Exile', 'Preferences', 'production_Config.ini')
|
||||||
|
} else {
|
||||||
|
filePath = path.join(app.getPath('documents'), 'My Games', 'Path of Exile', 'production_Config.ini')
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.access(filePath)
|
await fs.access(filePath)
|
||||||
// this.server.sendEventTo('any', {
|
// this.server.sendEventTo('any', {
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { promises as fs, watchFile, unwatchFile } from 'fs'
|
import { promises as fs, watchFile, unwatchFile } from 'fs'
|
||||||
|
import { app } from 'electron';
|
||||||
import { ServerEvents } from '../server'
|
import { ServerEvents } from '../server'
|
||||||
import { Logger } from '../RemoteLogger'
|
import { Logger } from '../RemoteLogger'
|
||||||
|
|
||||||
const COMMON_PATH = [
|
|
||||||
'C:\\Program Files (x86)\\Grinding Gear Games\\Path of Exile\\logs\\Client.txt',
|
|
||||||
'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Path of Exile\\logs\\Client.txt'
|
|
||||||
]
|
|
||||||
|
|
||||||
export class GameLogWatcher {
|
export class GameLogWatcher {
|
||||||
private offset = 0
|
private offset = 0
|
||||||
private filePath?: string
|
private filePath?: string
|
||||||
@@ -22,8 +18,20 @@ export class GameLogWatcher {
|
|||||||
async restart (logFile: string | null) {
|
async restart (logFile: string | null) {
|
||||||
if (this.filePath === logFile) return
|
if (this.filePath === logFile) return
|
||||||
|
|
||||||
if (!logFile && process.platform === 'win32') {
|
if (!logFile) {
|
||||||
for (const filePath of COMMON_PATH) {
|
let possiblePaths: string[] = []
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
possiblePaths = [
|
||||||
|
'C:\\Program Files (x86)\\Grinding Gear Games\\Path of Exile\\logs\\Client.txt',
|
||||||
|
'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Path of Exile\\logs\\Client.txt'
|
||||||
|
]
|
||||||
|
} else if (process.platform === 'darwin') {
|
||||||
|
possiblePaths = [
|
||||||
|
`${app.getPath('home')}/Library/Caches/com.GGG.PathOfExile/Logs/Client.txt`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const filePath of possiblePaths) {
|
||||||
try {
|
try {
|
||||||
await fs.access(filePath)
|
await fs.access(filePath)
|
||||||
// this.server.sendEventTo('any', {
|
// this.server.sendEventTo('any', {
|
||||||
|
|||||||
@@ -224,7 +224,16 @@ export class Shortcuts {
|
|||||||
|
|
||||||
function pressKeysToCopyItemText (pressedModKeys: string[] = [], showModsKey: string) {
|
function pressKeysToCopyItemText (pressedModKeys: string[] = [], showModsKey: string) {
|
||||||
let keys = mergeTwoHotkeys('Ctrl + C', showModsKey).split(' + ')
|
let keys = mergeTwoHotkeys('Ctrl + C', showModsKey).split(' + ')
|
||||||
keys = keys.filter(key => key !== 'C' && !pressedModKeys.includes(key))
|
keys = keys.filter(key => key !== 'C')
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
// On non-Mac platforms, don't toggle keys that are already being pressed.
|
||||||
|
//
|
||||||
|
// For unknown reasons, we need to toggle pressed keys on Mac for advanced
|
||||||
|
// mod descriptions to be copied. You can test this by setting the shortcut
|
||||||
|
// to "Alt + any letter". They'll work with this line, but not if it's
|
||||||
|
// commented out.
|
||||||
|
keys = keys.filter(key => !pressedModKeys.includes(key))
|
||||||
|
}
|
||||||
|
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
uIOhook.keyToggle(UiohookKey[key as UiohookKeyT], 'down')
|
uIOhook.keyToggle(UiohookKey[key as UiohookKeyT], 'down')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { uIOhook, UiohookKey as Key } from 'uiohook-napi'
|
import { uIOhook, UiohookKey as Key } from 'uiohook-napi'
|
||||||
|
import process from 'process';
|
||||||
import type { HostClipboard } from './HostClipboard'
|
import type { HostClipboard } from './HostClipboard'
|
||||||
import type { OverlayWindow } from '../windowing/OverlayWindow'
|
import type { OverlayWindow } from '../windowing/OverlayWindow'
|
||||||
|
|
||||||
@@ -14,14 +15,16 @@ const AUTO_CLEAR = [
|
|||||||
|
|
||||||
export function typeInChat (text: string, send: boolean, clipboard: HostClipboard) {
|
export function typeInChat (text: string, send: boolean, clipboard: HostClipboard) {
|
||||||
clipboard.restoreShortly((clipboard) => {
|
clipboard.restoreShortly((clipboard) => {
|
||||||
|
const modifiers = process.platform === 'darwin' ? [Key.Meta] : [Key.Ctrl]
|
||||||
|
|
||||||
if (text.startsWith(PLACEHOLDER_LAST)) {
|
if (text.startsWith(PLACEHOLDER_LAST)) {
|
||||||
text = text.slice(`${PLACEHOLDER_LAST} `.length)
|
text = text.slice(`${PLACEHOLDER_LAST} `.length)
|
||||||
clipboard.writeText(text)
|
clipboard.writeText(text)
|
||||||
uIOhook.keyTap(Key.Enter, [Key.Ctrl])
|
uIOhook.keyTap(Key.Enter, modifiers)
|
||||||
} else if (text.endsWith(PLACEHOLDER_LAST)) {
|
} else if (text.endsWith(PLACEHOLDER_LAST)) {
|
||||||
text = text.slice(0, -PLACEHOLDER_LAST.length)
|
text = text.slice(0, -PLACEHOLDER_LAST.length)
|
||||||
clipboard.writeText(text)
|
clipboard.writeText(text)
|
||||||
uIOhook.keyTap(Key.Enter, [Key.Ctrl])
|
uIOhook.keyTap(Key.Enter, modifiers)
|
||||||
uIOhook.keyTap(Key.Home)
|
uIOhook.keyTap(Key.Home)
|
||||||
// press twice to focus input when using controller
|
// press twice to focus input when using controller
|
||||||
uIOhook.keyTap(Key.Home)
|
uIOhook.keyTap(Key.Home)
|
||||||
@@ -30,11 +33,11 @@ export function typeInChat (text: string, send: boolean, clipboard: HostClipboar
|
|||||||
clipboard.writeText(text)
|
clipboard.writeText(text)
|
||||||
uIOhook.keyTap(Key.Enter)
|
uIOhook.keyTap(Key.Enter)
|
||||||
if (!AUTO_CLEAR.includes(text[0])) {
|
if (!AUTO_CLEAR.includes(text[0])) {
|
||||||
uIOhook.keyTap(Key.A, [Key.Ctrl])
|
uIOhook.keyTap(Key.A, modifiers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uIOhook.keyTap(Key.V, [Key.Ctrl])
|
uIOhook.keyTap(Key.V, modifiers)
|
||||||
|
|
||||||
if (send) {
|
if (send) {
|
||||||
uIOhook.keyTap(Key.Enter)
|
uIOhook.keyTap(Key.Enter)
|
||||||
@@ -56,7 +59,7 @@ export function stashSearch (
|
|||||||
overlay.assertGameActive()
|
overlay.assertGameActive()
|
||||||
clipboard.writeText(text)
|
clipboard.writeText(text)
|
||||||
uIOhook.keyTap(Key.F, [Key.Ctrl])
|
uIOhook.keyTap(Key.F, [Key.Ctrl])
|
||||||
uIOhook.keyTap(Key.V, [Key.Ctrl])
|
uIOhook.keyTap(Key.V, [process.platform === 'darwin' ? Key.Meta : Key.Ctrl])
|
||||||
uIOhook.keyTap(Key.Enter)
|
uIOhook.keyTap(Key.Enter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class GameWindow extends EventEmitter {
|
|||||||
if (!this._isTracking) {
|
if (!this._isTracking) {
|
||||||
OverlayController.events.on('focus', () => { this.isActive = true })
|
OverlayController.events.on('focus', () => { this.isActive = true })
|
||||||
OverlayController.events.on('blur', () => { this.isActive = false })
|
OverlayController.events.on('blur', () => { this.isActive = false })
|
||||||
OverlayController.attachByTitle(window, title)
|
OverlayController.attachByTitle(window, title, { hasTitleBarOnMac: true })
|
||||||
this._isTracking = true
|
this._isTracking = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user