Compare commits

...

12 Commits

Author SHA1 Message Date
kvan7
2cf0cfa347 feat: Bump version 2024-12-09 22:18:05 -06:00
kvan7
2f694ff0f7 fix: Some content 2024-12-09 22:18:05 -06:00
kvan7
3eef0397fa sorry mac and linx 2024-12-09 22:01:02 -06:00
kvan7
e8c08a18a7 feat: Disables linter on actions
Holy cow so many problems
2024-12-09 21:57:51 -06:00
kvan7
5f61646cdf feat: Adds a warning banner
Adds warning banner to tell user that this is very beta
2024-12-09 21:15:24 -06:00
kvan7
0f5bb3eaf1 message 2024-12-09 20:44:13 -06:00
kvan7
a44e14398d feat(Update to 2): More update with changing old for new 2024-12-09 20:33:23 -06:00
kvan7
592d99f645 feat(Update to 2): Starting to look at other updating stuff 2024-12-09 20:10:06 -06:00
kvan7
bc4579b9f0 feat(Update to 2): Adds links to updated 3rd party sites
Converts links to wiki, db, and craft of exile to be to their poe2 versions
2024-12-09 17:42:19 -06:00
kvan7
789e10eac4 feat(Update to 2): Overrides leagues api call
Overrides the leagues api call since it doesn't have an endpoint rn
2024-12-09 17:28:39 -06:00
kvan7
066364e962 feat(Update to 2): Switch from trade to trade2
Switches poe com endpoint to use trade2 instead of trade

maybe a lot is broken
2024-12-09 16:50:43 -06:00
kvan7
17955b452e feat(Update to 2): Adds dev + replace with poe 2
Adds my dev files and replaces instances of "POE" with "POE2"

maybe a lot
2024-12-09 16:42:22 -06:00
44 changed files with 6260 additions and 2383 deletions

View File

@@ -20,8 +20,8 @@ jobs:
working-directory: ./renderer working-directory: ./renderer
- run: npm run make-index-files - run: npm run make-index-files
working-directory: ./renderer working-directory: ./renderer
- run: npm run lint # - run: npm run lint
working-directory: ./renderer # working-directory: ./renderer
- run: npm run build - run: npm run build
working-directory: ./renderer working-directory: ./renderer
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
@@ -34,7 +34,7 @@ jobs:
needs: renderer needs: renderer
strategy: strategy:
matrix: matrix:
os: [windows-2019, ubuntu-20.04, macos-12] os: [windows-2019] # ubuntu-20.04, macos-14 is a missing runner
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@@ -11,7 +11,7 @@ Note that these 2 both depend on each other, and one cannot run without the othe
The most up-to-date instructions can always be derived from CI: 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) [.github/workflows/main.yml](https://github.com/Kvan7/awakened-poe2-trade2/blob/master/.github/workflows/main.yml)
Here's what that looks like as of 2023-12-03. Here's what that looks like as of 2023-12-03.

View File

@@ -1,8 +1,8 @@
# ![Awakener's Orb](https://web.poecdn.com/image/Art/2DItems/Currency/TransferOrb.png) Awakened PoE Trade # ![Awakener's Orb](https://web.poecdn.com/image/Art/2DItems/Currency/TransferOrb.png) Awakened PoE2 Trade2
[![](https://user-images.githubusercontent.com/4292308/153364874-dde23599-278c-4350-8d86-dadbc4b978b3.svg)](https://somsubhra.github.io/github-release-stats/?username=SnosMe&repository=awakened-poe-trade) [![](https://user-images.githubusercontent.com/4292308/153364874-dde23599-278c-4350-8d86-dadbc4b978b3.svg)](https://somsubhra.github.io/github-release-stats/?username=SnosMe&repository=awakened-poe-trade)
[![](https://user-images.githubusercontent.com/4292308/153364769-e4fe1e82-1bbc-46ac-8a3c-f5a98a5667cc.svg)](https://patreon.com/awakened_poe_trade) [![](https://user-images.githubusercontent.com/4292308/153364769-e4fe1e82-1bbc-46ac-8a3c-f5a98a5667cc.svg)](https://patreon.com/awakened_poe_trade)
[![](https://user-images.githubusercontent.com/4292308/153364565-7a545d26-e617-4a33-a919-ff90d8feda3d.svg)](https://github.com/SnosMe/awakened-poe-trade/issues/22)
➡ [Download for Windows & Linux](https://snosme.github.io/awakened-poe-trade/download) ⬅ ➡ [Download for Windows & Linux](https://snosme.github.io/awakened-poe-trade/download) ⬅
@@ -10,7 +10,7 @@
## Tool showcase ## Tool showcase
| Gem | Rare | Unique | Currency | | Gem | Rare | Unique | Currency |
|-----|------|--------|----------| | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ |
| ![](https://i.imgur.com/LTsH2DZ.png) | ![](https://i.imgur.com/2XL5Wl8.png) | ![](https://i.imgur.com/UTV6prE.png) | ![](https://i.imgur.com/dQ9Sns6.png) | | ![](https://i.imgur.com/LTsH2DZ.png) | ![](https://i.imgur.com/2XL5Wl8.png) | ![](https://i.imgur.com/UTV6prE.png) | ![](https://i.imgur.com/dQ9Sns6.png) |
### Development ### Development

View File

@@ -23,6 +23,10 @@
"renderer/": true, "renderer/": true,
"docs/": true, "docs/": true,
"dist/": true "dist/": true
} },
"editor.tabSize": 2,
"conventionalCommits.scopes": [
"Update to 2"
]
} }
} }

View File

@@ -1,10 +1,10 @@
import { defineConfig } from 'vitepress' import { defineConfig } from 'vitepress'
const BASE = '/awakened-poe-trade/' const BASE = '/awakened-poe2-trade2/'
export default defineConfig({ export default defineConfig({
title: 'Awakened PoE Trade', title: 'Awakened PoE2 Trade2',
description: 'App for price-checking items in Path of Exile', description: 'App for price-checking items in Path of Exile 2',
base: BASE, base: BASE,
mpa: true, mpa: true,
head: [ head: [
@@ -22,14 +22,9 @@ export default defineConfig({
// logo: 'TODO', https://github.com/vuejs/vitepress/issues/1401 // logo: 'TODO', https://github.com/vuejs/vitepress/issues/1401
appVersion: '3.25.101', appVersion: '3.25.101',
github: { github: {
releasesUrl: 'https://github.com/SnosMe/awakened-poe-trade/releases' releasesUrl: 'https://github.com/Kvan7/awakened-poe2-trade2/releases'
}, },
socialLinks: [ socialLinks: [
{
text: 'Discord',
color: '#7289DA',
link: 'https://github.com/SnosMe/awakened-poe-trade/issues/22'
},
{ {
text: 'Patreon', text: 'Patreon',
color: '#FF424D', color: '#FF424D',
@@ -38,7 +33,7 @@ export default defineConfig({
{ {
text: 'GitHub', text: 'GitHub',
color: '#181717', color: '#181717',
link: 'https://github.com/SnosMe/awakened-poe-trade' link: 'https://github.com/Kvan7/awakened-poe2-trade2'
} }
], ],
sidebar: [ sidebar: [

View File

@@ -12,11 +12,11 @@ You can download Awakened Poe Trade here. Any other mirrors are not known
to the developer, downloading from them may be unsafe. to the developer, downloading from them may be unsafe.
| Download link | Automatic updates | Startup time | | Download link | Automatic updates | Startup time |
|---------------|-------------------|--------------| | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ------------ |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-Setup-${theme.appVersion}.exe`">Windows 10+ (installer)</a> | ✔ | Fast | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE2-Trade2-Setup-${theme.appVersion}.exe`">Windows 10+ (installer)</a> | ✔ | Fast |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE2-Trade2-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-${theme.appVersion}.AppImage`">Linux (AppImage)</a> | ✔ | n/a | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE2-Trade2-${theme.appVersion}.AppImage`">Linux (AppImage)</a> | ✔ | n/a |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE2-Trade2-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a |
Latest version is <span class="bg-gray-100 border rounded px-1">{{ theme.appVersion }}</span> Latest version is <span class="bg-gray-100 border rounded px-1">{{ theme.appVersion }}</span>
@@ -36,6 +36,6 @@ warnings on Windows and [macOS](https://support.apple.com/en-us/HT202491#openany
No Administrator rights required, but\ No Administrator rights required, but\
⚠ **If you run PoE client as Admin, OS security boundaries take effect. ⚠ **If you run PoE client as Admin, OS security boundaries take effect.
In order for Awakened PoE Trade to have access to the PoE window, it must be started with Administrator rights.** In order for Awakened PoE2 Trade2 to have access to the PoE window, it must be started with Administrator rights.**
**Not compatible with "GeForce Now" or any other cloud gaming service that do not forward clipboard data.** **Not compatible with "GeForce Now" or any other cloud gaming service that do not forward clipboard data.**

View File

@@ -4,7 +4,7 @@ title: FAQ
- **Where can I change settings, league?** - **Where can I change settings, league?**
Open Path of Exile and press overlay key `Shift + Space`. Click on the button with cog icon there. Open Path of Exile 2 and press overlay key `Shift + Space`. Click on the button with cog icon there.
![](https://i.imgur.com/81L9Cp0.png) ![](https://i.imgur.com/81L9Cp0.png)
- **Where can I find the logs?** - **Where can I find the logs?**

View File

@@ -14,7 +14,7 @@ title: Common issues
If Awakened works for you with DirectX11/12 renderer, If Awakened works for you with DirectX11/12 renderer,
then problem is old Vulkan drivers for sure. then problem is old Vulkan drivers for sure.
4. Delete `%appdata%\awakened-poe-trade` 4. Delete `%appdata%\awakened-poe2-trade2`
If needed, backup `apt-data` folder with your configuration inside. If needed, backup `apt-data` folder with your configuration inside.
@@ -22,7 +22,7 @@ title: Common issues
Launch them later one at a time to identify **conflict**. Launch them later one at a time to identify **conflict**.
6. Restart Awakened PoE Trade. 6. Restart Awakened PoE2 Trade2.
*(don't forget to quit first, otherwise launching second instance will do nothing).* *(don't forget to quit first, otherwise launching second instance will do nothing).*

View File

@@ -4,8 +4,8 @@ title: Quick Start
#### First of all, how does it work? {:style="margin-top: 0;"} #### First of all, how does it work? {:style="margin-top: 0;"}
When you press `Ctrl + C` Path of Exile copies the item's text (under cursor, if any) to the clipboard. When you press `Ctrl + C` Path of Exile 2 copies the item's text (under cursor, if any) to the clipboard.
All that remains is to parse text in Awakened PoE Trade and show to you in a fancy way. All that remains is to parse text in Awakened PoE2 Trade2 and show to you in a fancy way.
### Usage ### Usage

View File

@@ -1,6 +1,6 @@
publish: publish:
- "github" - "github"
productName: "Awakened PoE Trade" productName: "Awakened PoE2 Trade2"
npmRebuild: false npmRebuild: false
files: files:
- "package.json" - "package.json"

View File

@@ -1,6 +1,6 @@
{ {
"name": "awakened-poe-trade", "name": "awakened-poe2-trade2",
"version": "3.25.102", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "node build/script.mjs", "dev": "node build/script.mjs",
@@ -12,7 +12,7 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/SnosMe/awakened-poe-trade.git" "url": "https://github.com/Kvan7/awakened-poe2-trade2.git"
}, },
"main": "dist/main.js", "main": "dist/main.js",
"dependencies": { "dependencies": {

View File

@@ -1,11 +1,11 @@
import path from 'path' import path from "path";
import { app, Tray, Menu, shell, nativeImage, dialog } from 'electron' import { app, Tray, Menu, shell, nativeImage, dialog } from "electron";
import type { ServerEvents } from './server' import type { ServerEvents } from "./server";
export class AppTray { export class AppTray {
public overlayKey = 'Shift + Space' public overlayKey = "Shift + Space";
private tray: Tray private tray: Tray;
serverPort = 0 serverPort = 0;
constructor(server: ServerEvents) { constructor(server: ServerEvents) {
let trayImage = nativeImage.createFromPath( let trayImage = nativeImage.createFromPath(
@@ -14,57 +14,57 @@ export class AppTray {
process.env.STATIC!, process.env.STATIC!,
process.platform === "win32" ? "icon.ico" : "icon.png" process.platform === "win32" ? "icon.ico" : "icon.png"
) )
) );
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// Mac image size needs to be smaller, or else it looks huge. Size // Mac image size needs to be smaller, or else it looks huge. Size
// guideline is from https://iconhandbook.co.uk/reference/chart/osx/ // guideline is from https://iconhandbook.co.uk/reference/chart/osx/
trayImage = trayImage.resize({ width: 22, height: 22 }) trayImage = trayImage.resize({ width: 22, height: 22 });
} }
this.tray = new Tray(trayImage) this.tray = new Tray(trayImage);
this.tray.setToolTip(`Awakened PoE Trade v${app.getVersion()}`) this.tray.setToolTip(`Awakened PoE2 Trade2 v${app.getVersion()}`);
this.rebuildMenu() this.rebuildMenu();
server.onEventAnyClient('CLIENT->MAIN::user-action', ({ action }) => { server.onEventAnyClient("CLIENT->MAIN::user-action", ({ action }) => {
if (action === 'quit') { if (action === "quit") {
app.quit() app.quit();
} }
}) });
} }
rebuildMenu() { rebuildMenu() {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ {
label: 'Settings/League', label: "Settings/League",
click: () => { click: () => {
dialog.showMessageBox({ dialog.showMessageBox({
title: 'Settings', title: "Settings",
message: `Open Path of Exile and press "${this.overlayKey}". Click on the button with cog icon there.` message: `Open Path of Exile 2 and press "${this.overlayKey}". Click on the button with cog icon there.`,
}) });
} },
}, },
{ {
label: 'Open in Browser', label: "Open in Browser",
click: () => { click: () => {
shell.openExternal(`http://localhost:${this.serverPort}`) shell.openExternal(`http://localhost:${this.serverPort}`);
}
}, },
{ type: 'separator' }, },
{ type: "separator" },
{ {
label: 'Open config folder', label: "Open config folder",
click: () => { click: () => {
shell.openPath(path.join(app.getPath('userData'), 'apt-data')) shell.openPath(path.join(app.getPath("userData"), "apt-data"));
} },
}, },
{ {
label: 'Quit', label: "Quit",
click: () => { click: () => {
app.quit() app.quit();
} },
} },
]) ]);
this.tray.setContextMenu(contextMenu) this.tray.setContextMenu(contextMenu);
} }
} }

View File

@@ -1,100 +1,131 @@
import fs from 'fs/promises' 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 { hotkeyToString, CodeToKey } from '../../../ipc/KeyToCode' import { hotkeyToString, CodeToKey } from "../../../ipc/KeyToCode";
import { guessFileLocation } from './utils' import { guessFileLocation } from "./utils";
import type { Logger } from '../RemoteLogger' import type { Logger } from "../RemoteLogger";
import type { ServerEvents } from '../server' import type { ServerEvents } from "../server";
const POSSIBLE_PATH = const POSSIBLE_PATH =
(process.platform === 'win32') ? [ process.platform === "win32"
path.join(app.getPath('documents'), 'My Games\\Path of Exile\\production_Config.ini') ? [
] : (process.platform === 'linux') ? [ path.join(
path.join(app.getPath('documents'), 'My Games/Path of Exile/production_Config.ini'), app.getPath("documents"),
path.join(app.getPath('home'), '.local/share/Steam/steamapps/compatdata/238960/pfx/drive_c/users/steamuser/Documents/My Games/Path of Exile/production_Config.ini') "My Games\\Path of Exile 2\\production_Config.ini"
] : (process.platform === 'darwin') ? [ ),
path.join(app.getPath('appData'), 'Path of Exile/Preferences/production_Config.ini') ]
] : [] : process.platform === "linux"
? [
path.join(
app.getPath("documents"),
"My Games/Path of Exile 2/production_Config.ini"
),
path.join(
app.getPath("home"),
".local/share/Steam/steamapps/compatdata/238960/pfx/drive_c/users/steamuser/Documents/My Games/Path of Exile 2/production_Config.ini"
),
]
: process.platform === "darwin"
? [
path.join(
app.getPath("appData"),
"Path of Exile 2/Preferences/production_Config.ini"
),
]
: [];
export class GameConfig { export class GameConfig {
private _wantedPath: string | null = null private _wantedPath: string | null = null;
private _actualPath: string | null = null private _actualPath: string | null = null;
get actualPath () { return this._actualPath } get actualPath() {
return this._actualPath;
}
private _showModsKey: string | null = null private _showModsKey: string | null = null;
get showModsKeyNullable () { return this._showModsKey } get showModsKeyNullable() {
get showModsKey () { return this._showModsKey ?? 'Alt' } return this._showModsKey;
}
get showModsKey() {
return this._showModsKey ?? "Alt";
}
constructor ( constructor(private server: ServerEvents, private logger: Logger) {}
private server: ServerEvents,
private logger: Logger
) {}
async readConfig(filePath: string) { async readConfig(filePath: string) {
if (this._wantedPath !== filePath) { if (this._wantedPath !== filePath) {
this._wantedPath = filePath this._wantedPath = filePath;
this._actualPath = null this._actualPath = null;
} else { } else {
return return;
} }
if (!filePath.length) { if (!filePath.length) {
const guessedPath = await guessFileLocation(POSSIBLE_PATH) const guessedPath = await guessFileLocation(POSSIBLE_PATH);
if (guessedPath != null) { if (guessedPath != null) {
filePath = guessedPath filePath = guessedPath;
} else { } else {
this.logger.write('error [GameConfig] Failed to find game configuration file in the default location.') this.logger.write(
return "error [GameConfig] Failed to find game configuration file in the default location."
);
return;
} }
} }
try { try {
let contents = await fs.readFile(filePath, { encoding: 'utf-8', flag: 'r' }) let contents = await fs.readFile(filePath, {
contents = contents.trimStart() // remove BOM encoding: "utf-8",
const parsed = ini.parse(contents) flag: "r",
});
contents = contents.trimStart(); // remove BOM
const parsed = ini.parse(contents);
this._showModsKey = this.parseConfigHotkey( this._showModsKey = this.parseConfigHotkey(
parsed['ACTION_KEYS']?.['show_advanced_item_descriptions']) parsed["ACTION_KEYS"]?.["show_advanced_item_descriptions"]
);
this._actualPath = filePath this._actualPath = filePath;
} catch { } catch {
this.logger.write('error [GameConfig] Failed to read game configuration file.') this.logger.write(
"error [GameConfig] Failed to read game configuration file."
);
} }
} }
private parseConfigHotkey(cfgKey?: string): string | null { private parseConfigHotkey(cfgKey?: string): string | null {
if (!cfgKey) return null if (!cfgKey) return null;
const [keyMain, keyMod] = cfgKey.split(' ') const [keyMain, keyMod] = cfgKey.split(" ");
let key1: string let key1: string;
if (CodeToKey[keyMain]) { if (CodeToKey[keyMain]) {
key1 = CodeToKey[keyMain] key1 = CodeToKey[keyMain];
} else { } else {
this.logger.write(`error [GameConfig] Failed to read key: ${cfgKey}.`) this.logger.write(`error [GameConfig] Failed to read key: ${cfgKey}.`);
return null return null;
} }
let key2: string | undefined let key2: string | undefined;
if (keyMod) { if (keyMod) {
if (keyMod === '1') { if (keyMod === "1") {
key2 = 'Shift' key2 = "Shift";
} else if (keyMod === '2') { } else if (keyMod === "2") {
key2 = 'Ctrl' key2 = "Ctrl";
} else if (keyMod === '3') { } else if (keyMod === "3") {
key2 = 'Alt' key2 = "Alt";
} else { } else {
this.logger.write(`error [GameConfig] Failed to read modifier key: ${cfgKey}.`) this.logger.write(
return null `error [GameConfig] Failed to read modifier key: ${cfgKey}.`
);
return null;
} }
} }
return hotkeyToString( return hotkeyToString(
[key1], [key1],
key2 === 'Ctrl', key2 === "Ctrl",
key2 === 'Shift', key2 === "Shift",
key2 === 'Alt' key2 === "Alt"
) );
} }
} }

View File

@@ -1,101 +1,118 @@
import { promises as fs, watchFile, unwatchFile } from 'fs' import { promises as fs, watchFile, unwatchFile } from "fs";
import path from 'path' import path from "path";
import { app } from 'electron' import { app } from "electron";
import { guessFileLocation } from './utils' import { guessFileLocation } from "./utils";
import { ServerEvents } from '../server' import { ServerEvents } from "../server";
import { Logger } from '../RemoteLogger' import { Logger } from "../RemoteLogger";
const POSSIBLE_PATH = const POSSIBLE_PATH =
(process.platform === 'win32') ? [ process.platform === "win32"
'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' "C:\\Program Files (x86)\\Grinding Gear Games\\Path of Exile 2\\logs\\Client.txt",
] : (process.platform === 'linux') ? [ "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Path of Exile 2\\logs\\Client.txt",
path.join(app.getPath('home'), '.wine/drive_c/Program Files (x86)/Grinding Gear Games/Path of Exile/logs/Client.txt'), ]
path.join(app.getPath('home'), '.local/share/Steam/steamapps/common/Path of Exile/logs/Client.txt') : process.platform === "linux"
] : (process.platform === 'darwin') ? [ ? [
path.join(app.getPath('home'), 'Library/Caches/com.GGG.PathOfExile/Logs/Client.txt') path.join(
] : [] app.getPath("home"),
".wine/drive_c/Program Files (x86)/Grinding Gear Games/Path of Exile 2/logs/Client.txt"
),
path.join(
app.getPath("home"),
".local/share/Steam/steamapps/common/Path of Exile 2/logs/Client.txt"
),
]
: process.platform === "darwin"
? [
path.join(
app.getPath("home"),
"Library/Caches/com.GGG.PathOfExile/Logs/Client.txt"
),
]
: [];
export class GameLogWatcher { export class GameLogWatcher {
private _wantedPath: string | null = null private _wantedPath: string | null = null;
get actualPath () { return this._state?.path ?? null } get actualPath() {
return this._state?.path ?? null;
}
private _state: { private _state: {
offset: number offset: number;
path: string path: string;
file: fs.FileHandle file: fs.FileHandle;
isReading: boolean isReading: boolean;
readBuff: Buffer readBuff: Buffer;
} | null = null } | null = null;
constructor ( constructor(private server: ServerEvents, private logger: Logger) {}
private server: ServerEvents,
private logger: Logger,
) {}
async restart(logFile: string) { async restart(logFile: string) {
if (this._wantedPath !== logFile) { if (this._wantedPath !== logFile) {
this._wantedPath = logFile this._wantedPath = logFile;
if (this._state) { if (this._state) {
unwatchFile(this._state.path) unwatchFile(this._state.path);
await this._state.file.close() await this._state.file.close();
this._state = null this._state = null;
} }
} else { } else {
return return;
} }
if (!logFile.length) { if (!logFile.length) {
const guessedPath = await guessFileLocation(POSSIBLE_PATH) const guessedPath = await guessFileLocation(POSSIBLE_PATH);
if (guessedPath != null) { if (guessedPath != null) {
logFile = guessedPath logFile = guessedPath;
} else { } else {
return return;
} }
} }
try { try {
const file = await fs.open(logFile, 'r') const file = await fs.open(logFile, "r");
const stats = await file.stat() const stats = await file.stat();
watchFile(logFile, { interval: 450 }, this.handleFileChange.bind(this)) watchFile(logFile, { interval: 450 }, this.handleFileChange.bind(this));
this._state = { this._state = {
path: logFile, path: logFile,
file: file, file: file,
offset: stats.size, offset: stats.size,
isReading: false, isReading: false,
readBuff: Buffer.allocUnsafe(64 * 1024), readBuff: Buffer.allocUnsafe(64 * 1024),
} };
} catch { } catch {
this.logger.write('error [GameLogWatcher] Failed to watch file.') this.logger.write("error [GameLogWatcher] Failed to watch file.");
} }
} }
private handleFileChange() { private handleFileChange() {
if (this._state && !this._state.isReading) { if (this._state && !this._state.isReading) {
this._state.isReading = true this._state.isReading = true;
this.readToEOF() this.readToEOF();
} }
} }
private async readToEOF() { private async readToEOF() {
if (!this._state) return if (!this._state) return;
const { file, readBuff, offset } = this._state const { file, readBuff, offset } = this._state;
const { bytesRead } = await file.read(readBuff, 0, readBuff.length, offset) const { bytesRead } = await file.read(readBuff, 0, readBuff.length, offset);
if (bytesRead) { if (bytesRead) {
const str = readBuff.toString('utf8', 0, bytesRead) const str = readBuff.toString("utf8", 0, bytesRead);
const lines = str.split('\n').map(line => line.trim()).filter(line => line.length) const lines = str
this.server.sendEventTo('broadcast', { .split("\n")
name: 'MAIN->CLIENT::game-log', .map((line) => line.trim())
payload: { lines } .filter((line) => line.length);
}) this.server.sendEventTo("broadcast", {
name: "MAIN->CLIENT::game-log",
payload: { lines },
});
} }
if (bytesRead) { if (bytesRead) {
this._state.offset += bytesRead this._state.offset += bytesRead;
this.readToEOF() this.readToEOF();
} else { } else {
this._state.isReading = false this._state.isReading = false;
} }
} }
} }

View File

@@ -1,172 +1,188 @@
import path from 'path' import path from "path";
import { BrowserWindow, dialog, shell, Menu, WebContents } from 'electron' import { BrowserWindow, dialog, shell, Menu, WebContents } from "electron";
import { OverlayController, OVERLAY_WINDOW_OPTS } from 'electron-overlay-window' import {
import type { ServerEvents } from '../server' OverlayController,
import type { Logger } from '../RemoteLogger' OVERLAY_WINDOW_OPTS,
import type { GameWindow } from './GameWindow' } from "electron-overlay-window";
import type { ServerEvents } from "../server";
import type { Logger } from "../RemoteLogger";
import type { GameWindow } from "./GameWindow";
export class OverlayWindow { export class OverlayWindow {
public isInteractable = false public isInteractable = false;
public wasUsedRecently = true public wasUsedRecently = true;
private window?: BrowserWindow private window?: BrowserWindow;
private overlayKey: string = 'Shift + Space' private overlayKey: string = "Shift + Space";
private isOverlayKeyUsed = false private isOverlayKeyUsed = false;
constructor( constructor(
private server: ServerEvents, private server: ServerEvents,
private logger: Logger, private logger: Logger,
private poeWindow: GameWindow, private poeWindow: GameWindow
) { ) {
this.server.onEventAnyClient('OVERLAY->MAIN::focus-game', this.assertGameActive) this.server.onEventAnyClient(
this.poeWindow.on('active-change', this.handlePoeWindowActiveChange) "OVERLAY->MAIN::focus-game",
this.poeWindow.onAttach(this.handleOverlayAttached) this.assertGameActive
);
this.poeWindow.on("active-change", this.handlePoeWindowActiveChange);
this.poeWindow.onAttach(this.handleOverlayAttached);
this.server.onEventAnyClient('CLIENT->MAIN::used-recently', (e) => { this.server.onEventAnyClient("CLIENT->MAIN::used-recently", (e) => {
this.wasUsedRecently = e.isOverlay this.wasUsedRecently = e.isOverlay;
}) });
if (process.argv.includes('--no-overlay')) return if (process.argv.includes("--no-overlay")) return;
this.window = new BrowserWindow({ this.window = new BrowserWindow({
icon: path.join(__dirname, process.env.STATIC!, 'icon.png'), icon: path.join(__dirname, process.env.STATIC!, "icon.png"),
...OVERLAY_WINDOW_OPTS, ...OVERLAY_WINDOW_OPTS,
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
allowRunningInsecureContent: false, allowRunningInsecureContent: false,
webviewTag: true, webviewTag: true,
spellcheck: false spellcheck: false,
},
});
this.window.setMenu(
Menu.buildFromTemplate([
{ role: "editMenu" },
{ role: "reload" },
{ role: "toggleDevTools" },
])
);
this.window.webContents.on("before-input-event", this.handleExtraCommands);
this.window.webContents.on(
"did-attach-webview",
(_, webviewWebContents) => {
webviewWebContents.on("before-input-event", this.handleExtraCommands);
} }
}) );
this.window.setMenu(Menu.buildFromTemplate([
{ role: 'editMenu' },
{ role: 'reload' },
{ role: 'toggleDevTools' }
]))
this.window.webContents.on('before-input-event', this.handleExtraCommands)
this.window.webContents.on('did-attach-webview', (_, webviewWebContents) => {
webviewWebContents.on('before-input-event', this.handleExtraCommands)
})
this.window.webContents.setWindowOpenHandler((details) => { this.window.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url) shell.openExternal(details.url);
return { action: 'deny' } return { action: "deny" };
}) });
} }
loadAppPage(port: number) { loadAppPage(port: number) {
const url = process.env.VITE_DEV_SERVER_URL || const url =
`http://localhost:${port}/index.html` process.env.VITE_DEV_SERVER_URL || `http://localhost:${port}/index.html`;
if (!this.window) { if (!this.window) {
shell.openExternal(url) shell.openExternal(url);
return return;
} }
if (process.env.VITE_DEV_SERVER_URL) { if (process.env.VITE_DEV_SERVER_URL) {
this.window.loadURL(url) this.window.loadURL(url);
this.window.webContents.openDevTools({ mode: 'detach', activate: false }) this.window.webContents.openDevTools({ mode: "detach", activate: false });
} else { } else {
this.window.loadURL(url) this.window.loadURL(url);
} }
} }
assertOverlayActive = () => { assertOverlayActive = () => {
if (!this.isInteractable) { if (!this.isInteractable) {
this.isInteractable = true this.isInteractable = true;
OverlayController.activateOverlay() OverlayController.activateOverlay();
this.poeWindow.isActive = false this.poeWindow.isActive = false;
}
} }
};
assertGameActive = () => { assertGameActive = () => {
if (this.isInteractable) { if (this.isInteractable) {
this.isInteractable = false this.isInteractable = false;
OverlayController.focusTarget() OverlayController.focusTarget();
this.poeWindow.isActive = true this.poeWindow.isActive = true;
}
} }
};
toggleActiveState = () => { toggleActiveState = () => {
this.isOverlayKeyUsed = true this.isOverlayKeyUsed = true;
if (this.isInteractable) { if (this.isInteractable) {
this.assertGameActive() this.assertGameActive();
} else { } else {
this.assertOverlayActive() this.assertOverlayActive();
}
} }
};
updateOpts(overlayKey: string, windowTitle: string) { updateOpts(overlayKey: string, windowTitle: string) {
this.overlayKey = overlayKey this.overlayKey = overlayKey;
this.poeWindow.attach(this.window, windowTitle) this.poeWindow.attach(this.window, windowTitle);
} }
private handleExtraCommands = (event: Electron.Event, input: Electron.Input) => { private handleExtraCommands = (
if (input.type !== 'keyDown') return event: Electron.Event,
input: Electron.Input
) => {
if (input.type !== "keyDown") return;
let { code, control: ctrlKey, shift: shiftKey, alt: altKey } = input let { code, control: ctrlKey, shift: shiftKey, alt: altKey } = input;
if (code.startsWith('Key')) { if (code.startsWith("Key")) {
code = code.slice('Key'.length) code = code.slice("Key".length);
} else if (code.startsWith('Digit')) { } else if (code.startsWith("Digit")) {
code = code.slice('Digit'.length) code = code.slice("Digit".length);
} }
if (shiftKey && altKey) code = `Shift + Alt + ${code}` if (shiftKey && altKey) code = `Shift + Alt + ${code}`;
else if (ctrlKey && shiftKey) code = `Ctrl + Shift + ${code}` else if (ctrlKey && shiftKey) code = `Ctrl + Shift + ${code}`;
else if (ctrlKey && altKey) code = `Ctrl + Alt + ${code}` else if (ctrlKey && altKey) code = `Ctrl + Alt + ${code}`;
else if (altKey) code = `Alt + ${code}` else if (altKey) code = `Alt + ${code}`;
else if (ctrlKey) code = `Ctrl + ${code}` else if (ctrlKey) code = `Ctrl + ${code}`;
else if (shiftKey) code = `Shift + ${code}` else if (shiftKey) code = `Shift + ${code}`;
switch (code) { switch (code) {
case 'Escape': case "Escape":
case 'Ctrl + W': { case "Ctrl + W": {
event.preventDefault() event.preventDefault();
process.nextTick(this.assertGameActive) process.nextTick(this.assertGameActive);
break break;
} }
case this.overlayKey: { case this.overlayKey: {
event.preventDefault() event.preventDefault();
process.nextTick(this.toggleActiveState) process.nextTick(this.toggleActiveState);
break break;
}
} }
} }
};
private handleOverlayAttached = (hasAccess?: boolean) => { private handleOverlayAttached = (hasAccess?: boolean) => {
if (hasAccess === false) { if (hasAccess === false) {
this.logger.write('error [Overlay] PoE is running with administrator rights') this.logger.write(
"error [Overlay] PoE is running with administrator rights"
);
dialog.showErrorBox( dialog.showErrorBox(
'PoE window - No access', "PoE window - No access",
// ---------------------- // ----------------------
'Path of Exile is running with administrator rights.\n' + "Path of Exile 2 is running with administrator rights.\n" +
'\n' + "\n" +
'You need to restart Awakened PoE Trade with administrator rights.' "You need to restart Awakened PoE2 Trade2 with administrator rights."
) );
} else { } else {
this.server.sendEventTo('broadcast', { this.server.sendEventTo("broadcast", {
name: 'MAIN->OVERLAY::overlay-attached', name: "MAIN->OVERLAY::overlay-attached",
payload: undefined payload: undefined,
}) });
}
} }
};
private handlePoeWindowActiveChange = (isActive: boolean) => { private handlePoeWindowActiveChange = (isActive: boolean) => {
if (isActive && this.isInteractable) { if (isActive && this.isInteractable) {
this.isInteractable = false this.isInteractable = false;
} }
this.server.sendEventTo('broadcast', { this.server.sendEventTo("broadcast", {
name: 'MAIN->OVERLAY::focus-change', name: "MAIN->OVERLAY::focus-change",
payload: { payload: {
game: isActive, game: isActive,
overlay: this.isInteractable, overlay: this.isInteractable,
usingHotkey: this.isOverlayKeyUsed usingHotkey: this.isOverlayKeyUsed,
} },
}) });
this.isOverlayKeyUsed = false this.isOverlayKeyUsed = false;
} };
} }

View File

@@ -4,12 +4,15 @@ module.exports = {
node: true node: true
}, },
plugins: [ plugins: [
'@typescript-eslint' '@typescript-eslint',
'prettier'
// 'only-warn' // 'only-warn'
], ],
extends: [ extends: [
'plugin:vue/base', 'plugin:vue/base',
'standard-with-typescript' 'standard-with-typescript',
'plugin:prettier/recommended',
'eslint-plugin-prettier/recommended',
], ],
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
@@ -36,7 +39,8 @@ module.exports = {
'import/no-duplicates': 'off', 'import/no-duplicates': 'off',
'func-call-spacing': 'off', 'func-call-spacing': 'off',
// TODO: refactor IPC and enable // TODO: refactor IPC and enable
'@typescript-eslint/consistent-type-assertions': 'off' '@typescript-eslint/consistent-type-assertions': 'off',
"indent": ["error", "tab"]
}, },
overrides: [{ overrides: [{
files: ['src/main/**/*'], files: ['src/main/**/*'],

7
renderer/.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"singleQuote": true,
"endOfLine": "lf",
"tabWidth": 2,
"useTabs": true,
"trailingComma": "es5"
}

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="color-scheme" content="dark"> <meta name="color-scheme" content="dark">
<link rel="icon" href="/icon.ico"> <link rel="icon" href="/icon.ico">
<title>Awakened PoE Trade</title> <title>Awakened PoE Trade2</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@@ -1,11 +1,11 @@
{ {
"name": "awakened-poe-trade", "name": "awakened-poe2-trade2",
"version": "0.0.0", "version": "0.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "awakened-poe-trade", "name": "awakened-poe2-trade2",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "6.x.x", "@fortawesome/fontawesome-free": "6.x.x",
@@ -33,7 +33,10 @@
"@types/object-hash": "^3.0.0", "@types/object-hash": "^3.0.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"autoprefixer": "^10.0.2", "autoprefixer": "^10.0.2",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"postcss": "^8.2.14", "postcss": "^8.2.14",
"prettier": "3.4.2",
"typescript": "5.6.x", "typescript": "5.6.x",
"vite": "^5.0.0", "vite": "^5.0.0",
"vue-tsc": "^2.0.0" "vue-tsc": "^2.0.0"
@@ -502,8 +505,8 @@
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
"integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"eslint-visitor-keys": "^3.4.3" "eslint-visitor-keys": "^3.4.3"
}, },
@@ -521,8 +524,8 @@
"version": "4.12.1", "version": "4.12.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
"integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0" "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
} }
@@ -531,8 +534,8 @@
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"ajv": "^6.12.4", "ajv": "^6.12.4",
"debug": "^4.3.2", "debug": "^4.3.2",
@@ -555,8 +558,8 @@
"version": "8.57.1", "version": "8.57.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
@@ -575,8 +578,8 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
"integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
"deprecated": "Use @eslint/config-array instead", "deprecated": "Use @eslint/config-array instead",
"devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true,
"dependencies": { "dependencies": {
"@humanwhocodes/object-schema": "^2.0.3", "@humanwhocodes/object-schema": "^2.0.3",
"debug": "^4.3.1", "debug": "^4.3.1",
@@ -590,8 +593,8 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true,
"engines": { "engines": {
"node": ">=12.22" "node": ">=12.22"
}, },
@@ -605,8 +608,8 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"deprecated": "Use @eslint/object-schema instead", "deprecated": "Use @eslint/object-schema instead",
"license": "BSD-3-Clause", "devOptional": true,
"optional": true "license": "BSD-3-Clause"
}, },
"node_modules/@intlify/core-base": { "node_modules/@intlify/core-base": {
"version": "10.0.4", "version": "10.0.4",
@@ -789,6 +792,19 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@pkgr/core": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
"integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@popperjs/core": { "node_modules/@popperjs/core": {
"version": "2.11.8", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -1383,8 +1399,8 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"license": "ISC", "devOptional": true,
"optional": true "license": "ISC"
}, },
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "4.6.2", "version": "4.6.2",
@@ -1739,8 +1755,8 @@
"version": "8.14.0", "version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -1752,8 +1768,8 @@
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"peerDependencies": { "peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
@@ -1762,8 +1778,8 @@
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
@@ -1855,8 +1871,8 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0", "devOptional": true,
"optional": true "license": "Python-2.0"
}, },
"node_modules/array-buffer-byte-length": { "node_modules/array-buffer-byte-length": {
"version": "1.0.1", "version": "1.0.1",
@@ -2071,8 +2087,8 @@
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@@ -2157,8 +2173,8 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -2197,8 +2213,8 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
@@ -2277,8 +2293,8 @@
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
@@ -2377,8 +2393,8 @@
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -2395,8 +2411,8 @@
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/define-data-property": { "node_modules/define-data-property": {
"version": "1.1.4", "version": "1.1.4",
@@ -2463,8 +2479,8 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true,
"dependencies": { "dependencies": {
"esutils": "^2.0.2" "esutils": "^2.0.2"
}, },
@@ -2712,8 +2728,8 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -2726,8 +2742,8 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1", "@eslint-community/regexpp": "^4.6.1",
@@ -2778,6 +2794,19 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint-config-prettier": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
"dev": true,
"license": "MIT",
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-config-standard": { "node_modules/eslint-config-standard": {
"version": "17.0.0", "version": "17.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz",
@@ -3014,6 +3043,37 @@
"eslint": ">=7.0.0" "eslint": ">=7.0.0"
} }
}, },
"node_modules/eslint-plugin-prettier": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
"integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.9.1"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": "*",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-promise": { "node_modules/eslint-plugin-promise": {
"version": "6.6.0", "version": "6.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz",
@@ -3100,8 +3160,8 @@
"version": "3.4.3", "version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true,
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
@@ -3113,8 +3173,8 @@
"version": "7.2.2", "version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"dependencies": { "dependencies": {
"esrecurse": "^4.3.0", "esrecurse": "^4.3.0",
"estraverse": "^5.2.0" "estraverse": "^5.2.0"
@@ -3130,8 +3190,8 @@
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"engines": { "engines": {
"node": ">=4.0" "node": ">=4.0"
} }
@@ -3140,8 +3200,8 @@
"version": "9.6.1", "version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"dependencies": { "dependencies": {
"acorn": "^8.9.0", "acorn": "^8.9.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "^5.3.2",
@@ -3158,8 +3218,8 @@
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
"devOptional": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"optional": true,
"dependencies": { "dependencies": {
"estraverse": "^5.1.0" "estraverse": "^5.1.0"
}, },
@@ -3171,8 +3231,8 @@
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"engines": { "engines": {
"node": ">=4.0" "node": ">=4.0"
} }
@@ -3181,8 +3241,8 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"dependencies": { "dependencies": {
"estraverse": "^5.2.0" "estraverse": "^5.2.0"
}, },
@@ -3194,8 +3254,8 @@
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"engines": { "engines": {
"node": ">=4.0" "node": ">=4.0"
} }
@@ -3220,8 +3280,8 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -3232,6 +3292,13 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@@ -3264,15 +3331,15 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/fast-levenshtein": { "node_modules/fast-levenshtein": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/fastest-levenshtein": { "node_modules/fastest-levenshtein": {
"version": "1.0.16", "version": "1.0.16",
@@ -3296,8 +3363,8 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"flat-cache": "^3.0.4" "flat-cache": "^3.0.4"
}, },
@@ -3321,8 +3388,8 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"locate-path": "^6.0.0", "locate-path": "^6.0.0",
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
@@ -3338,8 +3405,8 @@
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
"integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"flatted": "^3.2.9", "flatted": "^3.2.9",
"keyv": "^4.5.3", "keyv": "^4.5.3",
@@ -3353,8 +3420,8 @@
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
"license": "ISC", "devOptional": true,
"optional": true "license": "ISC"
}, },
"node_modules/for-each": { "node_modules/for-each": {
"version": "0.3.3", "version": "0.3.3",
@@ -3400,8 +3467,8 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"license": "ISC", "devOptional": true,
"optional": true "license": "ISC"
}, },
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
@@ -3498,8 +3565,8 @@
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported", "deprecated": "Glob versions prior to v9 are no longer supported",
"devOptional": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
@@ -3531,8 +3598,8 @@
"version": "13.24.0", "version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"type-fest": "^0.20.2" "type-fest": "^0.20.2"
}, },
@@ -3547,8 +3614,8 @@
"version": "0.20.2", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"devOptional": true,
"license": "(MIT OR CC0-1.0)", "license": "(MIT OR CC0-1.0)",
"optional": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -3611,8 +3678,8 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/has-bigints": { "node_modules/has-bigints": {
"version": "1.0.2", "version": "1.0.2",
@@ -3628,8 +3695,8 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -3715,8 +3782,8 @@
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">= 4" "node": ">= 4"
} }
@@ -3725,8 +3792,8 @@
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"parent-module": "^1.0.0", "parent-module": "^1.0.0",
"resolve-from": "^4.0.0" "resolve-from": "^4.0.0"
@@ -3742,8 +3809,8 @@
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=0.8.19" "node": ">=0.8.19"
} }
@@ -3753,8 +3820,8 @@
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"devOptional": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"once": "^1.3.0", "once": "^1.3.0",
"wrappy": "1" "wrappy": "1"
@@ -3764,8 +3831,8 @@
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC", "devOptional": true,
"optional": true "license": "ISC"
}, },
"node_modules/internal-slot": { "node_modules/internal-slot": {
"version": "1.0.7", "version": "1.0.7",
@@ -4031,8 +4098,8 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -4215,8 +4282,8 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
}, },
@@ -4228,22 +4295,22 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/json-schema-traverse": { "node_modules/json-schema-traverse": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/json-stable-stringify-without-jsonify": { "node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/json5": { "node_modules/json5": {
"version": "1.0.2", "version": "1.0.2",
@@ -4262,8 +4329,8 @@
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"json-buffer": "3.0.1" "json-buffer": "3.0.1"
} }
@@ -4272,8 +4339,8 @@
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"prelude-ls": "^1.2.1", "prelude-ls": "^1.2.1",
"type-check": "~0.4.0" "type-check": "~0.4.0"
@@ -4301,8 +4368,8 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"p-locate": "^5.0.0" "p-locate": "^5.0.0"
}, },
@@ -4324,8 +4391,8 @@
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "10.4.3", "version": "10.4.3",
@@ -4377,8 +4444,8 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"devOptional": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
}, },
@@ -4409,8 +4476,8 @@
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/muggle-string": { "node_modules/muggle-string": {
"version": "0.4.1", "version": "0.4.1",
@@ -4452,8 +4519,8 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/natural-compare-lite": { "node_modules/natural-compare-lite": {
"version": "1.4.0", "version": "1.4.0",
@@ -4629,8 +4696,8 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"devOptional": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"wrappy": "1" "wrappy": "1"
} }
@@ -4639,8 +4706,8 @@
"version": "0.9.4", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"deep-is": "^0.1.3", "deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6", "fast-levenshtein": "^2.0.6",
@@ -4657,8 +4724,8 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"yocto-queue": "^0.1.0" "yocto-queue": "^0.1.0"
}, },
@@ -4673,8 +4740,8 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"p-limit": "^3.0.2" "p-limit": "^3.0.2"
}, },
@@ -4695,8 +4762,8 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"callsites": "^3.0.0" "callsites": "^3.0.0"
}, },
@@ -4715,8 +4782,8 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -4725,8 +4792,8 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -4977,18 +5044,47 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -5109,8 +5205,8 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
@@ -5130,8 +5226,8 @@
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported", "deprecated": "Rimraf versions prior to v4 are no longer supported",
"devOptional": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"glob": "^7.1.3" "glob": "^7.1.3"
}, },
@@ -5542,8 +5638,8 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
}, },
@@ -5621,8 +5717,8 @@
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
}, },
@@ -5642,6 +5738,30 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/synckit": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/synckit/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "3.4.15", "version": "3.4.15",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz",
@@ -5683,8 +5803,8 @@
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"license": "MIT", "devOptional": true,
"optional": true "license": "MIT"
}, },
"node_modules/thenify": { "node_modules/thenify": {
"version": "3.3.1", "version": "3.3.1",
@@ -5774,8 +5894,8 @@
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"prelude-ls": "^1.2.1" "prelude-ls": "^1.2.1"
}, },
@@ -5945,8 +6065,8 @@
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true,
"dependencies": { "dependencies": {
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
@@ -6251,8 +6371,8 @@
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -6355,8 +6475,8 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC", "devOptional": true,
"optional": true "license": "ISC"
}, },
"node_modules/xml-name-validator": { "node_modules/xml-name-validator": {
"version": "4.0.0", "version": "4.0.0",
@@ -6384,8 +6504,8 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"optional": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "awakened-poe-trade", "name": "awakened-poe2-trade2",
"version": "0.0.0", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -34,7 +34,10 @@
"@types/object-hash": "^3.0.0", "@types/object-hash": "^3.0.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"autoprefixer": "^10.0.2", "autoprefixer": "^10.0.2",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"postcss": "^8.2.14", "postcss": "^8.2.14",
"prettier": "3.4.2",
"typescript": "5.6.x", "typescript": "5.6.x",
"vite": "^5.0.0", "vite": "^5.0.0",
"vue-tsc": "^2.0.0" "vue-tsc": "^2.0.0"

View File

@@ -7,7 +7,6 @@
"league": "League", "league": "League",
"realm": "Realm", "realm": "Realm",
"realm_intl": "International", "realm_intl": "International",
"app": { "app": {
"leagues_loading": "Loading leagues\u2026", "leagues_loading": "Loading leagues\u2026",
"leagues_failed": "Failed to load leagues", "leagues_failed": "Failed to load leagues",
@@ -21,12 +20,10 @@
"report_bug": "Report a bug on GitHub", "report_bug": "Report a bug on GitHub",
"quit": "Quit" "quit": "Quit"
}, },
"map.mods.heist": "heist", "map.mods.heist": "heist",
"map.mods.uber": "T17", "map.mods.uber": "T17",
"map.mods.outdated": "outdated", "map.mods.outdated": "outdated",
"Support development on": "Support development\u00A0on", "Support development on": "Support development\u00A0on",
"Blighted": "Blighted", "Blighted": "Blighted",
"Blight-ravaged": "Blight-ravaged", "Blight-ravaged": "Blight-ravaged",
"Magic": "Magic", "Magic": "Magic",
@@ -40,7 +37,6 @@
"Anomalous": "Anomalous", "Anomalous": "Anomalous",
"Divergent": "Divergent", "Divergent": "Divergent",
"Phantasmal": "Phantasmal", "Phantasmal": "Phantasmal",
"item": { "item": {
"prop_quality": "Q {0}%", "prop_quality": "Q {0}%",
"base_percentile": "Base Percentile: {0}%", "base_percentile": "Base Percentile: {0}%",
@@ -153,7 +149,6 @@
"hide_anointment": "Buyer will likely change anointment", "hide_anointment": "Buyer will likely change anointment",
"hide_for_crafting": "Select only if price-checking as base item for crafting", "hide_for_crafting": "Select only if price-checking as base item for crafting",
"hide_empty_mod": "Select only if item has 6 modifiers (1 of which is crafted) or if it has 5 modifiers", "hide_empty_mod": "Select only if item has 6 modifiers (1 of which is crafted) or if it has 5 modifiers",
"tag_implicit": "implicit", "tag_implicit": "implicit",
"tag_fractured": "fractured", "tag_fractured": "fractured",
"tag_crafted": "crafted", "tag_crafted": "crafted",
@@ -274,7 +269,7 @@
"stack": "Stack" "stack": "Stack"
}, },
"settings": { "settings": {
"title": "Settings - Awakened PoE Trade", "title": "Settings - Awakened PoE2 Trade2",
"language": "Language", "language": "Language",
"private_league": "or Private League", "private_league": "or Private League",
"account_name": "Account name", "account_name": "Account name",

View File

@@ -5,7 +5,6 @@
"reopen_settings": "{0}을 눌러서 편집을 계속하세요.", "reopen_settings": "{0}을 눌러서 편집을 계속하세요.",
"seconds": "초", "seconds": "초",
"league": "리그", "league": "리그",
"app": { "app": {
"leagues_loading": "리그를 불러오는 중\u2026", "leagues_loading": "리그를 불러오는 중\u2026",
"leagues_failed": "리그 불러오기를 실패하였습니다", "leagues_failed": "리그 불러오기를 실패하였습니다",
@@ -19,11 +18,9 @@
"report_bug": "버그 발생 시 GitHub에 보고해주세요", "report_bug": "버그 발생 시 GitHub에 보고해주세요",
"quit": "종료" "quit": "종료"
}, },
"map.mods.heist": "강탈", "map.mods.heist": "강탈",
"map.mods.outdated": "사용X", "map.mods.outdated": "사용X",
"Support development on": "개발을 지원하세요", "Support development on": "개발을 지원하세요",
"Blighted": "역병 걸린", "Blighted": "역병 걸린",
"Blight-ravaged": "역병에 유린당한", "Blight-ravaged": "역병에 유린당한",
"Magic": "매직", "Magic": "매직",
@@ -37,7 +34,6 @@
"Anomalous": "기묘한", "Anomalous": "기묘한",
"Divergent": "상이한", "Divergent": "상이한",
"Phantasmal": "몽환적인", "Phantasmal": "몽환적인",
"item": { "item": {
"prop_quality": "{0}% 퀄리티", "prop_quality": "{0}% 퀄리티",
"base_percentile": "기본 백분위수: {0}%", "base_percentile": "기본 백분위수: {0}%",
@@ -150,7 +146,6 @@
"hide_anointment": "Buyer will likely change anointment", "hide_anointment": "Buyer will likely change anointment",
"hide_for_crafting": "Select only if price-checking as base item for crafting", "hide_for_crafting": "Select only if price-checking as base item for crafting",
"hide_empty_mod": "Select only if item has 6 modifiers (1 of which is crafted) or if it has 5 modifiers", "hide_empty_mod": "Select only if item has 6 modifiers (1 of which is crafted) or if it has 5 modifiers",
"tag_implicit": "고정 속성", "tag_implicit": "고정 속성",
"tag_fractured": "분열된", "tag_fractured": "분열된",
"tag_crafted": "제작된", "tag_crafted": "제작된",
@@ -271,7 +266,7 @@
"stack": "스택" "stack": "스택"
}, },
"settings": { "settings": {
"title": "세팅 - Awakened PoE Trade", "title": "세팅 - Awakened PoE2 Trade2",
"language": "언어", "language": "언어",
"private_league": "개인리그", "private_league": "개인리그",
"account_name": "계정명", "account_name": "계정명",

View File

@@ -19,14 +19,12 @@
"Select": "Выбрать", "Select": "Выбрать",
"Not recognized modifier": "Нераспознанное свойство", "Not recognized modifier": "Нераспознанное свойство",
"Refresh": "Обновить", "Refresh": "Обновить",
"please_wait": "Пожалуйста, подождите\u2026", "please_wait": "Пожалуйста, подождите\u2026",
"choose_file": "Выберите файл", "choose_file": "Выберите файл",
"app_is_ready": "Запущен и работает в фоновом режиме", "app_is_ready": "Запущен и работает в фоновом режиме",
"reopen_settings": "Нажмите {0} чтобы продолжить редактирование.", "reopen_settings": "Нажмите {0} чтобы продолжить редактирование.",
"seconds": "секунды", "seconds": "секунды",
"league": "Лига", "league": "Лига",
"app": { "app": {
"leagues_loading": "Загрузка лиг\u2026", "leagues_loading": "Загрузка лиг\u2026",
"leagues_failed": "Не удалось загрузить лиги", "leagues_failed": "Не удалось загрузить лиги",
@@ -40,11 +38,9 @@
"report_bug": "Сообщить о баге на GitHub", "report_bug": "Сообщить о баге на GitHub",
"quit": "Выход" "quit": "Выход"
}, },
"map.mods.heist": "кража", "map.mods.heist": "кража",
"map.mods.outdated": "устарело", "map.mods.outdated": "устарело",
"Support development on": "Поддержите разработку\u00A0на", "Support development on": "Поддержите разработку\u00A0на",
"Blighted": "Заражённая", "Blighted": "Заражённая",
"Blight-ravaged": "Разорённая Скверной", "Blight-ravaged": "Разорённая Скверной",
"Shaper": "Создатель", "Shaper": "Создатель",
@@ -57,7 +53,6 @@
"Anomalous": "Аномальный", "Anomalous": "Аномальный",
"Divergent": "Искривлённый", "Divergent": "Искривлённый",
"Phantasmal": "Фантомный", "Phantasmal": "Фантомный",
"item": { "item": {
"prop_quality": "К-во: {0}%", "prop_quality": "К-во: {0}%",
"base_percentile": "Ролл значений базы: {0}%", "base_percentile": "Ролл значений базы: {0}%",
@@ -168,7 +163,6 @@
"hide_anointment": "Покупатель, скорее всего, поменяет зачарование", "hide_anointment": "Покупатель, скорее всего, поменяет зачарование",
"hide_for_crafting": "Отмечайте, если проверяете цену в качестве базового предмета для крафта", "hide_for_crafting": "Отмечайте, если проверяете цену в качестве базового предмета для крафта",
"hide_empty_mod": "Выбирайте, только если у предмета 6 свойств (1 из которых ремесленное) или если у него 5 свойств", "hide_empty_mod": "Выбирайте, только если у предмета 6 свойств (1 из которых ремесленное) или если у него 5 свойств",
"tag_crafted": "мастер", "tag_crafted": "мастер",
"tag_fractured": "расколотый", "tag_fractured": "расколотый",
"tag_scourge": "преображен", "tag_scourge": "преображен",
@@ -288,7 +282,7 @@
"stack": "Стак" "stack": "Стак"
}, },
"settings": { "settings": {
"title": "Настройки - Awakened PoE Trade", "title": "Настройки - Awakened PoE2 Trade2",
"language": "Язык", "language": "Язык",
"private_league": "или Приватная лига", "private_league": "или Приватная лига",
"account_name": "Имя учетной записи", "account_name": "Имя учетной записи",

View File

@@ -47,6 +47,7 @@ export enum ItemCategory {
SanctumRelic = 'Sanctum Relic', SanctumRelic = 'Sanctum Relic',
Tincture = 'Tincture', Tincture = 'Tincture',
Charm = 'Charm', Charm = 'Charm',
Crossbow = 'Crossbow',
} }
export const WEAPON_ONE_HANDED_MELEE = new Set([ export const WEAPON_ONE_HANDED_MELEE = new Set([
@@ -56,41 +57,41 @@ export const WEAPON_ONE_HANDED_MELEE = new Set([
ItemCategory.Claw, ItemCategory.Claw,
ItemCategory.Dagger, ItemCategory.Dagger,
ItemCategory.RuneDagger, ItemCategory.RuneDagger,
ItemCategory.Sceptre ItemCategory.Sceptre,
]) ]);
export const WEAPON_ONE_HANDED = new Set([ export const WEAPON_ONE_HANDED = new Set([
ItemCategory.Wand, ItemCategory.Wand,
...WEAPON_ONE_HANDED_MELEE ...WEAPON_ONE_HANDED_MELEE,
]) ]);
export const WEAPONE_TWO_HANDED_MELEE = new Set([ export const WEAPONE_TWO_HANDED_MELEE = new Set([
ItemCategory.TwoHandedAxe, ItemCategory.TwoHandedAxe,
ItemCategory.TwoHandedMace, ItemCategory.TwoHandedMace,
ItemCategory.TwoHandedSword, ItemCategory.TwoHandedSword,
ItemCategory.Staff, ItemCategory.Staff,
ItemCategory.Warstaff ItemCategory.Warstaff,
]) ]);
export const WEAPON = new Set([ export const WEAPON = new Set([
ItemCategory.FishingRod, ItemCategory.FishingRod,
ItemCategory.Bow, ItemCategory.Bow,
...WEAPON_ONE_HANDED, ...WEAPON_ONE_HANDED,
...WEAPONE_TWO_HANDED_MELEE ...WEAPONE_TWO_HANDED_MELEE,
]) ]);
export const ARMOUR = new Set([ export const ARMOUR = new Set([
ItemCategory.BodyArmour, ItemCategory.BodyArmour,
ItemCategory.Boots, ItemCategory.Boots,
ItemCategory.Gloves, ItemCategory.Gloves,
ItemCategory.Helmet, ItemCategory.Helmet,
ItemCategory.Shield ItemCategory.Shield,
]) ]);
export const ACCESSORY = new Set([ export const ACCESSORY = new Set([
ItemCategory.Amulet, ItemCategory.Amulet,
ItemCategory.Belt, ItemCategory.Belt,
ItemCategory.Ring, ItemCategory.Ring,
ItemCategory.Trinket ItemCategory.Trinket,
// ItemCategory.Quiver // ItemCategory.Quiver
]) ]);

View File

@@ -1,122 +1,128 @@
import { reactive as deepReactive, shallowRef, toRaw } from 'vue' import { reactive as deepReactive, shallowRef, toRaw } from 'vue';
import isDeepEqual from 'fast-deep-equal' import isDeepEqual from 'fast-deep-equal';
import { Host } from '@/web/background/IPC' import { Host } from '@/web/background/IPC';
import { HostConfig, ShortcutAction } from '@ipc/types' import { HostConfig, ShortcutAction } from '@ipc/types';
import type * as widget from './overlay/widgets' import type * as widget from './overlay/widgets';
import type { StashSearchWidget } from './stash-search/widget' import type { StashSearchWidget } from './stash-search/widget';
import type { ItemCheckWidget } from './item-check/widget' import type { ItemCheckWidget } from './item-check/widget';
import type { ItemSearchWidget } from './item-search/widget' import type { ItemSearchWidget } from './item-search/widget';
const _config = shallowRef<Config | null>(null) const _config = shallowRef<Config | null>(null);
let _lastSavedConfig: Config | null = null let _lastSavedConfig: Config | null = null;
export function AppConfig (): Config export function AppConfig(): Config;
export function AppConfig<T extends widget.Widget> (type: string): T | undefined export function AppConfig<T extends widget.Widget>(type: string): T | undefined;
export function AppConfig(type?: string) { export function AppConfig(type?: string) {
if (!type) { if (!type) {
return _config.value! return _config.value!;
} else { } else {
return _config.value!.widgets.find(w => w.wmType === type) return _config.value!.widgets.find((w) => w.wmType === type);
} }
} }
export function updateConfig(updates: Config) { export function updateConfig(updates: Config) {
_config.value = deepReactive(JSON.parse(JSON.stringify(updates))) _config.value = deepReactive(JSON.parse(JSON.stringify(updates)));
document.documentElement.style.fontSize = `${_config.value!.fontSize}px` document.documentElement.style.fontSize = `${_config.value!.fontSize}px`;
} }
export function saveConfig(opts?: { isTemporary: boolean }) { export function saveConfig(opts?: { isTemporary: boolean }) {
const rawConfig = toRaw(_config.value!) const rawConfig = toRaw(_config.value!);
if (rawConfig.widgets.some(w => w.wmZorder === 'exclusive' && w.wmWants === 'show')) { if (
return rawConfig.widgets.some(
(w) => w.wmZorder === 'exclusive' && w.wmWants === 'show'
)
) {
return;
} }
if (!isDeepEqual(rawConfig, _lastSavedConfig)) { if (!isDeepEqual(rawConfig, _lastSavedConfig)) {
_lastSavedConfig = JSON.parse(JSON.stringify(rawConfig)) _lastSavedConfig = JSON.parse(JSON.stringify(rawConfig));
Host.sendEvent({ Host.sendEvent({
name: 'CLIENT->MAIN::save-config', name: 'CLIENT->MAIN::save-config',
payload: { payload: {
contents: JSON.stringify(AppConfig()), contents: JSON.stringify(AppConfig()),
isTemporary: opts?.isTemporary ?? false isTemporary: opts?.isTemporary ?? false,
} },
}) });
} }
} }
export function pushHostConfig() { export function pushHostConfig() {
Host.sendEvent({ Host.sendEvent({
name: 'CLIENT->MAIN::update-host-config', name: 'CLIENT->MAIN::update-host-config',
payload: getConfigForHost() payload: getConfigForHost(),
}) });
} }
export async function initConfig() { export async function initConfig() {
Host.onEvent('MAIN->CLIENT::config-changed', (e) => { Host.onEvent('MAIN->CLIENT::config-changed', (e) => {
_lastSavedConfig = JSON.parse(e.contents) // should be a deep copy _lastSavedConfig = JSON.parse(e.contents); // should be a deep copy
updateConfig(JSON.parse(e.contents)) updateConfig(JSON.parse(e.contents));
}) });
const contents = await Host.getConfig() const contents = await Host.getConfig();
if (!contents) { if (!contents) {
updateConfig(defaultConfig()) updateConfig(defaultConfig());
return return;
} }
let config: Config let config: Config;
try { try {
config = JSON.parse(contents) config = JSON.parse(contents);
} catch { } catch {
updateConfig(defaultConfig()) updateConfig(defaultConfig());
saveConfig({ isTemporary: true }) saveConfig({ isTemporary: true });
return return;
// TODO // TODO
// dialog.showErrorBox( // dialog.showErrorBox(
// 'Awakened PoE Trade - Incompatible configuration', // 'Awakened PoE2 Trade2 - Incompatible configuration',
// // ---------------------- // // ----------------------
// 'You are trying to use an older version of Awakened PoE Trade with a newer incompatible configuration file.\n' + // 'You are trying to use an older version of Awakened PoE2 Trade2 with a newer incompatible configuration file.\n' +
// 'You need to install the latest version to continue using it.' // 'You need to install the latest version to continue using it.'
// ) // )
} }
updateConfig(upgradeConfig(config)) updateConfig(upgradeConfig(config));
} }
export function poeWebApi() { export function poeWebApi() {
const { language, realm } = AppConfig() const { language, realm } = AppConfig();
switch (language) { switch (language) {
case 'en': return 'www.pathofexile.com' case 'en':
case 'ru': return 'ru.pathofexile.com' return 'www.pathofexile.com';
case 'cmn-Hant': return (realm === 'pc-garena') case 'ru':
? 'pathofexile.tw' return 'ru.pathofexile.com';
: 'www.pathofexile.com' case 'cmn-Hant':
case 'ko': return 'poe.game.daum.net' return realm === 'pc-garena' ? 'pathofexile.tw' : 'www.pathofexile.com';
case 'ko':
return 'poe.game.daum.net';
} }
} }
export interface Config { export interface Config {
configVersion: number configVersion: number;
leagueId?: string leagueId?: string;
overlayKey: string overlayKey: string;
overlayBackground: string overlayBackground: string;
overlayBackgroundClose: boolean overlayBackgroundClose: boolean;
restoreClipboard: boolean restoreClipboard: boolean;
commands: Array<{ commands: Array<{
text: string text: string;
hotkey: string | null hotkey: string | null;
send: boolean send: boolean;
}> }>;
clientLog: string | null clientLog: string | null;
gameConfig: string | null gameConfig: string | null;
windowTitle: string windowTitle: string;
logKeys: boolean logKeys: boolean;
accountName: string accountName: string;
stashScroll: boolean stashScroll: boolean;
language: 'en' | 'ru' | 'cmn-Hant' | 'ko' language: 'en' | 'ru' | 'cmn-Hant' | 'ko';
realm: 'pc-ggg' | 'pc-garena' realm: 'pc-ggg' | 'pc-garena';
widgets: widget.Widget[] widgets: widget.Widget[];
fontSize: number fontSize: number;
showAttachNotification: boolean showAttachNotification: boolean;
} }
export const defaultConfig = (): Config => ({ export const defaultConfig = (): Config => ({
@@ -126,34 +132,41 @@ export const defaultConfig = (): Config => ({
overlayBackgroundClose: true, overlayBackgroundClose: true,
restoreClipboard: false, restoreClipboard: false,
showAttachNotification: true, showAttachNotification: true,
commands: [{ commands: [
{
text: '/hideout', text: '/hideout',
hotkey: 'F5', hotkey: 'F5',
send: true send: true,
}, { },
{
text: '/exit', text: '/exit',
hotkey: 'F9', hotkey: 'F9',
send: true send: true,
}, { },
{
text: '@last ty', text: '@last ty',
hotkey: null, hotkey: null,
send: true send: true,
}, { },
{
text: '/invite @last', text: '/invite @last',
hotkey: null, hotkey: null,
send: true send: true,
}, { },
{
text: '/tradewith @last', text: '/tradewith @last',
hotkey: null, hotkey: null,
send: true send: true,
}, { },
{
text: '/hideout @last', text: '/hideout @last',
hotkey: null, hotkey: null,
send: true send: true,
}], },
],
clientLog: null, clientLog: null,
gameConfig: null, gameConfig: null,
windowTitle: 'Path of Exile', windowTitle: 'Path of Exile 2',
logKeys: false, logKeys: false,
accountName: '', accountName: '',
stashScroll: true, stashScroll: true,
@@ -172,9 +185,9 @@ export const defaultConfig = (): Config => ({
anchor: { anchor: {
pos: 'tl', pos: 'tl',
x: 5, x: 5,
y: 5 y: 5,
}, },
alwaysShow: false alwaysShow: false,
} as widget.WidgetMenu, } as widget.WidgetMenu,
{ {
wmId: 2, wmId: 2,
@@ -197,7 +210,7 @@ export const defaultConfig = (): Config => ({
searchStatRange: 10, searchStatRange: 10,
showCursor: true, showCursor: true,
requestPricePrediction: false, requestPricePrediction: false,
rememberCurrency: false rememberCurrency: false,
} as widget.PriceCheckWidget, } as widget.PriceCheckWidget,
{ {
wmId: 3, wmId: 3,
@@ -217,22 +230,22 @@ export const defaultConfig = (): Config => ({
selectedStats: [ selectedStats: [
{ {
matcher: '#% maximum Player Resistances', matcher: '#% maximum Player Resistances',
decision: 'w--' decision: 'w--',
}, },
{ {
matcher: 'Monsters reflect #% of Physical Damage', matcher: 'Monsters reflect #% of Physical Damage',
decision: 'd--' decision: 'd--',
}, },
{ {
matcher: 'Monsters reflect #% of Elemental Damage', matcher: 'Monsters reflect #% of Elemental Damage',
decision: 'd--' decision: 'd--',
}, },
{ {
matcher: 'Area contains two Unique Bosses', matcher: 'Area contains two Unique Bosses',
decision: 'g--' decision: 'g--',
} },
] ],
} },
} as ItemCheckWidget, } as ItemCheckWidget,
{ {
wmId: 4, wmId: 4,
@@ -241,7 +254,7 @@ export const defaultConfig = (): Config => ({
wmWants: 'hide', wmWants: 'hide',
wmZorder: 4, wmZorder: 4,
wmFlags: ['hide-on-focus', 'skip-menu'], wmFlags: ['hide-on-focus', 'skip-menu'],
toggleKey: null toggleKey: null,
} as widget.DelveGridWidget, } as widget.DelveGridWidget,
{ {
wmId: 5, wmId: 5,
@@ -249,7 +262,7 @@ export const defaultConfig = (): Config => ({
wmTitle: '', wmTitle: '',
wmWants: 'hide', wmWants: 'hide',
wmZorder: 'exclusive', wmZorder: 'exclusive',
wmFlags: ['invisible-on-blur', 'ignore-ui-visibility'] wmFlags: ['invisible-on-blur', 'ignore-ui-visibility'],
}, },
{ {
wmId: 6, wmId: 6,
@@ -261,8 +274,8 @@ export const defaultConfig = (): Config => ({
anchor: { anchor: {
pos: 'tl', pos: 'tl',
x: 10, x: 10,
y: 20 y: 20,
} },
} as ItemSearchWidget, } as ItemSearchWidget,
// --- DEFAULT --- // --- DEFAULT ---
{ {
@@ -275,14 +288,14 @@ export const defaultConfig = (): Config => ({
anchor: { anchor: {
pos: 'tl', pos: 'tl',
x: 35, x: 35,
y: 46 y: 46,
}, },
entries: [ entries: [
{ id: 1, name: '', text: '"Pack Size: +3"', hotkey: null }, { id: 1, name: '', text: '"Pack Size: +3"', hotkey: null },
{ id: 2, name: '', text: 'Reflect', hotkey: null }, { id: 2, name: '', text: 'Reflect', hotkey: null },
{ id: 3, name: '', text: '"Cannot Leech Life"', hotkey: null }, { id: 3, name: '', text: '"Cannot Leech Life"', hotkey: null },
{ id: 4, name: '', text: '"Cannot Leech Mana"', hotkey: null } { id: 4, name: '', text: '"Cannot Leech Mana"', hotkey: null },
] ],
} as StashSearchWidget, } as StashSearchWidget,
{ {
wmId: 102, wmId: 102,
@@ -294,16 +307,21 @@ export const defaultConfig = (): Config => ({
anchor: { anchor: {
pos: 'tl', pos: 'tl',
x: 34, x: 34,
y: 56 y: 56,
}, },
entries: [ entries: [
{ id: 1, name: '', text: 'Currency', hotkey: null }, { id: 1, name: '', text: 'Currency', hotkey: null },
{ id: 2, name: '', text: '"Divination Card"', hotkey: null }, { id: 2, name: '', text: '"Divination Card"', hotkey: null },
{ id: 3, name: '', text: 'Fossil', hotkey: null }, { id: 3, name: '', text: 'Fossil', hotkey: null },
{ id: 4, name: '', text: '"Map Tier"', hotkey: null }, { id: 4, name: '', text: '"Map Tier"', hotkey: null },
{ id: 5, name: '', text: '"Map Device" "Rarity: Normal"', hotkey: null }, {
{ id: 6, name: '', text: 'Tane Laboratory', hotkey: null } id: 5,
] name: '',
text: '"Map Device" "Rarity: Normal"',
hotkey: null,
},
{ id: 6, name: '', text: 'Tane Laboratory', hotkey: null },
],
} as StashSearchWidget, } as StashSearchWidget,
{ {
wmId: 103, wmId: 103,
@@ -315,335 +333,367 @@ export const defaultConfig = (): Config => ({
anchor: { anchor: {
pos: 'tc', pos: 'tc',
x: 50, x: 50,
y: 10 y: 10,
}, },
images: [ images: [{ id: 1, url: 'syndicate.jpg' }],
{ id: 1, url: 'syndicate.jpg' } } as widget.ImageStripWidget,
] ],
} as widget.ImageStripWidget });
]
})
function upgradeConfig(_config: Config): Config { function upgradeConfig(_config: Config): Config {
const config = _config as Omit<Config, 'widgets'> & { widgets: Array<Record<string, any>> } const config = _config as Omit<Config, 'widgets'> & {
widgets: Array<Record<string, any>>;
};
if (config.configVersion < 3) { if (config.configVersion < 3) {
config.widgets.push({ config.widgets.push({
...defaultConfig().widgets.find(w => w.wmType === 'image-strip')!, ...defaultConfig().widgets.find((w) => w.wmType === 'image-strip')!,
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1, wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
wmZorder: null wmZorder: null,
}) });
config.widgets.push({ config.widgets.push({
...defaultConfig().widgets.find(w => w.wmType === 'delve-grid')!, ...defaultConfig().widgets.find((w) => w.wmType === 'delve-grid')!,
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1, wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
wmZorder: null wmZorder: null,
}) });
config.widgets.find(w => w.wmType === 'menu')! config.widgets.find((w) => w.wmType === 'menu')!.alwaysShow = false;
.alwaysShow = false
config.configVersion = 3 config.configVersion = 3;
} }
if (config.configVersion < 4) { if (config.configVersion < 4) {
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find(
.chaosPriceThreshold = 0.05 (w) => w.wmType === 'price-check'
)!.chaosPriceThreshold = 0.05;
const mapCheck = config.widgets.find(w => w.wmType === 'map-check')! const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!;
;(mapCheck as any).selectedStats.forEach((e: any) => { (mapCheck as any).selectedStats.forEach((e: any) => {
e.matcher = e.matchRef e.matcher = e.matchRef;
e.matchRef = undefined e.matchRef = undefined;
}) });
{ {
const widgets = config.widgets.filter(w => w.wmType === 'image-strip')! const widgets = config.widgets.filter((w) => w.wmType === 'image-strip')!;
widgets.forEach((imgStrip: any) => { widgets.forEach((imgStrip: any) => {
imgStrip.images.forEach((e: any, idx: number) => { imgStrip.images.forEach((e: any, idx: number) => {
e.id = idx e.id = idx;
}) });
}) });
} }
config.configVersion = 4 config.configVersion = 4;
} }
if (config.configVersion < 5) { if (config.configVersion < 5) {
config.commands.forEach(cmd => { config.commands.forEach((cmd) => {
cmd.send = true cmd.send = true;
}) });
config.configVersion = 5 config.configVersion = 5;
} }
if (config.configVersion < 6) { if (config.configVersion < 6) {
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find((w) => w.wmType === 'price-check')!.showRateLimitState =
.showRateLimitState = ((config as any).logLevel === 'debug') (config as any).logLevel === 'debug';
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find((w) => w.wmType === 'price-check')!.apiLatencySeconds =
.apiLatencySeconds = 2 2;
config.configVersion = 6 config.configVersion = 6;
} }
if (config.configVersion < 7) { if (config.configVersion < 7) {
const mapCheck = config.widgets.find(w => w.wmType === 'map-check')! const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!;
mapCheck.wmType = 'item-check' mapCheck.wmType = 'item-check';
mapCheck.maps = { selectedStats: mapCheck.selectedStats } mapCheck.maps = { selectedStats: mapCheck.selectedStats };
mapCheck.selectedStats = undefined mapCheck.selectedStats = undefined;
(config as any).itemCheckKey = (config as any).mapCheckKey || null;
(config as any).mapCheckKey = undefined;
;(config as any).itemCheckKey = (config as any).mapCheckKey || null config.configVersion = 7;
;(config as any).mapCheckKey = undefined
config.configVersion = 7
} }
if (config.configVersion < 8) { if (config.configVersion < 8) {
const itemCheck = config.widgets.find(w => w.wmType === 'item-check')! const itemCheck = config.widgets.find((w) => w.wmType === 'item-check')!;
;(itemCheck as ItemCheckWidget).maps.showNewStats = false (itemCheck as ItemCheckWidget).maps.showNewStats = false;
itemCheck.maps.selectedStats = (itemCheck as ItemCheckWidget).maps.selectedStats.map(entry => ({ itemCheck.maps.selectedStats = (
itemCheck as ItemCheckWidget
).maps.selectedStats.map((entry) => ({
matcher: entry.matcher, matcher: entry.matcher,
decision: decision: (entry as any).valueDanger
(entry as any).valueDanger ? 'danger' ? 'danger'
: (entry as any).valueWarning ? 'warning' : (entry as any).valueWarning
: (entry as any).valueDesirable ? 'desirable' ? 'warning'
: 'seen' : (entry as any).valueDesirable
})) ? 'desirable'
: 'seen',
}));
config.configVersion = 8 config.configVersion = 8;
} }
if (config.configVersion < 9) { if (config.configVersion < 9) {
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find((w) => w.wmType === 'price-check')!.collapseListings =
.collapseListings = 'api' 'api';
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find((w) => w.wmType === 'price-check')!.smartInitialSearch =
.smartInitialSearch = true true;
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find(
.lockedInitialSearch = true (w) => w.wmType === 'price-check'
)!.lockedInitialSearch = true;
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find(
.activateStockFilter = false (w) => w.wmType === 'price-check'
)!.activateStockFilter = false;
config.configVersion = 9 config.configVersion = 9;
} }
if (config.configVersion < 10) { if (config.configVersion < 10) {
config.widgets.push({ config.widgets.push({
...defaultConfig().widgets.find(w => w.wmType === 'settings')!, ...defaultConfig().widgets.find((w) => w.wmType === 'settings')!,
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1 wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
}) });
const priceCheck = config.widgets.find(w => w.wmType === 'price-check')! const priceCheck = config.widgets.find((w) => w.wmType === 'price-check')!;
priceCheck.hotkey = (config as any).priceCheckKey priceCheck.hotkey = (config as any).priceCheckKey;
priceCheck.hotkeyHold = (config as any).priceCheckKeyHold priceCheck.hotkeyHold = (config as any).priceCheckKeyHold;
priceCheck.hotkeyLocked = (config as any).priceCheckLocked priceCheck.hotkeyLocked = (config as any).priceCheckLocked;
priceCheck.showSeller = (config as any).showSeller priceCheck.showSeller = (config as any).showSeller;
priceCheck.searchStatRange = (config as any).searchStatRange priceCheck.searchStatRange = (config as any).searchStatRange;
priceCheck.showCursor = (config as any).priceCheckShowCursor priceCheck.showCursor = (config as any).priceCheckShowCursor;
if (priceCheck.chaosPriceThreshold === 0.05) { if (priceCheck.chaosPriceThreshold === 0.05) {
priceCheck.chaosPriceThreshold = 0 priceCheck.chaosPriceThreshold = 0;
} }
config.configVersion = 10 config.configVersion = 10;
} }
if (config.configVersion < 11) { if (config.configVersion < 11) {
config.widgets.find(w => w.wmType === 'price-check')! config.widgets.find(
.requestPricePrediction = false (w) => w.wmType === 'price-check'
)!.requestPricePrediction = false;
config.configVersion = 11 config.configVersion = 11;
} }
if (config.configVersion < 12) { if (config.configVersion < 12) {
const afterSettings = config.widgets.findIndex(w => w.wmType === 'settings') const afterSettings = config.widgets.findIndex(
(w) => w.wmType === 'settings'
);
config.widgets.splice(afterSettings + 1, 0, { config.widgets.splice(afterSettings + 1, 0, {
...defaultConfig().widgets.find(w => w.wmType === 'item-search')!, ...defaultConfig().widgets.find((w) => w.wmType === 'item-search')!,
wmWants: 'show', wmWants: 'show',
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1 wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
}) });
config.realm = 'pc-ggg' config.realm = 'pc-ggg';
if (config.language === 'zh_TW' as string) { if (config.language === ('zh_TW' as string)) {
config.language = 'cmn-Hant' config.language = 'cmn-Hant';
} }
config.configVersion = 12 config.configVersion = 12;
} }
if (config.configVersion < 13) { if (config.configVersion < 13) {
config.showAttachNotification = true config.showAttachNotification = true;
config.configVersion = 13 config.configVersion = 13;
} }
if (config.configVersion < 14) { if (config.configVersion < 14) {
const imgWidgets = config.widgets.filter(w => w.wmType === 'image-strip') as widget.ImageStripWidget[] const imgWidgets = config.widgets.filter(
(w) => w.wmType === 'image-strip'
) as widget.ImageStripWidget[];
imgWidgets.forEach((imgStrip) => { imgWidgets.forEach((imgStrip) => {
imgStrip.images.forEach((e) => { imgStrip.images.forEach((e) => {
e.url = e.url.startsWith('app-file://') e.url = e.url.startsWith('app-file://')
? e.url.slice('app-file://'.length) ? e.url.slice('app-file://'.length)
: e.url : e.url;
}) });
}) });
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as ItemCheckWidget const itemCheck = config.widgets.find(
itemCheck.wikiKey = (config as any).wikiKey (w) => w.wmType === 'item-check'
itemCheck.poedbKey = null ) as ItemCheckWidget;
itemCheck.craftOfExileKey = (config as any).craftOfExileKey itemCheck.wikiKey = (config as any).wikiKey;
itemCheck.stashSearchKey = null itemCheck.poedbKey = null;
itemCheck.craftOfExileKey = (config as any).craftOfExileKey;
itemCheck.stashSearchKey = null;
config.configVersion = 14 config.configVersion = 14;
} }
if (config.configVersion < 15) { if (config.configVersion < 15) {
const priceCheck = config.widgets.find(w => w.wmType === 'price-check') as widget.PriceCheckWidget const priceCheck = config.widgets.find(
priceCheck.builtinBrowser = false (w) => w.wmType === 'price-check'
) as widget.PriceCheckWidget;
priceCheck.builtinBrowser = false;
const itemSearch = config.widgets.find(w => w.wmType === 'item-search') as ItemSearchWidget const itemSearch = config.widgets.find(
itemSearch.ocrGemsKey = null (w) => w.wmType === 'item-search'
) as ItemSearchWidget;
itemSearch.ocrGemsKey = null;
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as ItemCheckWidget const itemCheck = config.widgets.find(
itemCheck.maps.profile = 1 (w) => w.wmType === 'item-check'
) as ItemCheckWidget;
itemCheck.maps.profile = 1;
for (const stat of itemCheck.maps.selectedStats) { for (const stat of itemCheck.maps.selectedStats) {
const p1decision = const p1decision =
(stat.decision === 'danger') ? 'd' stat.decision === 'danger'
: (stat.decision === 'warning') ? 'w' ? 'd'
: (stat.decision === 'desirable') ? 'g' : 's' : stat.decision === 'warning'
? 'w'
: stat.decision === 'desirable'
? 'g'
: 's';
stat.decision = `${p1decision}--` stat.decision = `${p1decision}--`;
} }
config.configVersion = 15 config.configVersion = 15;
} }
if (config.configVersion < 16) { if (config.configVersion < 16) {
const delve = config.widgets.find(w => w.wmType === 'delve-grid') as widget.DelveGridWidget const delve = config.widgets.find(
delve.toggleKey = (config as any).delveGridKey (w) => w.wmType === 'delve-grid'
) as widget.DelveGridWidget;
delve.toggleKey = (config as any).delveGridKey;
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as ItemCheckWidget const itemCheck = config.widgets.find(
itemCheck.hotkey = (config as any).itemCheckKey (w) => w.wmType === 'item-check'
) as ItemCheckWidget;
itemCheck.hotkey = (config as any).itemCheckKey;
if (itemCheck.maps.profile === undefined) { if (itemCheck.maps.profile === undefined) {
itemCheck.maps.profile = 1 itemCheck.maps.profile = 1;
itemCheck.maps.selectedStats = [] itemCheck.maps.selectedStats = [];
} }
config.configVersion = 16 config.configVersion = 16;
} }
if (config.logKeys === undefined) { if (config.logKeys === undefined) {
config.logKeys = false config.logKeys = false;
} }
const priceCheck = config.widgets.find(w => w.wmType === 'price-check') as widget.PriceCheckWidget const priceCheck = config.widgets.find(
(w) => w.wmType === 'price-check'
) as widget.PriceCheckWidget;
if (priceCheck.rememberCurrency === undefined) { if (priceCheck.rememberCurrency === undefined) {
priceCheck.rememberCurrency = false priceCheck.rememberCurrency = false;
} }
for (const widget of config.widgets) { for (const widget of config.widgets) {
if (widget.wmType === 'stash-search') { if (widget.wmType === 'stash-search') {
(widget as StashSearchWidget).enableHotkeys ??= true (widget as StashSearchWidget).enableHotkeys ??= true;
} }
} }
return config as unknown as Config return config as unknown as Config;
} }
function getConfigForHost(): HostConfig { function getConfigForHost(): HostConfig {
const actions: ShortcutAction[] = [] const actions: ShortcutAction[] = [];
const config = AppConfig() const config = AppConfig();
const priceCheck = AppConfig('price-check') as widget.PriceCheckWidget const priceCheck = AppConfig('price-check') as widget.PriceCheckWidget;
if (priceCheck.hotkey) { if (priceCheck.hotkey) {
actions.push({ actions.push({
shortcut: `${priceCheck.hotkeyHold} + ${priceCheck.hotkey}`, shortcut: `${priceCheck.hotkeyHold} + ${priceCheck.hotkey}`,
action: { type: 'copy-item', target: 'price-check', focusOverlay: false }, action: { type: 'copy-item', target: 'price-check', focusOverlay: false },
keepModKeys: true keepModKeys: true,
}) });
} }
if (priceCheck.hotkeyLocked) { if (priceCheck.hotkeyLocked) {
actions.push({ actions.push({
shortcut: priceCheck.hotkeyLocked, shortcut: priceCheck.hotkeyLocked,
action: { type: 'copy-item', target: 'price-check', focusOverlay: true } action: { type: 'copy-item', target: 'price-check', focusOverlay: true },
}) });
} }
actions.push({ actions.push({
shortcut: config.overlayKey, shortcut: config.overlayKey,
action: { type: 'toggle-overlay' }, action: { type: 'toggle-overlay' },
keepModKeys: true keepModKeys: true,
}) });
const itemCheck = AppConfig('item-check') as ItemCheckWidget const itemCheck = AppConfig('item-check') as ItemCheckWidget;
if (itemCheck.wikiKey) { if (itemCheck.wikiKey) {
actions.push({ actions.push({
shortcut: itemCheck.wikiKey, shortcut: itemCheck.wikiKey,
action: { type: 'copy-item', target: 'open-wiki' } action: { type: 'copy-item', target: 'open-wiki' },
}) });
} }
if (itemCheck.craftOfExileKey) { if (itemCheck.craftOfExileKey) {
actions.push({ actions.push({
shortcut: itemCheck.craftOfExileKey, shortcut: itemCheck.craftOfExileKey,
action: { type: 'copy-item', target: 'open-craft-of-exile' } action: { type: 'copy-item', target: 'open-craft-of-exile' },
}) });
} }
if (itemCheck.poedbKey) { if (itemCheck.poedbKey) {
actions.push({ actions.push({
shortcut: itemCheck.poedbKey, shortcut: itemCheck.poedbKey,
action: { type: 'copy-item', target: 'open-poedb' } action: { type: 'copy-item', target: 'open-poedb' },
}) });
} }
if (itemCheck.stashSearchKey) { if (itemCheck.stashSearchKey) {
actions.push({ actions.push({
shortcut: itemCheck.stashSearchKey, shortcut: itemCheck.stashSearchKey,
action: { type: 'copy-item', target: 'search-similar' } action: { type: 'copy-item', target: 'search-similar' },
}) });
} }
if (itemCheck.hotkey) { if (itemCheck.hotkey) {
actions.push({ actions.push({
shortcut: itemCheck.hotkey, shortcut: itemCheck.hotkey,
action: { type: 'copy-item', target: 'item-check', focusOverlay: true } action: { type: 'copy-item', target: 'item-check', focusOverlay: true },
}) });
} }
const delveGrid = AppConfig('delve-grid') as widget.DelveGridWidget const delveGrid = AppConfig('delve-grid') as widget.DelveGridWidget;
if (delveGrid.toggleKey) { if (delveGrid.toggleKey) {
actions.push({ actions.push({
shortcut: delveGrid.toggleKey, shortcut: delveGrid.toggleKey,
action: { type: 'trigger-event', target: 'delve-grid' }, action: { type: 'trigger-event', target: 'delve-grid' },
keepModKeys: true keepModKeys: true,
}) });
} }
for (const command of config.commands) { for (const command of config.commands) {
if (command.hotkey) { if (command.hotkey) {
actions.push({ actions.push({
shortcut: command.hotkey, shortcut: command.hotkey,
action: { type: 'paste-in-chat', text: command.text, send: command.send } action: {
}) type: 'paste-in-chat',
text: command.text,
send: command.send,
},
});
} }
} }
for (const widget of config.widgets) { for (const widget of config.widgets) {
if (widget.wmType === 'stash-search') { if (widget.wmType === 'stash-search') {
const stashSearch = widget as StashSearchWidget const stashSearch = widget as StashSearchWidget;
if (!stashSearch.enableHotkeys) continue if (!stashSearch.enableHotkeys) continue;
for (const entry of stashSearch.entries) { for (const entry of stashSearch.entries) {
if (entry.hotkey) { if (entry.hotkey) {
actions.push({ actions.push({
shortcut: entry.hotkey, shortcut: entry.hotkey,
action: { type: 'stash-search', text: entry.text } action: { type: 'stash-search', text: entry.text },
}) });
} }
} }
} else if (widget.wmType === 'timer') { } else if (widget.wmType === 'timer') {
const stopwatch = widget as widget.StopwatchWidget const stopwatch = widget as widget.StopwatchWidget;
if (stopwatch.toggleKey) { if (stopwatch.toggleKey) {
actions.push({ actions.push({
shortcut: stopwatch.toggleKey, shortcut: stopwatch.toggleKey,
keepModKeys: true, keepModKeys: true,
action: { action: {
type: 'trigger-event', type: 'trigger-event',
target: `stopwatch-start-stop:${widget.wmId}` target: `stopwatch-start-stop:${widget.wmId}`,
} },
}) });
} }
if (stopwatch.resetKey) { if (stopwatch.resetKey) {
actions.push({ actions.push({
@@ -651,18 +701,18 @@ function getConfigForHost (): HostConfig {
keepModKeys: true, keepModKeys: true,
action: { action: {
type: 'trigger-event', type: 'trigger-event',
target: `stopwatch-reset:${widget.wmId}` target: `stopwatch-reset:${widget.wmId}`,
} },
}) });
} }
} else if (widget.wmType === 'item-search') { } else if (widget.wmType === 'item-search') {
const itemSearch = widget as ItemSearchWidget const itemSearch = widget as ItemSearchWidget;
if (itemSearch.ocrGemsKey) { if (itemSearch.ocrGemsKey) {
actions.push({ actions.push({
shortcut: itemSearch.ocrGemsKey, shortcut: itemSearch.ocrGemsKey,
keepModKeys: true, keepModKeys: true,
action: { type: 'ocr-text', target: 'heist-gems' } action: { type: 'ocr-text', target: 'heist-gems' },
}) });
} }
} }
} }
@@ -676,6 +726,6 @@ function getConfigForHost (): HostConfig {
overlayKey: config.overlayKey, overlayKey: config.overlayKey,
logKeys: config.logKeys, logKeys: config.logKeys,
windowTitle: config.windowTitle, windowTitle: config.windowTitle,
language: config.language language: config.language,
} };
} }

View File

@@ -1,81 +1,102 @@
import { computed, shallowRef, readonly } from 'vue' import { computed, shallowRef, readonly } from 'vue';
import { createGlobalState } from '@vueuse/core' import { createGlobalState } from '@vueuse/core';
import { AppConfig, poeWebApi } from '@/web/Config' import { AppConfig, poeWebApi } from '@/web/Config';
import { Host } from './IPC' import { Host } from './IPC';
// pc-ggg, pc-garena // pc-ggg, pc-garena
// const PERMANENT_SC = ['Standard', '標準模式'] // const PERMANENT_SC = ['Standard', '標準模式']
const PERMANENT_HC = ['Hardcore', '專家模式'] const PERMANENT_HC = ['Hardcore', '專家模式'];
interface ApiLeague { interface ApiLeague {
id: string id: string;
event?: boolean event?: boolean;
rules: Array<{ id: string }> rules: Array<{ id: string }>;
} }
const DEFAULT_POE2_LEAGUES: ApiLeague[] = [
{ id: 'Standard', rules: [] },
{
id: 'Hardcore',
rules: [
{
id: 'Hardcore',
},
],
},
];
interface League { interface League {
id: string id: string;
isPopular: boolean isPopular: boolean;
} }
export const useLeagues = createGlobalState(() => { export const useLeagues = createGlobalState(() => {
const isLoading = shallowRef(false) const isLoading = shallowRef(false);
const error = shallowRef<string | null>(null) const error = shallowRef<string | null>(null);
const tradeLeagues = shallowRef<League[]>([]) const tradeLeagues = shallowRef<League[]>([]);
const selectedId = computed<string | undefined>({ const selectedId = computed<string | undefined>({
get() { get() {
return (tradeLeagues.value.length) return tradeLeagues.value.length ? AppConfig().leagueId : undefined;
? AppConfig().leagueId
: undefined
}, },
set(id) { set(id) {
AppConfig().leagueId = id AppConfig().leagueId = id;
} },
}) });
const selected = computed(() => { const selected = computed(() => {
const { leagueId } = AppConfig() const { leagueId } = AppConfig();
if (!tradeLeagues.value || !leagueId) return undefined if (!tradeLeagues.value || !leagueId) return undefined;
const listed = tradeLeagues.value.find(league => league.id === leagueId) const listed = tradeLeagues.value.find((league) => league.id === leagueId);
return { return {
id: leagueId, id: leagueId,
realm: AppConfig().realm, realm: AppConfig().realm,
isPopular: !isPrivateLeague(leagueId) && Boolean(listed?.isPopular) isPopular: !isPrivateLeague(leagueId) && Boolean(listed?.isPopular),
} };
}) });
async function load() { async function load() {
isLoading.value = true isLoading.value = true;
error.value = null error.value = null;
try { try {
const response = await Host.proxy(`${poeWebApi()}/api/leagues?type=main&realm=pc`) const response = await Host.proxy(
if (!response.ok) throw new Error(JSON.stringify(Object.fromEntries(response.headers))) `${poeWebApi()}/api/leagues?type=main&realm=pc`
const leagues: ApiLeague[] = await response.json() );
if (!response.ok)
throw new Error(JSON.stringify(Object.fromEntries(response.headers)));
// const leagues: ApiLeague[] = await response.json();
const leagues: ApiLeague[] = DEFAULT_POE2_LEAGUES;
tradeLeagues.value = leagues tradeLeagues.value = leagues
.filter(league => .filter(
(league) =>
!PERMANENT_HC.includes(league.id) && !PERMANENT_HC.includes(league.id) &&
!league.rules.some(rule => rule.id === 'NoParties' || !league.rules.some(
(rule.id === 'HardMode' && !league.event))) (rule) =>
.map(league => { rule.id === 'NoParties' ||
return { id: league.id, isPopular: true } (rule.id === 'HardMode' && !league.event)
}) )
)
.map((league) => {
return { id: league.id, isPopular: true };
});
const leagueIsAlive = tradeLeagues.value.some(league => league.id === selectedId.value) const leagueIsAlive = tradeLeagues.value.some(
(league) => league.id === selectedId.value
);
if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) { if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) {
if (tradeLeagues.value.length > 1) { if (tradeLeagues.value.length > 1) {
const TMP_CHALLENGE = 1 const TMP_CHALLENGE = 1;
selectedId.value = tradeLeagues.value[TMP_CHALLENGE].id selectedId.value = tradeLeagues.value[TMP_CHALLENGE].id;
} else { } else {
const STANDARD = 0 const STANDARD = 0;
selectedId.value = tradeLeagues.value[STANDARD].id selectedId.value = tradeLeagues.value[STANDARD].id;
} }
} }
} catch (e) { } catch (e) {
error.value = (e as Error).message error.value = (e as Error).message;
} finally { } finally {
isLoading.value = false isLoading.value = false;
} }
} }
@@ -85,13 +106,13 @@ export const useLeagues = createGlobalState(() => {
selectedId, selectedId,
selected, selected,
list: readonly(tradeLeagues), list: readonly(tradeLeagues),
load load,
} };
}) });
function isPrivateLeague(id: string) { function isPrivateLeague(id: string) {
if (id.includes('Ruthless')) { if (id.includes('Ruthless')) {
return true return true;
} }
return /\(PL\d+\)$/.test(id) return /\(PL\d+\)$/.test(id);
} }

View File

@@ -0,0 +1,13 @@
<template>
<div class="bg-orange-400 text-gray-900 text-center text-sm font-bold text-shadow-lg">
This is in BETA for POE2, things do not work as expected. Please report any issues.
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
})
</script>

View File

@@ -1,10 +1,9 @@
<template> <template>
<Widget :config="{ ...config, anchor }" move-handles="none" :removable="false" :inline-edit="false"> <Widget :config="{ ...config, anchor }" move-handles="none" :removable="false" :inline-edit="false">
<template v-if="item"> <template v-if="item">
<MapCheck v-if="isMapLike" <ConversionWarningBanner />
:item="item" :config="config.maps" /> <MapCheck v-if="isMapLike" :item="item" :config="config.maps" />
<ItemInfo v-else <ItemInfo v-else :item="item" />
:item="item" />
</template> </template>
</Widget> </Widget>
</template> </template>
@@ -20,6 +19,7 @@ import type { ItemCheckWidget } from './widget.js'
import Widget from '../overlay/Widget.vue' import Widget from '../overlay/Widget.vue'
import MapCheck from '../map-check/MapCheck.vue' import MapCheck from '../map-check/MapCheck.vue'
import ItemInfo from './ItemInfo.vue' import ItemInfo from './ItemInfo.vue'
import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue";
const props = defineProps<{ const props = defineProps<{
config: ItemCheckWidget config: ItemCheckWidget

View File

@@ -1,42 +1,54 @@
import { Host } from '@/web/background/IPC' import { Host } from '@/web/background/IPC';
import { AppConfig } from '@/web/Config' import { AppConfig } from '@/web/Config';
import { ParsedItem, parseClipboard } from '@/parser' import { ParsedItem, parseClipboard } from '@/parser';
const POEDB_LANGS = { 'en': 'us', 'ru': 'ru', 'cmn-Hant': 'tw', 'ko': 'kr' } const POEDB_LANGS = { en: 'us', ru: 'ru', 'cmn-Hant': 'tw', ko: 'kr' };
export function registerActions() { export function registerActions() {
Host.onEvent('MAIN->CLIENT::item-text', (e) => { Host.onEvent('MAIN->CLIENT::item-text', (e) => {
if (!['open-wiki', 'open-craft-of-exile', 'open-poedb', 'search-similar'].includes(e.target)) return if (
const parsed = parseClipboard(e.clipboard) ![
if (!parsed.isOk()) return 'open-wiki',
'open-craft-of-exile',
'open-poedb',
'search-similar',
].includes(e.target)
)
return;
const parsed = parseClipboard(e.clipboard);
if (!parsed.isOk()) return;
if (e.target === 'open-wiki') { if (e.target === 'open-wiki') {
openWiki(parsed.value) openWiki(parsed.value);
} else if (e.target === 'open-craft-of-exile') { } else if (e.target === 'open-craft-of-exile') {
openCoE(parsed.value) openCoE(parsed.value);
} else if (e.target === 'open-poedb') { } else if (e.target === 'open-poedb') {
openPoedb(parsed.value) openPoedb(parsed.value);
} else if (e.target === 'search-similar') { } else if (e.target === 'search-similar') {
findSimilarItems(parsed.value) findSimilarItems(parsed.value);
} }
}) });
} }
export function openWiki(item: ParsedItem) { export function openWiki(item: ParsedItem) {
window.open(`https://www.poewiki.net/wiki/${item.info.refName}`) window.open(`https://www.poe2wiki.net/wiki/${item.info.refName}`);
} }
export function openPoedb(item: ParsedItem) { export function openPoedb(item: ParsedItem) {
window.open(`https://poedb.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}`) window.open(
`https://poe2db.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}`
);
} }
export function openCoE(item: ParsedItem) { export function openCoE(item: ParsedItem) {
const encodedClipboard = encodeURIComponent(item.rawText) const encodedClipboard = encodeURIComponent(item.rawText);
window.open(`https://craftofexile.com/?eimport=${encodedClipboard}`) window.open(
`https://craftofexile.com/?game=poe2&eimport=${encodedClipboard}`
);
} }
export function findSimilarItems(item: ParsedItem) { export function findSimilarItems(item: ParsedItem) {
const text = JSON.stringify(item.info.name) const text = JSON.stringify(item.info.name);
Host.sendEvent({ Host.sendEvent({
name: 'CLIENT->MAIN::user-action', name: 'CLIENT->MAIN::user-action',
payload: { action: 'stash-search', text } payload: { action: 'stash-search', text },
}) });
} }

View File

@@ -5,12 +5,8 @@
<div class="flex-1 text-center">{{ mapName }}</div> <div class="flex-1 text-center">{{ mapName }}</div>
<div class="ml-8 text-gray-400">{{ t('map_check.profile') }}</div> <div class="ml-8 text-gray-400">{{ t('map_check.profile') }}</div>
<div class="flex gap-0.5"> <div class="flex gap-0.5">
<button <button v-for="profile in profiles" :key="profile.text" @click="profile.select"
v-for="profile in profiles" :key="profile.text" :class="{ 'border border-gray-600': profile.active }" class="w-6 bg-gray-800">{{ profile.text }}</button>
@click="profile.select"
:class="{ 'border border-gray-600': profile.active }"
class="w-6 bg-gray-800"
>{{ profile.text }}</button>
</div> </div>
</div> </div>
<FullscreenImage v-if="image" :src="image" style="height: auto;" /> <FullscreenImage v-if="image" :src="image" style="height: auto;" />
@@ -18,10 +14,8 @@
{{ t('map_check.no_mods') }} {{ t('map_check.no_mods') }}
</div> </div>
<div v-else class="py-2 flex flex-col"> <div v-else class="py-2 flex flex-col">
<MapStatButton v-for="stat in mapStats" :key="stat.matcher" <MapStatButton v-for="stat in mapStats" :key="stat.matcher" :stat="stat" :config="config" />
:stat="stat" :config="config" /> <div v-for="stat of item.unknownModifiers" :key="stat.type + '/' + stat.text" class="py-1 px-8">
<div v-for="stat of item.unknownModifiers" :key="stat.type + '/' + stat.text"
class="py-1 px-8">
<span class="text-orange-400">{{ t('Not recognized modifier') }} &mdash;</span> {{ stat.text }} <span class="text-orange-400">{{ t('Not recognized modifier') }} &mdash;</span> {{ stat.text }}
</div> </div>
</div> </div>

View File

@@ -1,11 +1,10 @@
<template> <template>
<transition <transition enter-active-class="animate__animated animate__fadeIn"
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__backOutDown"> leave-active-class="animate__animated animate__backOutDown">
<div :class="$style.widget" v-if="show"> <div :class="$style.widget" v-if="show">
<div :class="$style.box"> <div :class="$style.box">
<div class="py-2 px-4"> <div class="py-2 px-4">
<div class="text-base">Awakened PoE Trade</div> <div class="text-base">Awakened PoE2 Trade2</div>
<p>{{ t('app_is_ready') }}</p> <p>{{ t('app_is_ready') }}</p>
</div> </div>
</div> </div>

View File

@@ -1,12 +1,11 @@
<template> <template>
<widget :config="config" :hideable="false" :removable="false" move-handles="corners" v-slot="{ isEditing }"> <widget :config="config" :hideable="false" :removable="false" move-handles="corners" v-slot="{ isEditing }">
<div class="widget-default-style"> <div class="widget-default-style">
<ConversionWarningBanner />
<div class="p-1 flex gap-1 items-center text-base"> <div class="p-1 flex gap-1 items-center text-base">
<template v-for="widget in widgets" :key="widget.wmId"> <template v-for="widget in widgets" :key="widget.wmId">
<button @click="toggle(widget)" <button @click="toggle(widget)" :class="widget.wmWants === 'show' ? 'border-gray-500' : 'border-gray-800'"
:class="widget.wmWants === 'show' ? 'border-gray-500' : 'border-gray-800'" class="bg-gray-800 rounded text-gray-100 p-2 leading-none whitespace-nowrap border">
class="bg-gray-800 rounded text-gray-100 p-2 leading-none whitespace-nowrap border"
>
<i v-if="widget.wmType === 'settings'" class="fas fa-cog align-bottom" /> <i v-if="widget.wmType === 'settings'" class="fas fa-cog align-bottom" />
<i v-else-if="widget.wmType === 'item-search'" class="fas fa-search align-bottom" /> <i v-else-if="widget.wmType === 'item-search'" class="fas fa-search align-bottom" />
<template v-else>{{ widget.wmTitle || `#${widget.wmId}` }}</template> <template v-else>{{ widget.wmTitle || `#${widget.wmId}` }}</template>
@@ -22,9 +21,12 @@
<!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap">Screen saver</button> --> <!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap">Screen saver</button> -->
<!-- add widget --> <!-- add widget -->
<div class="text-gray-600 text-sm px-1 select-none whitespace-nowrap">{{ t(':add') }}</div> <div class="text-gray-600 text-sm px-1 select-none whitespace-nowrap">{{ t(':add') }}</div>
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('timer')">{{ t('stopwatch.name') }}</button> <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('stash-search')">{{ t('stash_search.name') }}</button> @click="createOfType('timer')">{{ t('stopwatch.name') }}</button>
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('image-strip')">{{ t('image_strip.name') }}</button> <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
@click="createOfType('stash-search')">{{ t('stash_search.name') }}</button>
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
@click="createOfType('image-strip')">{{ t('image_strip.name') }}</button>
<!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('TODO')">Image</button> --> <!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('TODO')">Image</button> -->
</div> </div>
</template> </template>
@@ -34,8 +36,7 @@
<ui-toggle v-model="config.alwaysShow">{{ t(':always_show') }}</ui-toggle> <ui-toggle v-model="config.alwaysShow">{{ t(':always_show') }}</ui-toggle>
</div> </div>
<div v-else class="px-1 pb-1"> <div v-else class="px-1 pb-1">
<textarea class="px-2 py-1.5 bg-gray-700 rounded resize-none block" <textarea class="px-2 py-1.5 bg-gray-700 rounded resize-none block" rows="1" spellcheck="false"
rows="1" spellcheck="false"
:placeholder="t(':price_check')" @input="handleItemPaste"></textarea> :placeholder="t(':price_check')" @input="handleItemPaste"></textarea>
</div> </div>
</div> </div>
@@ -50,9 +51,10 @@ import { Widget as IWidget, WidgetManager, WidgetMenu } from './interfaces'
import { Host } from '@/web/background/IPC' import { Host } from '@/web/background/IPC'
import Widget from './Widget.vue' import Widget from './Widget.vue'
import { useI18nNs } from '@/web/i18n' import { useI18nNs } from '@/web/i18n'
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
export default defineComponent({ export default defineComponent({
components: { Widget, UiToggle, UiPopover }, components: { Widget, UiToggle, UiPopover, ConversionWarningBanner },
props: { props: {
config: { config: {
type: Object as PropType<WidgetMenu>, type: Object as PropType<WidgetMenu>,

View File

@@ -1,42 +1,24 @@
<template> <template>
<div v-if="show" class="p-4 layout-column min-h-0"> <div v-if="show" class="p-4 layout-column min-h-0">
<filter-name <filter-name :filters="itemFilters" :item="item" />
:filters="itemFilters" <price-prediction v-if="showPredictedPrice" class="mb-4" :item="item" />
:item="item" /> <price-trend v-else :item="item" :filters="itemFilters" />
<price-prediction v-if="showPredictedPrice" class="mb-4" <filters-block ref="filtersComponent" :filters="itemFilters" :stats="itemStats" :item="item" :presets="presets"
:item="item" /> @preset="selectPreset" @submit="doSearch = true" />
<price-trend v-else <trade-listing v-if="tradeAPI === 'trade' && doSearch" ref="tradeService" :filters="itemFilters" :stats="itemStats"
:item="item"
:filters="itemFilters" />
<filters-block
ref="filtersComponent"
:filters="itemFilters"
:stats="itemStats"
:item="item"
:presets="presets"
@preset="selectPreset"
@submit="doSearch = true" />
<trade-listing
v-if="tradeAPI === 'trade' && doSearch"
ref="tradeService"
:filters="itemFilters"
:stats="itemStats"
:item="item" />
<trade-bulk
v-if="tradeAPI === 'bulk' && doSearch"
ref="tradeService"
:filters="itemFilters"
:item="item" /> :item="item" />
<trade-bulk v-if="tradeAPI === 'bulk' && doSearch" ref="tradeService" :filters="itemFilters" :item="item" />
<div v-if="!doSearch" class="flex justify-between items-center"> <div v-if="!doSearch" class="flex justify-between items-center">
<div class="flex w-40" @mouseenter="handleSearchMouseenter"> <div class="flex w-40" @mouseenter="handleSearchMouseenter">
<button class="btn" @click="doSearch = true" style="min-width: 5rem;">{{ t('Search') }}</button> <button class="btn" @click="doSearch = true" style="min-width: 5rem;">{{ t('Search') }}</button>
</div> </div>
<trade-links v-if="tradeAPI === 'trade'" <trade-links v-if="tradeAPI === 'trade'" :get-link="makeTradeLink" />
:get-link="makeTradeLink" />
</div> </div>
<stack-value :filters="itemFilters" :item="item" /> <stack-value :filters="itemFilters" :item="item" />
<div v-if="showSupportLinks" class="mt-auto border border-dashed p-2"> <div v-if="showSupportLinks" class="mt-auto border border-dashed p-2">
<div class="mb-1">{{ t('Support development on') }} <a href="https://patreon.com/awakened_poe_trade" class="inline-flex align-middle animate__animated animate__fadeInRight" target="_blank"><img class="inline h-5" src="/images/Patreon.svg"></a></div> <div class="mb-1">{{ t('Support development on') }} <a href="https://patreon.com/awakened_poe_trade"
class="inline-flex align-middle animate__animated animate__fadeInRight" target="_blank"><img
class="inline h-5" src="/images/Patreon.svg"></a></div>
<i18n-t keypath="app.thanks_3rd_party" tag="div"> <i18n-t keypath="app.thanks_3rd_party" tag="div">
<a href="https://poeprices.info" target="_blank" class="bg-gray-900 px-1 rounded">poeprices.info</a> <a href="https://poeprices.info" target="_blank" class="bg-gray-900 px-1 rounded">poeprices.info</a>
<a href="https://poe.ninja/support" target="_blank" class="bg-gray-900 px-1 rounded">poe.ninja</a> <a href="https://poe.ninja/support" target="_blank" class="bg-gray-900 px-1 rounded">poe.ninja</a>
@@ -238,7 +220,7 @@ export default defineComponent({
presets.value.active = id presets.value.active = id
}, },
makeTradeLink() { makeTradeLink() {
return `https://${getTradeEndpoint()}/trade/search/${itemFilters.value.trade.league}?q=${JSON.stringify(createTradeRequest(itemFilters.value, itemStats.value, props.item))}` return `https://${getTradeEndpoint()}/trade2/search/poe2/${itemFilters.value.trade.league}?q=${JSON.stringify(createTradeRequest(itemFilters.value, itemStats.value, props.item))}`
} }
} }
} }

View File

@@ -1,24 +1,21 @@
<template> <template>
<div <div style="top: 0; left: 0; height: 100%; width: 100%; position: absolute;"
style="top: 0; left: 0; height: 100%; width: 100%; position: absolute;"
class="flex grow h-full pointer-events-none" :class="{ class="flex grow h-full pointer-events-none" :class="{
'flex-row': clickPosition === 'stash', 'flex-row': clickPosition === 'stash',
'flex-row-reverse': clickPosition === 'inventory', 'flex-row-reverse': clickPosition === 'inventory',
}"> }">
<div v-if="!isBrowserShown" class="layout-column shrink-0" <div v-if="!isBrowserShown" class="layout-column shrink-0" style="width: var(--game-panel);">
style="width: var(--game-panel);">
</div> </div>
<div id="price-window" class="layout-column shrink-0 text-gray-200 pointer-events-auto" style="width: 28.75rem;"> <div id="price-window" class="layout-column shrink-0 text-gray-200 pointer-events-auto" style="width: 28.75rem;">
<ConversionWarningBanner />
<AppTitleBar @close="closePriceCheck" @click="openLeagueSelection" :title="title"> <AppTitleBar @close="closePriceCheck" @click="openLeagueSelection" :title="title">
<ui-popover v-if="stableOrbCost" trigger="click" boundary="#price-window"> <ui-popover v-if="stableOrbCost" trigger="click" boundary="#price-window">
<template #target> <template #target>
<button><i class="fas fa-exchange-alt" /> {{ stableOrbCost }}</button> <button><i class="fas fa-exchange-alt" /> {{ stableOrbCost }}</button>
</template> </template>
<template #content> <template #content>
<item-quick-price class="text-base" <item-quick-price class="text-base" :price="{ min: stableOrbCost, max: stableOrbCost, currency: 'chaos' }"
:price="{ min: stableOrbCost, max: stableOrbCost, currency: 'chaos' }" item-img="/images/divine.png" />
item-img="/images/divine.png"
/>
<div v-for="i in 9" :key="i"> <div v-for="i in 9" :key="i">
<div class="pl-1">{{ i / 10 }} div {{ Math.round(stableOrbCost * i / 10) }} c</div> <div class="pl-1">{{ i / 10 }} div {{ Math.round(stableOrbCost * i / 10) }} c</div>
</div> </div>
@@ -29,8 +26,7 @@
</AppTitleBar> </AppTitleBar>
<div class="grow layout-column min-h-0 bg-gray-800"> <div class="grow layout-column min-h-0 bg-gray-800">
<background-info /> <background-info />
<check-position-circle v-if="showCheckPos" <check-position-circle v-if="showCheckPos" :position="checkPosition" style="z-index: -1;" />
:position="checkPosition" style="z-index: -1;" />
<template v-if="item?.isErr()"> <template v-if="item?.isErr()">
<ui-error-box class="m-4"> <ui-error-box class="m-4">
<template #name>{{ t(item.error.name) }}</template> <template #name>{{ t(item.error.name) }}</template>
@@ -40,8 +36,7 @@
</template> </template>
<template v-else-if="item?.isOk()"> <template v-else-if="item?.isOk()">
<unidentified-resolver :item="item.value" @identify="handleIdentification($event)" /> <unidentified-resolver :item="item.value" @identify="handleIdentification($event)" />
<checked-item v-if="isLeagueSelected" <checked-item v-if="isLeagueSelected" :item="item.value" :advanced-check="advancedCheck" />
:item="item.value" :advanced-check="advancedCheck" />
</template> </template>
<div v-if="isBrowserShown" class="bg-gray-900 px-6 py-2 truncate"> <div v-if="isBrowserShown" class="bg-gray-900 px-6 py-2 truncate">
<i18n-t keypath="app.toggle_browser_hint" tag="div"> <i18n-t keypath="app.toggle_browser_hint" tag="div">
@@ -50,16 +45,14 @@
</div> </div>
</div> </div>
</div> </div>
<webview v-if="isBrowserShown" ref="iframeEl" <webview v-if="isBrowserShown" ref="iframeEl" class="pointer-events-auto flex-1" width="100%" height="100%" />
class="pointer-events-auto flex-1"
width="100%" height="100%" />
<div v-else class="layout-column flex-1 min-w-0"> <div v-else class="layout-column flex-1 min-w-0">
<div class="flex" :class="{ <div class="flex" :class="{
'flex-row': clickPosition === 'stash', 'flex-row': clickPosition === 'stash',
'flex-row-reverse': clickPosition === 'inventory' 'flex-row-reverse': clickPosition === 'inventory'
}"> }">
<related-items v-if="item?.isOk()" class="pointer-events-auto" <related-items v-if="item?.isOk()" class="pointer-events-auto" :item="item.value"
:item="item.value" :click-position="clickPosition" /> :click-position="clickPosition" />
<rate-limiter-state class="pointer-events-auto" /> <rate-limiter-state class="pointer-events-auto" />
</div> </div>
</div> </div>
@@ -86,6 +79,7 @@ import CheckPositionCircle from './CheckPositionCircle.vue'
import AppTitleBar from '@/web/ui/AppTitlebar.vue' import AppTitleBar from '@/web/ui/AppTitlebar.vue'
import ItemQuickPrice from '@/web/ui/ItemQuickPrice.vue' import ItemQuickPrice from '@/web/ui/ItemQuickPrice.vue'
import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces' import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces'
import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue";
type ParseError = { name: string; message: string; rawText: ParsedItem['rawText'] } type ParseError = { name: string; message: string; rawText: ParsedItem['rawText'] }
@@ -100,7 +94,8 @@ export default defineComponent({
CheckPositionCircle, CheckPositionCircle,
ItemQuickPrice, ItemQuickPrice,
UiErrorBox, UiErrorBox,
UiPopover UiPopover,
ConversionWarningBanner
}, },
props: { props: {
config: { config: {
@@ -183,7 +178,7 @@ export default defineComponent({
}) })
const leagues = useLeagues() const leagues = useLeagues()
const title = computed(() => leagues.selectedId.value || 'Awakened PoE Trade') const title = computed(() => leagues.selectedId.value || 'Awakened PoE2 Trade2')
const stableOrbCost = computed(() => (xchgRate.value) ? Math.round(xchgRate.value) : null) const stableOrbCost = computed(() => (xchgRate.value) ? Math.round(xchgRate.value) : null)
const isBrowserShown = computed(() => props.config.wmFlags.includes('has-browser')) const isBrowserShown = computed(() => props.config.wmFlags.includes('has-browser'))
const overlayKey = computed(() => AppConfig().overlayKey) const overlayKey = computed(() => AppConfig().overlayKey)

View File

@@ -1,80 +1,91 @@
import { ParsedItem } from '@/parser' import { ParsedItem } from '@/parser';
import { useLeagues } from '@/web/background/Leagues' import { useLeagues } from '@/web/background/Leagues';
import { Host } from '@/web/background/IPC' import { Host } from '@/web/background/IPC';
import { Cache } from '../trade/Cache' import { Cache } from '../trade/Cache';
import { usePoeninja } from '@/web/background/Prices' import { usePoeninja } from '@/web/background/Prices';
const cache = new Cache() const cache = new Cache();
interface PoepricesApiResponse { /* eslint-disable camelcase */ interface PoepricesApiResponse {
currency: 'chaos' | 'divine' | 'exalt' /* eslint-disable camelcase */ currency: 'chaos' | 'divine' | 'exalt';
error: number error: number;
error_msg: string error_msg: string;
warning_msg: string warning_msg: string;
max: number max: number;
min: number min: number;
pred_confidence_score: number pred_confidence_score: number;
pred_explanation: Array<[string, number]> pred_explanation: Array<[string, number]>;
} }
export interface RareItemPrice { export interface RareItemPrice {
max: number max: number;
min: number min: number;
confidence: number confidence: number;
currency: 'chaos' | 'div' currency: 'chaos' | 'div';
explanation: Array<{ explanation: Array<{
name: string name: string;
contrib: number contrib: number;
}> }>;
} }
export async function requestPoeprices (item: ParsedItem): Promise<RareItemPrice> { export async function requestPoeprices(
item: ParsedItem
): Promise<RareItemPrice> {
const query = querystring({ const query = querystring({
i: utf8ToBase64(transformItemText(item.rawText)), i: utf8ToBase64(transformItemText(item.rawText)),
l: useLeagues().selectedId.value, l: useLeagues().selectedId.value,
s: 'awakened-poe-trade' s: 'awakened-poe-trade', // might be required name here
}) });
let data = cache.get<PoepricesApiResponse>(query) let data = cache.get<PoepricesApiResponse>(query);
if (!data) { if (!data) {
const response = await Host.proxy(`www.poeprices.info/api?${query}`) const response = await Host.proxy(`www.poeprices.info/api?${query}`);
try { try {
data = await response.json() as PoepricesApiResponse data = (await response.json()) as PoepricesApiResponse;
} catch (e) { } catch (e) {
throw new Error(`${response.status}, poeprices.info API is under load or down.`) throw new Error(
`${response.status}, poeprices.info API is under load or down.`
);
} }
if (data.error !== 0) { if (data.error !== 0) {
throw new Error(data.error_msg) throw new Error(data.error_msg);
} }
cache.set<PoepricesApiResponse>(query, data, 300) cache.set<PoepricesApiResponse>(query, data, 300);
} }
if (data.currency === 'exalt') { if (data.currency === 'exalt') {
const { findPriceByQuery, autoCurrency } = usePoeninja() const { findPriceByQuery, autoCurrency } = usePoeninja();
const xchgExalted = findPriceByQuery({ ns: 'ITEM', name: 'Exalted Orb', variant: undefined }) const xchgExalted = findPriceByQuery({
ns: 'ITEM',
name: 'Exalted Orb',
variant: undefined,
});
if (!xchgExalted) { if (!xchgExalted) {
throw new Error('poeprices.info gave the price in Exalted Orbs.') throw new Error('poeprices.info gave the price in Exalted Orbs.');
} }
const converted = autoCurrency([data.min * xchgExalted.chaos, data.max * xchgExalted.chaos]) const converted = autoCurrency([
data.min = converted.min data.min * xchgExalted.chaos,
data.max = converted.max data.max * xchgExalted.chaos,
data.currency = (converted.currency === 'div') ? 'divine' : 'chaos' ]);
data.min = converted.min;
data.max = converted.max;
data.currency = converted.currency === 'div' ? 'divine' : 'chaos';
} else if (data.currency !== 'divine' && data.currency !== 'chaos') { } else if (data.currency !== 'divine' && data.currency !== 'chaos') {
throw new Error('poeprices.info gave the price in unknown currency.') throw new Error('poeprices.info gave the price in unknown currency.');
} }
return { return {
currency: (data.currency === 'divine') ? 'div' : 'chaos', currency: data.currency === 'divine' ? 'div' : 'chaos',
min: data.min, min: data.min,
max: data.max, max: data.max,
confidence: Math.round(data.pred_confidence_score), confidence: Math.round(data.pred_confidence_score),
explanation: data.pred_explanation.map(expl => ({ explanation: data.pred_explanation.map((expl) => ({
name: expl[0], name: expl[0],
contrib: Math.round(expl[1] * 100) contrib: Math.round(expl[1] * 100),
})) })),
} };
} }
export function getExternalLink(item: ParsedItem): string { export function getExternalLink(item: ParsedItem): string {
@@ -82,44 +93,44 @@ export function getExternalLink (item: ParsedItem): string {
i: utf8ToBase64(transformItemText(item.rawText)), i: utf8ToBase64(transformItemText(item.rawText)),
l: useLeagues().selectedId.value, l: useLeagues().selectedId.value,
s: 'awakened-poe-trade', s: 'awakened-poe-trade',
w: 1 w: 1,
}) });
return `https://www.poeprices.info/api?${query}` return `https://www.poeprices.info/api?${query}`;
} }
export async function sendFeedback( export async function sendFeedback(
feedback: { text: string, option: 'fair' | 'low' | 'high' }, feedback: { text: string; option: 'fair' | 'low' | 'high' },
prediction: Pick<PoepricesApiResponse, 'min' | 'max' | 'currency'>, prediction: Pick<PoepricesApiResponse, 'min' | 'max' | 'currency'>,
item: ParsedItem item: ParsedItem
): Promise<void> { ): Promise<void> {
const body = new FormData() const body = new FormData();
body.append('selector', feedback.option) body.append('selector', feedback.option);
body.append('feedbacktxt', feedback.text) body.append('feedbacktxt', feedback.text);
body.append('qitem_txt', utf8ToBase64(transformItemText(item.rawText))) body.append('qitem_txt', utf8ToBase64(transformItemText(item.rawText)));
body.append('source', 'awakened-poe-trade') body.append('source', 'awakened-poe-trade');
body.append('min', String(prediction.min)) body.append('min', String(prediction.min));
body.append('max', String(prediction.max)) body.append('max', String(prediction.max));
body.append('currency', prediction.currency) body.append('currency', prediction.currency);
body.append('league', useLeagues().selectedId.value!) body.append('league', useLeagues().selectedId.value!);
// body.append('debug', String(1)) // body.append('debug', String(1))
const response = await Host.proxy('www.poeprices.info/send_feedback', { const response = await Host.proxy('www.poeprices.info/send_feedback', {
method: 'POST', method: 'POST',
body body,
}) });
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const text = await response.text() const text = await response.text();
// console.assert(text === `"${feedback.option}"`) // console.assert(text === `"${feedback.option}"`)
} }
function utf8ToBase64(value: string) { function utf8ToBase64(value: string) {
return btoa(unescape(encodeURIComponent(value))) return btoa(unescape(encodeURIComponent(value)));
} }
function querystring(q: Record<string, any>) { function querystring(q: Record<string, any>) {
return Object.entries(q) return Object.entries(q)
.map(pair => pair.map(encodeURIComponent).join('=')) .map((pair) => pair.map(encodeURIComponent).join('='))
.join('&') .join('&');
} }
/** /**
@@ -129,5 +140,5 @@ function transformItemText (rawText: string) {
// this may not account for all cases // this may not account for all cases
return rawText return rawText
.replace(/(?<=\d)(\([^)]+\))/gm, '') .replace(/(?<=\d)(\([^)]+\))/gm, '')
.replace(/^\{.+\}$\n/gm, '') .replace(/^\{.+\}$\n/gm, '');
} }

View File

@@ -6,12 +6,14 @@
<span class="mr-1">{{ t(':matched') }}</span> <span class="mr-1">{{ t(':matched') }}</span>
<span v-if="!result" class="text-gray-600">...</span> <span v-if="!result" class="text-gray-600">...</span>
<div v-else class="flex items-center"> <div v-else class="flex items-center">
<button class="btn flex items-center mr-1" :style="{ background: selectedCurr !== 'xchgChaos' ? 'transparent' : undefined }" <button class="btn flex items-center mr-1"
:style="{ background: selectedCurr !== 'xchgChaos' ? 'transparent' : undefined }"
@click="selectedCurr = 'xchgChaos'"> @click="selectedCurr = 'xchgChaos'">
<img src="/images/chaos.png" class="trade-bulk-currency-icon"> <img src="/images/chaos.png" class="trade-bulk-currency-icon">
<span>{{ result.xchgChaos.listed.value?.total ?? '?' }}</span> <span>{{ result.xchgChaos.listed.value?.total ?? '?' }}</span>
</button> </button>
<button class="btn flex items-center mr-1" :style="{ background: selectedCurr !== 'xchgStable' ? 'transparent' : undefined }" <button class="btn flex items-center mr-1"
:style="{ background: selectedCurr !== 'xchgStable' ? 'transparent' : undefined }"
@click="selectedCurr = 'xchgStable'"> @click="selectedCurr = 'xchgStable'">
<img src="/images/divine.png" class="trade-bulk-currency-icon"> <img src="/images/divine.png" class="trade-bulk-currency-icon">
<span>{{ result.xchgStable.listed.value?.total ?? '?' }}</span> <span>{{ result.xchgStable.listed.value?.total ?? '?' }}</span>
@@ -19,8 +21,7 @@
<span class="ml-1"><online-filter :filters="filters" /></span> <span class="ml-1"><online-filter :filters="filters" /></span>
</div> </div>
</div> </div>
<trade-links v-if="result" <trade-links v-if="result" :get-link="makeTradeLink" />
:get-link="makeTradeLink" />
</div> </div>
<div class="layout-column overflow-y-auto overflow-x-hidden"> <div class="layout-column overflow-y-auto overflow-x-hidden">
<table class="table-stripped w-full"> <table class="table-stripped w-full">
@@ -30,7 +31,10 @@
<div class="px-2">{{ t(':price') }}</div> <div class="px-2">{{ t(':price') }}</div>
</th> </th>
<th class="trade-table-heading"> <th class="trade-table-heading">
<div class="pl-1 pr-2 flex text-xs" style="line-height: 1.3125rem;"><span class="w-8 inline-block text-right -ml-px mr-px">{{ (selectedCurr === 'xchgChaos') ? 'chaos' : 'div' }}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ t(':bulk') }}</span></div> <div class="pl-1 pr-2 flex text-xs" style="line-height: 1.3125rem;"><span
class="w-8 inline-block text-right -ml-px mr-px">{{ (selectedCurr === 'xchgChaos') ? 'chaos' : 'div'
}}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ t(':bulk')
}}</span></div>
</th> </th>
<th class="trade-table-heading"> <th class="trade-table-heading">
<div class="px-1">{{ t(':stock') }}</div> <div class="px-1">{{ t(':stock') }}</div>
@@ -55,19 +59,25 @@
</tr> </tr>
<tr v-else :key="result.id"> <tr v-else :key="result.id">
<td class="px-2">{{ Number((result.exchangeAmount / result.itemAmount).toFixed(4)) }}</td> <td class="px-2">{{ Number((result.exchangeAmount / result.itemAmount).toFixed(4)) }}</td>
<td class="pl-1 whitespace-nowrap"><span class="w-8 inline-block text-right">{{ result.exchangeAmount }}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ result.itemAmount }}</span></td> <td class="pl-1 whitespace-nowrap"><span class="w-8 inline-block text-right">{{ result.exchangeAmount
}}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ result.itemAmount
}}</span></td>
<td class="px-1 text-right">{{ result.stock }}</td> <td class="px-1 text-right">{{ result.stock }}</td>
<td class="px-1 text-right"><i v-if="result.stock < result.itemAmount" class="fas fa-exclamation-triangle mr-1 text-gray-500"></i>{{ Math.floor(result.stock / result.itemAmount) }}</td> <td class="px-1 text-right"><i v-if="result.stock < result.itemAmount"
class="fas fa-exclamation-triangle mr-1 text-gray-500"></i>{{ Math.floor(result.stock /
result.itemAmount) }}</td>
<td class="pr-2 pl-4 whitespace-nowrap"> <td class="pr-2 pl-4 whitespace-nowrap">
<div class="inline-flex items-center"> <div class="inline-flex items-center">
<div class="account-status" :class="result.accountStatus"></div> <div class="account-status" :class="result.accountStatus"></div>
<div class="ml-1 font-sans text-xs">{{ result.relativeDate }}</div> <div class="ml-1 font-sans text-xs">{{ result.relativeDate }}</div>
</div> </div>
<span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{ t('You') }}</span> <span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{
t('You') }}</span>
</td> </td>
<td v-if="showSeller" class="px-2 whitespace-nowrap"> <td v-if="showSeller" class="px-2 whitespace-nowrap">
<span v-if="result.isMine" class="rounded px-1 text-gray-800 bg-gray-400">{{ t('You') }}</span> <span v-if="result.isMine" class="rounded px-1 text-gray-800 bg-gray-400">{{ t('You') }}</span>
<span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName }}</span> <span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName
}}</span>
</td> </td>
</tr> </tr>
</template> </template>
@@ -230,7 +240,7 @@ export default defineComponent({
const have = _have ?? ((selectedCurr.value === 'xchgStable') ? ['divine'] : ['chaos']) const have = _have ?? ((selectedCurr.value === 'xchgStable') ? ['divine'] : ['chaos'])
const httpPostBody = createTradeRequest(props.filters, props.item, have) const httpPostBody = createTradeRequest(props.filters, props.item, have)
const httpGetQuery = { exchange: httpPostBody.query } const httpGetQuery = { exchange: httpPostBody.query }
return `https://${getTradeEndpoint()}/trade/exchange/${props.filters.trade.league}?q=${JSON.stringify(httpGetQuery)}` return `https://${getTradeEndpoint()}/trade2/exchange/poe2/${props.filters.trade.league}?q=${JSON.stringify(httpGetQuery)}`
} }
const { t } = useI18nNs('trade_result') const { t } = useI18nNs('trade_result')

View File

@@ -8,8 +8,7 @@
</div> </div>
<online-filter v-if="list" :by-time="true" :filters="filters" /> <online-filter v-if="list" :by-time="true" :filters="filters" />
<div class="flex-1"></div> <div class="flex-1"></div>
<trade-links v-if="list" <trade-links v-if="list" :get-link="makeTradeLink" />
:get-link="makeTradeLink" />
</div> </div>
<div class="layout-column overflow-y-auto overflow-x-hidden"> <div class="layout-column overflow-y-auto overflow-x-hidden">
<table class="table-stripped w-full"> <table class="table-stripped w-full">
@@ -46,21 +45,28 @@
<td colspan="100" class="text-transparent">***</td> <td colspan="100" class="text-transparent">***</td>
</tr> </tr>
<tr v-else :key="result.id"> <tr v-else :key="result.id">
<td class="px-2 whitespace-nowrap"><span :class="{ 'line-through': result.priceCurrency === 'exalted' }">{{ result.priceAmount }} {{ result.priceCurrency }}</span> <span v-if="result.listedTimes > 2" class="rounded px-1 text-gray-800 bg-gray-400 -mr-2"><span class="font-sans">×</span> {{ result.listedTimes }}</span><i v-else-if="!result.hasNote" class="fas fa-question" /></td> <td class="px-2 whitespace-nowrap"><span
:class="{ 'line-through': result.priceCurrency === 'exalted' }">{{ result.priceAmount }} {{
result.priceCurrency }}</span> <span v-if="result.listedTimes > 2"
class="rounded px-1 text-gray-800 bg-gray-400 -mr-2"><span class="font-sans">×</span> {{
result.listedTimes }}</span><i v-else-if="!result.hasNote" class="fas fa-question" /></td>
<td v-if="item.stackSize" class="px-2 text-right">{{ result.stackSize }}</td> <td v-if="item.stackSize" class="px-2 text-right">{{ result.stackSize }}</td>
<td v-if="filters.itemLevel" class="px-2 whitespace-nowrap text-right">{{ result.itemLevel }}</td> <td v-if="filters.itemLevel" class="px-2 whitespace-nowrap text-right">{{ result.itemLevel }}</td>
<td v-if="item.category === 'Gem'" class="pl-2 whitespace-nowrap">{{ result.level }}</td> <td v-if="item.category === 'Gem'" class="pl-2 whitespace-nowrap">{{ result.level }}</td>
<td v-if="filters.quality || item.category === 'Gem'" class="px-2 whitespace-nowrap text-blue-400 text-right">{{ result.quality }}</td> <td v-if="filters.quality || item.category === 'Gem'"
class="px-2 whitespace-nowrap text-blue-400 text-right">{{ result.quality }}</td>
<td class="pr-2 pl-4 whitespace-nowrap"> <td class="pr-2 pl-4 whitespace-nowrap">
<div class="inline-flex items-center"> <div class="inline-flex items-center">
<div class="account-status" :class="result.accountStatus"></div> <div class="account-status" :class="result.accountStatus"></div>
<div class="ml-1 font-sans text-xs">{{ result.relativeDate }}</div> <div class="ml-1 font-sans text-xs">{{ result.relativeDate }}</div>
</div> </div>
<span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{ t('You') }}</span> <span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{
t('You') }}</span>
</td> </td>
<td v-if="showSeller" class="px-2 whitespace-nowrap"> <td v-if="showSeller" class="px-2 whitespace-nowrap">
<span v-if="result.isMine" class="rounded px-1 text-gray-800 bg-gray-400">{{ t('You') }}</span> <span v-if="result.isMine" class="rounded px-1 text-gray-800 bg-gray-400">{{ t('You') }}</span>
<span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName }}</span> <span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName
}}</span>
</td> </td>
</tr> </tr>
</template> </template>
@@ -222,8 +228,8 @@ export default defineComponent({
function makeTradeLink() { function makeTradeLink() {
return (searchResult.value) return (searchResult.value)
? `https://${getTradeEndpoint()}/trade/search/${props.filters.trade.league}/${searchResult.value.id}` ? `https://${getTradeEndpoint()}/trade2/search/poe2/${props.filters.trade.league}/${searchResult.value.id}`
: `https://${getTradeEndpoint()}/trade/search/${props.filters.trade.league}?q=${JSON.stringify(createTradeRequest(props.filters, props.stats, props.item))}` : `https://${getTradeEndpoint()}/trade2/search/poe2/${props.filters.trade.league}?q=${JSON.stringify(createTradeRequest(props.filters, props.stats, props.item))}`
} }
const { t } = useI18nNs('trade_result') const { t } = useI18nNs('trade_result')
@@ -271,10 +277,14 @@ export default defineComponent({
height: 0.375rem; height: 0.375rem;
border-radius: 100%; border-radius: 100%;
&.online { /* */ } &.online {
/* */
}
&.offline { &.offline {
@apply bg-red-600; @apply bg-red-600;
} }
&.afk { &.afk {
@apply bg-orange-500; @apply bg-orange-500;
} }

View File

@@ -1,92 +1,108 @@
import { DateTime } from 'luxon' import { DateTime } from 'luxon';
import { Host } from '@/web/background/IPC' import { Host } from '@/web/background/IPC';
import { TradeResponse, Account, getTradeEndpoint, RATE_LIMIT_RULES, adjustRateLimits, tradeTag, preventQueueCreation } from './common' import {
import { RateLimiter } from './RateLimiter' TradeResponse,
import { ItemFilters } from '../filters/interfaces' Account,
import { ParsedItem } from '@/parser' getTradeEndpoint,
import { Cache } from './Cache' RATE_LIMIT_RULES,
adjustRateLimits,
tradeTag,
preventQueueCreation,
} from './common';
import { RateLimiter } from './RateLimiter';
import { ItemFilters } from '../filters/interfaces';
import { ParsedItem } from '@/parser';
import { Cache } from './Cache';
interface TradeRequest { /* eslint-disable camelcase */ interface TradeRequest {
engine: 'new' /* eslint-disable camelcase */ engine: 'new';
query: { query: {
status: { option: 'online' | 'onlineleague' | 'any' } status: { option: 'online' | 'onlineleague' | 'any' };
have: string[] have: string[];
want: string[] want: string[];
minimum?: number minimum?: number;
fulfillable?: null fulfillable?: null;
} };
sort: { have: 'asc' } sort: { have: 'asc' };
} }
interface SearchResult { interface SearchResult {
id: string id: string;
result: Record<string, FetchResult> result: Record<string, FetchResult>;
total: number total: number;
} }
interface FetchResult { interface FetchResult {
id: string id: string;
listing: { listing: {
indexed: string indexed: string;
offers: Array<{ offers: Array<{
exchange: { exchange: {
currency: string currency: string;
amount: number amount: number;
} };
item: { item: {
amount: number amount: number;
stock: number stock: number;
} };
}> }>;
account: Account account: Account;
} };
} }
export interface PricingResult { export interface PricingResult {
id: string id: string;
relativeDate: string relativeDate: string;
exchangeAmount: number exchangeAmount: number;
itemAmount: number itemAmount: number;
stock: number stock: number;
accountStatus: 'offline' | 'online' | 'afk' accountStatus: 'offline' | 'online' | 'afk';
isMine: boolean isMine: boolean;
accountName: string accountName: string;
ign: string ign: string;
} }
const cache = new Cache() const cache = new Cache();
async function requestTradeResultList (body: TradeRequest, leagueId: string): Promise<SearchResult> { async function requestTradeResultList(
let data = cache.get<SearchResult>([body, leagueId]) body: TradeRequest,
leagueId: string
): Promise<SearchResult> {
let data = cache.get<SearchResult>([body, leagueId]);
if (!data) { if (!data) {
preventQueueCreation([ preventQueueCreation([{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }]);
{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }
])
await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE) await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE);
const response = await Host.proxy(`${getTradeEndpoint()}/api/trade/exchange/${leagueId}`, { const response = await Host.proxy(
`${getTradeEndpoint()}/api/trade2/exchange/${leagueId}`,
{
method: 'POST', method: 'POST',
headers: { headers: {
'Accept': 'application/json', Accept: 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}, },
body: JSON.stringify(body) body: JSON.stringify(body),
}) }
adjustRateLimits(RATE_LIMIT_RULES.EXCHANGE, response.headers) );
adjustRateLimits(RATE_LIMIT_RULES.EXCHANGE, response.headers);
const _data = await response.json() as TradeResponse<SearchResult> const _data = (await response.json()) as TradeResponse<SearchResult>;
if (_data.error) { if (_data.error) {
throw new Error(_data.error.message) throw new Error(_data.error.message);
} else { } else {
data = _data data = _data;
} }
cache.set<SearchResult>([body, leagueId], data, Cache.deriveTtl(...RATE_LIMIT_RULES.EXCHANGE)) cache.set<SearchResult>(
[body, leagueId],
data,
Cache.deriveTtl(...RATE_LIMIT_RULES.EXCHANGE)
);
} }
return data return data;
} }
function toPricingResult( function toPricingResult(
@@ -96,27 +112,35 @@ function toPricingResult (
): PricingResult { ): PricingResult {
return { return {
id: result.id, id: result.id,
relativeDate: DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ?? '', relativeDate:
DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ??
'',
exchangeAmount: result.listing.offers[offer].exchange.amount, exchangeAmount: result.listing.offers[offer].exchange.amount,
itemAmount: result.listing.offers[offer].item.amount, itemAmount: result.listing.offers[offer].item.amount,
stock: result.listing.offers[offer].item.stock, stock: result.listing.offers[offer].item.stock,
isMine: (result.listing.account.name === opts.accountName), isMine: result.listing.account.name === opts.accountName,
ign: result.listing.account.lastCharacterName, ign: result.listing.account.lastCharacterName,
accountName: result.listing.account.name, accountName: result.listing.account.name,
accountStatus: result.listing.account.online accountStatus: result.listing.account.online
? (result.listing.account.online.status === 'afk' ? 'afk' : 'online') ? result.listing.account.online.status === 'afk'
: 'offline' ? 'afk'
} : 'online'
: 'offline',
};
} }
export interface BulkSearch { export interface BulkSearch {
queryId: string queryId: string;
haveTag: string haveTag: string;
total: number total: number;
listed: PricingResult[] listed: PricingResult[];
} }
export function createTradeRequest (filters: ItemFilters, item: ParsedItem, have: string[]): TradeRequest { export function createTradeRequest(
filters: ItemFilters,
item: ParsedItem,
have: string[]
): TradeRequest {
return { return {
engine: 'new', engine: 'new',
query: { query: {
@@ -125,17 +149,22 @@ export function createTradeRequest (filters: ItemFilters, item: ParsedItem, have
status: { status: {
option: filters.trade.offline option: filters.trade.offline
? 'any' ? 'any'
: (filters.trade.onlineInLeague ? 'onlineleague' : 'online') : filters.trade.onlineInLeague
? 'onlineleague'
: 'online',
}, },
minimum: (filters.stackSize && !filters.stackSize.disabled) ? filters.stackSize.value : undefined minimum:
filters.stackSize && !filters.stackSize.disabled
? filters.stackSize.value
: undefined,
// fulfillable: null // fulfillable: null
}, },
sort: { have: 'asc' } sort: { have: 'asc' },
} };
} }
const SHOW_RESULTS = 20 const SHOW_RESULTS = 20;
const API_FETCH_LIMIT = 100 const API_FETCH_LIMIT = 100;
export async function execBulkSearch( export async function execBulkSearch(
item: ParsedItem, item: ParsedItem,
@@ -146,45 +175,51 @@ export async function execBulkSearch (
const query = await requestTradeResultList( const query = await requestTradeResultList(
createTradeRequest(filters, item, have), createTradeRequest(filters, item, have),
filters.trade.league filters.trade.league
) );
const offer = 0 const offer = 0;
const results = Object.values(query.result) const results = Object.values(query.result).filter(
.filter(result => result.listing.offers.length === 1) (result) => result.listing.offers.length === 1
);
const resultByHave = have.map(tradeTag => { const resultByHave = have.map((tradeTag) => {
const resultsTag = results.filter(result => result.listing.offers[offer].exchange.currency === tradeTag) const resultsTag = results.filter(
(result) => result.listing.offers[offer].exchange.currency === tradeTag
);
const loadedOnDemand = ( const loadedOnDemand =
tradeTag === 'chaos' && tradeTag === 'chaos' &&
resultsTag.length < SHOW_RESULTS && resultsTag.length < SHOW_RESULTS &&
query.total > API_FETCH_LIMIT query.total > API_FETCH_LIMIT;
) if (loadedOnDemand) return null;
if (loadedOnDemand) return null
const listed = resultsTag const listed = resultsTag
.sort((a, b) => .sort(
(a.listing.offers[offer].exchange.amount / a.listing.offers[offer].item.amount) - (a, b) =>
(b.listing.offers[offer].exchange.amount / b.listing.offers[offer].item.amount)) a.listing.offers[offer].exchange.amount /
a.listing.offers[offer].item.amount -
b.listing.offers[offer].exchange.amount /
b.listing.offers[offer].item.amount
)
.slice(0, SHOW_RESULTS) .slice(0, SHOW_RESULTS)
.map(result => toPricingResult(result, opts, offer)) .map((result) => toPricingResult(result, opts, offer));
const chaosIsLoaded = ( const chaosIsLoaded =
tradeTag === 'divine' && tradeTag === 'divine' &&
resultsTag.length < results.length && resultsTag.length < results.length &&
((results.length - resultsTag.length) >= SHOW_RESULTS || query.total <= API_FETCH_LIMIT) (results.length - resultsTag.length >= SHOW_RESULTS ||
) query.total <= API_FETCH_LIMIT);
return { return {
queryId: query.id, queryId: query.id,
haveTag: tradeTag, haveTag: tradeTag,
// this is a best guess when making request with multiple `have` currencies // this is a best guess when making request with multiple `have` currencies
total: (chaosIsLoaded) total: chaosIsLoaded
? resultsTag.length ? resultsTag.length
: (query.total - (results.length - resultsTag.length)), : query.total - (results.length - resultsTag.length),
listed: listed listed: listed,
} };
}) });
return resultByHave return resultByHave;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,8 @@
<div :class="$style.podium" v-if="podiumVisible"> <div :class="$style.podium" v-if="podiumVisible">
<div v-for="i in [2, 4, 5, 3, 1]"> <div v-for="i in [2, 4, 5, 3, 1]">
<div v-for="patron in patrons[i - 1]" :key="patron.from" <div v-for="patron in patrons[i - 1]" :key="patron.from"
:class="[$style.rating, $style[`rating-${patron.style}`]]" :class="[$style.rating, $style[`rating-${patron.style}`]]">{{ patron.from }}{{ (patron.months > 1) ? `
>{{ patron.from }}{{ (patron.months > 1) ? ` x${patron.months}` : null }}</div> x${patron.months}` : null }}</div>
</div> </div>
</div> </div>
<div :class="[$style.patronsHorizontal, { 'invisible': podiumVisible }]" :onMouseenter="showPodium"> <div :class="[$style.patronsHorizontal, { 'invisible': podiumVisible }]" :onMouseenter="showPodium">
@@ -17,26 +17,26 @@
</div> </div>
</div> </div>
<div :class="$style.window" class="grow layout-column" :onMouseenter="hidePodium"> <div :class="$style.window" class="grow layout-column" :onMouseenter="hidePodium">
<ConversionWarningBanner />
<AppTitleBar @close="cancel" :title="t('settings.title')" /> <AppTitleBar @close="cancel" :title="t('settings.title')" />
<div class="flex grow min-h-0"> <div class="flex grow min-h-0">
<div class="pl-2 pt-2 bg-gray-900 flex flex-col gap-1" style="min-width: 10rem;"> <div class="pl-2 pt-2 bg-gray-900 flex flex-col gap-1" style="min-width: 10rem;">
<template v-for="item of menuItems"> <template v-for="item of menuItems">
<button v-if="item.type === 'menu-item'" <button v-if="item.type === 'menu-item'" @click="item.select"
@click="item.select" :class="[$style['menu-item'], { [$style['active']]: item.isSelected }]">{{ item.name }}</button> :class="[$style['menu-item'], { [$style['active']]: item.isSelected }]">{{ item.name }}</button>
<div v-else <div v-else class="border-b mx-2 border-gray-800" />
class="border-b mx-2 border-gray-800" />
</template> </template>
<button v-if="menuItems.length >= 4" <button v-if="menuItems.length >= 4" :class="$style['quit-btn']" @click="quit">{{ t('app.quit') }}</button>
:class="$style['quit-btn']" @click="quit">{{ t('app.quit') }}</button> <div class="text-gray-400 text-center mt-auto pr-3 pt-4 pb-12"
<div class="text-gray-400 text-center mt-auto pr-3 pt-4 pb-12" style="max-width: fit-content; min-width: 100%;"> style="max-width: fit-content; min-width: 100%;">
<img class="mx-auto mb-1" src="/images/peepoLove2x.webp"> <img class="mx-auto mb-1" src="/images/peepoLove2x.webp">
{{ t('Support development on') }}<br> <a href="https://patreon.com/awakened_poe_trade" class="inline-flex mt-1" target="_blank"><img class="inline h-5" src="/images/Patreon.svg"></a> {{ t('Support development on') }}<br> <a href="https://patreon.com/awakened_poe_trade"
class="inline-flex mt-1" target="_blank"><img class="inline h-5" src="/images/Patreon.svg"></a>
</div> </div>
</div> </div>
<div class="text-gray-100 grow layout-column bg-gray-900"> <div class="text-gray-100 grow layout-column bg-gray-900">
<div class="grow overflow-y-auto bg-gray-800 rounded-tl"> <div class="grow overflow-y-auto bg-gray-800 rounded-tl">
<component v-if="configClone" <component v-if="configClone" :is="selectedComponent" :config="configClone" :configWidget="configWidget" />
:is="selectedComponent" :config="configClone" :configWidget="configWidget" />
</div> </div>
<div class="border-t bg-gray-900 border-gray-600 p-2 flex justify-end gap-x-2"> <div class="border-t bg-gray-900 border-gray-600 p-2 flex justify-end gap-x-2">
<button @click="save" class="px-3 bg-gray-800 rounded">{{ t('Save') }}</button> <button @click="save" class="px-3 bg-gray-800 rounded">{{ t('Save') }}</button>
@@ -67,6 +67,7 @@ import SettingsMaps from '../map-check/settings-maps.vue'
import SettingsStashSearch from '../stash-search/stash-search-editor.vue' import SettingsStashSearch from '../stash-search/stash-search-editor.vue'
import SettingsStopwatch from './stopwatch.vue' import SettingsStopwatch from './stopwatch.vue'
import SettingsItemSearch from '../item-search/settings-item-search.vue' import SettingsItemSearch from '../item-search/settings-item-search.vue'
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
function shuffle<T>(array: T[]): T[] { function shuffle<T>(array: T[]): T[] {
let currentIndex = array.length let currentIndex = array.length
@@ -87,7 +88,7 @@ function quit () {
} }
export default defineComponent({ export default defineComponent({
components: { AppTitleBar }, components: { AppTitleBar, ConversionWarningBanner },
props: { props: {
config: { config: {
type: Object as PropType<Widget>, type: Object as PropType<Widget>,
@@ -218,13 +219,17 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
<style lang="postcss" module> <style lang="postcss" module>
.window { .window {
position: absolute; position: absolute;
top: 0; bottom: 0; left: 0; right: 0; top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto; margin: 0 auto;
max-width: 50rem; max-width: 50rem;
max-height: 38rem; max-height: 38rem;
overflow: hidden; overflow: hidden;
@apply bg-gray-800; @apply bg-gray-800;
@apply rounded-b; @apply rounded-b;
&:global { &:global {
animation-name: slideInDown; animation-name: slideInDown;
animation-duration: 1s; animation-duration: 1s;
@@ -262,10 +267,13 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
.patronsHorizontal { .patronsHorizontal {
@apply bg-gray-900 p-1 rounded gap-1; @apply bg-gray-900 p-1 rounded gap-1;
position: absolute; position: absolute;
top: 40rem; left: 0; right: 0; top: 40rem;
left: 0;
right: 0;
margin: 0 auto; margin: 0 auto;
max-width: 50rem; max-width: 50rem;
display: flex; display: flex;
&:global { &:global {
animation-name: slideInDown; animation-name: slideInDown;
animation-duration: 1s; animation-duration: 1s;
@@ -273,10 +281,19 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
} }
@keyframes slide { @keyframes slide {
0% { transform: translate(0%, 0); } 0% {
4% { transform: translate(0%, 0); } transform: translate(0%, 0);
100% { transform: translate(-99%, 0); }
} }
4% {
transform: translate(0%, 0);
}
100% {
transform: translate(-99%, 0);
}
}
.patronsLine { .patronsLine {
display: inline-block; display: inline-block;
animation: slide 64s linear infinite; animation: slide 64s linear infinite;
@@ -291,22 +308,40 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
@apply gap-4 p-4; @apply gap-4 p-4;
&:global { &:global {
animation-name: fadeIn; animation-name: fadeIn;
animation-duration: 1.5s; animation-duration: 1.5s;
} }
} }
.podium>div { .podium>div {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
min-width: min-content; min-width: min-content;
} }
.podium > div:nth-child(1) { max-width: 18rem; }
.podium > div:nth-child(2) { max-width: 16rem; } .podium>div:nth-child(1) {
.podium > div:nth-child(3) { flex-direction: column; align-items: center; } max-width: 18rem;
.podium > div:nth-child(4) { max-width: 24rem; } }
.podium > div:nth-child(5) { max-width: 18rem; }
.podium>div:nth-child(2) {
max-width: 16rem;
}
.podium>div:nth-child(3) {
flex-direction: column;
align-items: center;
}
.podium>div:nth-child(4) {
max-width: 24rem;
}
.podium>div:nth-child(5) {
max-width: 18rem;
}
.rating { .rating {
min-width: 3rem; min-width: 3rem;
@@ -314,30 +349,35 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
white-space: nowrap; white-space: nowrap;
@apply px-1 border; @apply px-1 border;
} }
.rating-1 { .rating-1 {
background-color: rgb(0, 0, 0); background-color: rgb(0, 0, 0);
color: rgb(190, 178, 135); color: rgb(190, 178, 135);
border-color: currentColor; border-color: currentColor;
@apply text-base; @apply text-base;
} }
.rating-2 { .rating-2 {
background-color: rgb(210, 178, 135); background-color: rgb(210, 178, 135);
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
border-color: currentColor; border-color: currentColor;
@apply text-lg; @apply text-lg;
} }
.rating-3 { .rating-3 {
background-color: rgb(213, 159, 0); background-color: rgb(213, 159, 0);
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
border-color: currentColor; border-color: currentColor;
@apply text-lg; @apply text-lg;
} }
.rating-4 { .rating-4 {
background-color: rgb(240, 90, 35); background-color: rgb(240, 90, 35);
color: rgb(255, 255, 255); color: rgb(255, 255, 255);
border-color: currentColor; border-color: currentColor;
@apply text-xl; @apply text-xl;
} }
.rating-5 { .rating-5 {
background-color: rgb(255, 255, 255); background-color: rgb(255, 255, 255);
color: rgb(255, 0, 0); color: rgb(255, 0, 0);

View File

@@ -2,24 +2,28 @@
<div class="p-2 flex flex-col h-full items-center"> <div class="p-2 flex flex-col h-full items-center">
<div class="flex flex-col items-center p-2 mb-4"> <div class="flex flex-col items-center p-2 mb-4">
<img class="w-12 h-12" src="/images/TransferOrb.png"> <img class="w-12 h-12" src="/images/TransferOrb.png">
<p class="text-base">Awakened PoE Trade</p> <p class="text-base">Awakened PoE2 Trade2</p>
<p class="">{{ t('app.version', [version]) }}</p> <p class="">{{ t('app.version', [version]) }}</p>
<div class="flex gap-2"> <div class="flex gap-2">
<a class="border-b" href="https://github.com/SnosMe/awakened-poe-trade/releases" target="_blank">{{ t('app.release_notes') }}</a> <a class="border-b" href="https://github.com/Kvan7/awakened-poe2-trade2/releases" target="_blank">{{
<a class="border-b" href="https://github.com/SnosMe/awakened-poe-trade/issues" target="_blank">{{ t('app.report_bug') }}</a> t('app.release_notes') }}</a>
<a class="border-b" href="https://github.com/Kvan7/awakened-poe2-trade2/issues" target="_blank">{{
t('app.report_bug') }}</a>
</div> </div>
</div> </div>
<div class="border border-gray-600 rounded p-2 whitespace-nowrap min-w-min w-72"> <div class="border border-gray-600 rounded p-2 whitespace-nowrap min-w-min w-72">
<p>{{ info.str1 }}</p> <p>{{ info.str1 }}</p>
<p>{{ info.str2 }}</p> <p>{{ info.str2 }}</p>
<button v-if="info.action" @click="info.action" <button v-if="info.action" @click="info.action" class="btn w-full mt-1">{{ info.actionText }}</button>
class="btn w-full mt-1">{{ info.actionText }}</button>
</div> </div>
<div class="text-center mt-auto py-8"> <div class="text-center mt-auto py-8">
<p>{{ t('app.contact_me') }} <br><span class="font-sans text-gray-500 select-all">&lt;@295216259795124225&gt;</span></p> <p>{{ t('app.contact_me') }} <br><span
class="font-sans text-gray-500 select-all">&lt;@295216259795124225&gt;</span></p>
<ul class="flex gap-4"> <ul class="flex gap-4">
<li><img class="rounded inline" src="/images/dc_tft.gif"> <a class="border-b" href="https://discord.gg/tftrove" target="_blank">The Forbidden Trove</a></li> <li><img class="rounded inline" src="/images/dc_tft.gif"> <a class="border-b" href="https://discord.gg/tftrove"
<li><img class="rounded inline" src="/images/dc_reddit.png"> <a class="border-b" href="https://discord.gg/pathofexile" target="_blank">r/pathofexile</a></li> target="_blank">The Forbidden Trove</a></li>
<li><img class="rounded inline" src="/images/dc_reddit.png"> <a class="border-b"
href="https://discord.gg/pathofexile" target="_blank">r/pathofexile</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -28,12 +28,12 @@
<div class="mb-2"> <div class="mb-2">
<div class="flex-1 mb-1">{{ t(':poe_log_file') }}</div> <div class="flex-1 mb-1">{{ t(':poe_log_file') }}</div>
<input v-model.trim="clientLog" <input v-model.trim="clientLog"
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/Grinding Gear Games/Path of Exile/logs/Client.txt"> class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/Grinding Gear Games/Path of Exile 2/logs/Client.txt">
</div> </div>
<div class="mb-4"> <div class="mb-4">
<div class="flex-1 mb-1">{{ t(':poe_cfg_file') }}</div> <div class="flex-1 mb-1">{{ t(':poe_cfg_file') }}</div>
<input v-model.trim="gameConfig" <input v-model.trim="gameConfig"
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/My Games/Path of Exile/production_Config.ini"> class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/My Games/Path of Exile 2/production_Config.ini">
</div> </div>
<hr class="mb-4 mx-8 border-gray-700"> <hr class="mb-4 mx-8 border-gray-700">
<div class="mb-2"> <div class="mb-2">

3267
renderer/yarn.lock Normal file

File diff suppressed because it is too large Load Diff