mirror of
https://github.com/Kvan7/Exiled-Exchange-2.git
synced 2026-01-17 20:30:21 +00:00
Compare commits
7 Commits
dev
...
features/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df966c8041 | ||
|
|
c0fa40bb74 | ||
|
|
f5615ef391 | ||
|
|
3af6d48934 | ||
|
|
2d342bd86a | ||
|
|
7b0faf9f3f | ||
|
|
3fc3e13ac5 |
18
ipc/types.ts
18
ipc/types.ts
@@ -9,6 +9,7 @@ export interface HostConfig {
|
||||
windowTitle: string;
|
||||
language: string;
|
||||
readClientLog: boolean;
|
||||
libraryAlpha: boolean;
|
||||
}
|
||||
|
||||
export interface ShortcutAction {
|
||||
@@ -88,7 +89,8 @@ export type IpcEvent =
|
||||
| IpcItemText
|
||||
| IpcOcrText
|
||||
| IpcConfigChanged
|
||||
| IpcUserAction;
|
||||
| IpcUserAction
|
||||
| IpcWriteCsv;
|
||||
|
||||
export type IpcEventPayload<
|
||||
Name extends IpcEvent["name"],
|
||||
@@ -209,6 +211,20 @@ type IpcUserAction = Event<
|
||||
}
|
||||
>;
|
||||
|
||||
type IpcWriteCsv = Event<
|
||||
"CLIENT->MAIN::write-data",
|
||||
| {
|
||||
action: "log-item";
|
||||
text: string;
|
||||
}
|
||||
| {
|
||||
action: "session";
|
||||
start: boolean;
|
||||
name?: string;
|
||||
header?: string;
|
||||
}
|
||||
>;
|
||||
|
||||
interface Event<TName extends string, TPayload = undefined> {
|
||||
name: TName;
|
||||
payload: TPayload;
|
||||
|
||||
79
main/src/host-files/FileWriter.ts
Normal file
79
main/src/host-files/FileWriter.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import path from "path";
|
||||
import { app } from "electron";
|
||||
import { ServerEvents } from "../server";
|
||||
import { promises as fs, existsSync } from "fs";
|
||||
import { Logger } from "../RemoteLogger";
|
||||
|
||||
export class FileWriter {
|
||||
private uploadsPath = path.join(
|
||||
app.getPath("userData"),
|
||||
"apt-data",
|
||||
"csv-data",
|
||||
);
|
||||
|
||||
private _state: {
|
||||
file: fs.FileHandle;
|
||||
} | null = null;
|
||||
|
||||
private _enabled = false;
|
||||
|
||||
constructor(
|
||||
private server: ServerEvents,
|
||||
private logger: Logger,
|
||||
) {
|
||||
this.server.onEventAnyClient("CLIENT->MAIN::write-data", async (e) => {
|
||||
if (!this._enabled) return;
|
||||
if (e.action !== "log-item" && e.action !== "session") return;
|
||||
if (e.action === "log-item") {
|
||||
this.writeLine(e.text);
|
||||
return;
|
||||
}
|
||||
// e.action === "session"
|
||||
if (e.start) {
|
||||
if (!e.name || !e.header) {
|
||||
this.logger.write("error [FileWriter] Invalid session start event.");
|
||||
return;
|
||||
}
|
||||
|
||||
await this.writeSessionStart(e.name, e.header);
|
||||
} else {
|
||||
this.writeSessionEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async restart(enabled: boolean) {
|
||||
this._enabled = enabled;
|
||||
}
|
||||
|
||||
private async writeSessionStart(name: string, header: string) {
|
||||
try {
|
||||
if (!existsSync(this.uploadsPath)) {
|
||||
await fs.mkdir(this.uploadsPath, { recursive: true });
|
||||
}
|
||||
|
||||
const file = await fs.open(
|
||||
path.join(this.uploadsPath, name + ".csv"),
|
||||
"w",
|
||||
);
|
||||
this._state = { file };
|
||||
|
||||
this.writeLine(header);
|
||||
} catch {
|
||||
this.logger.write("error [FileWriter] Failed to create session file.");
|
||||
}
|
||||
}
|
||||
|
||||
private writeSessionEnd() {
|
||||
if (!this._state) return;
|
||||
|
||||
this._state.file.close();
|
||||
this._state = null;
|
||||
}
|
||||
|
||||
private writeLine(line: string) {
|
||||
if (!this._state) return;
|
||||
|
||||
this._state.file.write(line + "\n");
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import { OverlayVisibility } from "./windowing/OverlayVisibility";
|
||||
import { GameLogWatcher } from "./host-files/GameLogWatcher";
|
||||
import { HttpProxy } from "./proxy";
|
||||
import { installExtension, VUEJS_DEVTOOLS } from "electron-devtools-installer";
|
||||
import { FileWriter } from "./host-files/FileWriter";
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.exit();
|
||||
@@ -70,6 +71,7 @@ let tray: AppTray;
|
||||
const appUpdater = new AppUpdater(eventPipe);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const _httpProxy = new HttpProxy(server, logger);
|
||||
const fileWriter = new FileWriter(eventPipe, logger);
|
||||
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
try {
|
||||
@@ -114,6 +116,7 @@ let tray: AppTray;
|
||||
gameConfig.readConfig(cfg.gameConfig ?? "");
|
||||
appUpdater.checkAtStartup();
|
||||
tray.overlayKey = cfg.overlayKey;
|
||||
fileWriter.restart(cfg.libraryAlpha);
|
||||
},
|
||||
);
|
||||
uIOhook.start();
|
||||
|
||||
@@ -330,8 +330,6 @@
|
||||
"show_overlay_ready": "Benachrichtigung anzeigen, wenn Overlay ein PoE-Fenster erkennt",
|
||||
"debug_hotkeys": "Alle Tastenanschläge aufzeichnen",
|
||||
"preferred_trade_site": "Bevorzugte Handelsseite",
|
||||
"enable_alphas": "Alphas aktivieren",
|
||||
"alphas_warning": "Alphas sind extrem experimentell und werden wahrscheinlich nicht wie erwartet funktionieren. Bitte melden Sie alle auftretenden Probleme.",
|
||||
"help": "Hilfe",
|
||||
"show_tips_on_startup": "Tipps bei Startbenachrichtigung anzeigen",
|
||||
"show_tips_frequency": "Wie oft sollen Tipps beim Preis-Check angezeigt werden?",
|
||||
|
||||
@@ -362,8 +362,8 @@
|
||||
"show_overlay_ready": "Show a notification when the Overlay detects a PoE window",
|
||||
"debug_hotkeys": "Record all key presses",
|
||||
"preferred_trade_site": "Preferred trade site",
|
||||
"enable_alphas": "Enable alphas",
|
||||
"alphas_warning": "Alphas are extremely experimental and will probably not work as expected. Please report any issues you encounter.",
|
||||
"enable_alphas": "Enable experimental features",
|
||||
"alpha_library": "Item Data Collection",
|
||||
"help": "Help",
|
||||
"show_tips_on_startup": "Show tips on startup notification",
|
||||
"show_tips_frequency": "How often to show tips when price checking",
|
||||
@@ -467,5 +467,12 @@
|
||||
"exp_tracking_help": "XP tracking widget shows your current level, current experience multiplier, and how close you are to being under-leveled. XP penalty is applied immediately if you are over-leveled, and when you are under-leveled by \u230A level/16 + 3 \u230B. The 'Over' is how many levels the your current level is over the bottom of the 'safe zone', generally 0 or 1 means you are killing the highest level monsters you can without incurring the under-leveled penalty",
|
||||
"exp_inspire": "Based on ",
|
||||
"exp_astrict": "*Experience gain percentages, when over level 95, or monster level over 70, or potentially when not at 100% exp gain, may not be fully tested and accurate."
|
||||
},
|
||||
"library": {
|
||||
"name": "Library",
|
||||
"log_item_key": "Log item to csv",
|
||||
"output_file": "Output csv path",
|
||||
"output_file_help": "Some info text",
|
||||
"record_count": "Session Rolls:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,9 +328,7 @@
|
||||
"restore_clipboard": "Restaurar portapapeles",
|
||||
"show_overlay_ready": "Mostrar una notificación cuando la Superposición detecte una ventana de PoE",
|
||||
"debug_hotkeys": "Registrar todas las teclas presionadas",
|
||||
"preferred_trade_site": "Sitio de comercio preferido",
|
||||
"enable_alphas": "Habilitar alfas",
|
||||
"alphas_warning": "Las alfas son extremadamente experimentales y probablemente no funcionarán como se espera. Por favor, reporta cualquier problema que encuentres."
|
||||
"preferred_trade_site": "Sitio de comercio preferido"
|
||||
},
|
||||
"price_check": {
|
||||
"name": "Comprobación de precios",
|
||||
|
||||
@@ -350,8 +350,6 @@
|
||||
"show_overlay_ready": "Mostrar uma notificação quando a Sobreposição detectar uma janela do PoE",
|
||||
"debug_hotkeys": "Registrar todas as teclas pressionadas",
|
||||
"preferred_trade_site": "Site de comércio preferido",
|
||||
"enable_alphas": "Habilitar versões alfas",
|
||||
"alphas_warning": "As versões alfas são extremamente experimentais e provavelmente não funcionarão como esperado. Por favor, reporte qualquer problema que encontrar.",
|
||||
"help": "Ajuda",
|
||||
"show_tips_on_startup": "Mostrar dicas na notificação de inicialização",
|
||||
"show_tips_frequency": "Frequência das dicas ao checar preço",
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { StashSearchWidget } from "./stash-search/widget";
|
||||
import type { ItemCheckWidget } from "./item-check/widget";
|
||||
import type { ItemSearchWidget } from "./item-search/widget";
|
||||
import { registry as widgetRegistry } from "./overlay/widget-registry.js";
|
||||
import { LibraryWidget } from "./library/widget";
|
||||
|
||||
const _config = shallowRef<Config | null>(null);
|
||||
let _lastSavedConfig: Config | null = null;
|
||||
@@ -147,7 +148,7 @@ export interface Config {
|
||||
showAttachNotification: boolean;
|
||||
overlayAlwaysClose: boolean;
|
||||
enableAlphas: boolean;
|
||||
alphas: [];
|
||||
alphas: Array<"library">;
|
||||
tipsFrequency: TipsFrequency;
|
||||
readClientLog: boolean; // default to false, opt-in only
|
||||
}
|
||||
@@ -620,6 +621,21 @@ function upgradeConfig(_config: Config): Config {
|
||||
|
||||
config.configVersion = 29;
|
||||
}
|
||||
|
||||
if (config.configVersion < 30) {
|
||||
// NOTE: v0.13.11 || poe0.4.0d
|
||||
const itemSearchId: number = config.widgets.find(
|
||||
(w) => w.wmType === "item-search",
|
||||
)!.wmId;
|
||||
// splicing to insert after the item-search widget, for positioning on the main overlay
|
||||
config.widgets.splice(itemSearchId, 0, {
|
||||
...defaultConfig().widgets.find((w) => w.wmType === "library")!,
|
||||
wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
|
||||
});
|
||||
|
||||
config.configVersion = 30;
|
||||
}
|
||||
|
||||
return config as unknown as Config;
|
||||
}
|
||||
|
||||
@@ -682,6 +698,15 @@ function getConfigForHost(): HostConfig {
|
||||
action: { type: "copy-item", target: "item-check", focusOverlay: true },
|
||||
});
|
||||
}
|
||||
const library = AppConfig("library") as LibraryWidget;
|
||||
if (library.logItemKey) {
|
||||
actions.push({
|
||||
shortcut: library.logItemKey,
|
||||
keepModKeys: true,
|
||||
action: { type: "copy-item", target: "log-item" },
|
||||
});
|
||||
}
|
||||
|
||||
const delveGrid = AppConfig("delve-grid") as widget.DelveGridWidget;
|
||||
if (delveGrid.toggleKey) {
|
||||
actions.push({
|
||||
@@ -760,5 +785,6 @@ function getConfigForHost(): HostConfig {
|
||||
windowTitle: config.windowTitle,
|
||||
language: config.language,
|
||||
readClientLog: config.readClientLog,
|
||||
libraryAlpha: config.enableAlphas && config.alphas.includes("library"),
|
||||
};
|
||||
}
|
||||
|
||||
251
renderer/src/web/library/WidgetLibrary.vue
Normal file
251
renderer/src/web/library/WidgetLibrary.vue
Normal file
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<Widget
|
||||
:config="config"
|
||||
:removable="false"
|
||||
:inline-edit="false"
|
||||
move-handles="top-bottom"
|
||||
>
|
||||
<div
|
||||
class="widget-default-style p-1 flex flex-col overflow-y-auto min-h-0 min-w-48"
|
||||
>
|
||||
<div class="text-gray-100 p-1 flex items-center justify-between gap-4">
|
||||
<div
|
||||
v-if="inSession"
|
||||
class="text-gray-100 m-1 leading-4 w-full text-center p-1"
|
||||
>
|
||||
{{ sessionName }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="leading-4 rounded text-gray-100 p-1 bg-gray-700 w-full mb-1"
|
||||
v-model="sessionName"
|
||||
/>
|
||||
<button v-if="!inSession" @click="startSession" :class="$style.button">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button v-else @click="endSession" :class="$style.button">
|
||||
<i class="fas fa-stop"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-1 overflow-y-auto min-h-0">
|
||||
<div :class="$style.dataField">
|
||||
<div>{{ t(":record_count") }}</div>
|
||||
<div
|
||||
class="text-center bg-transparent text-gray-300 border-2 rounded border-gray-900 p-1"
|
||||
>
|
||||
{{ rollCount }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.dataField">
|
||||
<div>{{ lastMod }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Widget>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
PropType,
|
||||
ref,
|
||||
shallowRef,
|
||||
watch,
|
||||
} from "vue";
|
||||
|
||||
import Widget from "../overlay/Widget.vue";
|
||||
import { WidgetSpec } from "../overlay/interfaces";
|
||||
import { LibraryWidget } from "./widget";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Host, MainProcess } from "../background/IPC";
|
||||
import { parseClipboard, ParsedItem } from "@/parser";
|
||||
import { AppConfig } from "../Config";
|
||||
import { err, ok, Result } from "neverthrow";
|
||||
|
||||
function startSessionHost(name: string, header: string) {
|
||||
Host.sendEvent({
|
||||
name: "CLIENT->MAIN::write-data",
|
||||
payload: {
|
||||
action: "session",
|
||||
start: true,
|
||||
name,
|
||||
header,
|
||||
},
|
||||
});
|
||||
}
|
||||
function endSessionHost() {
|
||||
Host.sendEvent({
|
||||
name: "CLIENT->MAIN::write-data",
|
||||
payload: {
|
||||
action: "session",
|
||||
start: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function buildCsvString(
|
||||
item: ParsedItem,
|
||||
sessionType: "chaos",
|
||||
): Result<string, string> {
|
||||
if (sessionType === "chaos") {
|
||||
return ok(
|
||||
[
|
||||
item.info.refName,
|
||||
item.itemLevel,
|
||||
`"${JSON.stringify(item.newMods.map((mod) => mod.info.name))}"`,
|
||||
`"${JSON.stringify(item.newMods.map((mod) => mod.info.tier))}"`,
|
||||
].join(","),
|
||||
);
|
||||
}
|
||||
return err("sessionType not supported");
|
||||
}
|
||||
const headers = {
|
||||
chaos: "base,ilvl,mod,tier",
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
widget: {
|
||||
type: "library",
|
||||
instances: "single",
|
||||
initInstance: (): LibraryWidget => {
|
||||
return {
|
||||
wmId: 0,
|
||||
wmType: "library",
|
||||
wmTitle: "{icon=fa-book}",
|
||||
wmWants: "hide",
|
||||
wmZorder: null,
|
||||
wmFlags: ["invisible-on-blur", "menu::skip"],
|
||||
anchor: {
|
||||
pos: "tl",
|
||||
x: 20,
|
||||
y: 20,
|
||||
},
|
||||
logItemKey: null,
|
||||
outputPath: null,
|
||||
};
|
||||
},
|
||||
} satisfies WidgetSpec,
|
||||
components: { Widget },
|
||||
props: {
|
||||
config: {
|
||||
type: Object as PropType<LibraryWidget>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const libEnabled = computed(
|
||||
() => AppConfig().enableAlphas && AppConfig().alphas.includes("library"),
|
||||
);
|
||||
|
||||
const inSession = shallowRef<boolean>(false);
|
||||
|
||||
const sessionName = shallowRef<string>("mySession");
|
||||
const item = ref<ParsedItem | null>(null);
|
||||
const rollCount = shallowRef<number>(0);
|
||||
const sessionType = shallowRef<"chaos">("chaos");
|
||||
|
||||
watch(libEnabled, (curr) => {
|
||||
if (!curr) {
|
||||
endSessionHost();
|
||||
inSession.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
function startSession() {
|
||||
item.value = null;
|
||||
rollCount.value = 0;
|
||||
startSessionHost(sessionName.value, headers[sessionType.value]);
|
||||
inSession.value = true;
|
||||
props.config.wmFlags = props.config.wmFlags.filter(
|
||||
(f) => f !== "invisible-on-blur",
|
||||
);
|
||||
}
|
||||
function endSession() {
|
||||
endSessionHost();
|
||||
inSession.value = false;
|
||||
if (!props.config.wmFlags.includes("invisible-on-blur")) {
|
||||
props.config.wmFlags.push("invisible-on-blur");
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
item,
|
||||
(curr) => {
|
||||
if (!curr) return;
|
||||
|
||||
buildCsvString(curr, sessionType.value)
|
||||
.andThen((text) => {
|
||||
Host.sendEvent({
|
||||
name: "CLIENT->MAIN::write-data",
|
||||
payload: {
|
||||
action: "log-item",
|
||||
text,
|
||||
},
|
||||
});
|
||||
rollCount.value++;
|
||||
return ok(null);
|
||||
})
|
||||
.mapErr((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
MainProcess.onEvent("MAIN->CLIENT::item-text", (e) => {
|
||||
if (e.target !== "log-item") return;
|
||||
if (!libEnabled.value) return;
|
||||
if (!inSession.value) return;
|
||||
|
||||
performance.mark("log-item-event");
|
||||
item.value = parseClipboard(e.clipboard).unwrapOr(null);
|
||||
performance.mark("log-item-parsed");
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
return {
|
||||
t,
|
||||
startSession,
|
||||
endSession,
|
||||
inSession,
|
||||
sessionName,
|
||||
rollCount,
|
||||
lastMod: computed(() => item.value?.newMods.find(() => true)?.info.name),
|
||||
};
|
||||
},
|
||||
beforeUnmount() {
|
||||
endSessionHost();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="postcss" module>
|
||||
.button {
|
||||
@apply bg-gray-800;
|
||||
@apply rounded;
|
||||
line-height: 1;
|
||||
@apply w-8 h-8;
|
||||
@apply max-w-8 max-h-8;
|
||||
@apply min-w-8 min-h-8;
|
||||
|
||||
&:hover {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
.dataField {
|
||||
flex-shrink: 0;
|
||||
@apply rounded;
|
||||
@apply max-w-sm;
|
||||
@apply p-2 leading-4;
|
||||
@apply text-gray-100 bg-gray-800;
|
||||
@apply flex flex-row justify-between;
|
||||
@apply content-center;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
49
renderer/src/web/library/settings-library.vue
Normal file
49
renderer/src/web/library/settings-library.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-4 p-2 max-w-md">
|
||||
<div class="flex mb-4">
|
||||
<label class="flex-1">{{ t(":log_item_key") }}</label>
|
||||
<hotkey-input v-model="logItemKey" class="w-48" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="flex-1 mb-1">{{ t(":poe_log_file") }}</div>
|
||||
<input
|
||||
v-model.trim="outputPath"
|
||||
class="rounded bg-gray-900 px-1 block w-full font-sans"
|
||||
placeholder="...?/Documents/My Games/Path of Exile 2/output.csv"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from "vue";
|
||||
import { useI18nNs } from "@/web/i18n";
|
||||
import { configModelValue, configProp, findWidget } from "../settings/utils.js";
|
||||
import { LibraryWidget } from "./widget.js";
|
||||
import HotkeyInput from "../settings/HotkeyInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "library.name",
|
||||
components: { HotkeyInput },
|
||||
props: configProp(),
|
||||
setup(props) {
|
||||
const configLibraryWidget = computed(
|
||||
() => findWidget<LibraryWidget>("library", props.config)!,
|
||||
);
|
||||
|
||||
const { t } = useI18nNs("library");
|
||||
|
||||
return {
|
||||
t,
|
||||
logItemKey: configModelValue(
|
||||
() => configLibraryWidget.value,
|
||||
"logItemKey",
|
||||
),
|
||||
outputPath: configModelValue(
|
||||
() => configLibraryWidget.value,
|
||||
"outputPath",
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
7
renderer/src/web/library/widget.ts
Normal file
7
renderer/src/web/library/widget.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Anchor, Widget } from "../overlay/widgets";
|
||||
|
||||
export interface LibraryWidget extends Widget {
|
||||
anchor: Anchor;
|
||||
logItemKey: string | null;
|
||||
outputPath: string | null;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import WidgetDelveGrid from "./WidgetDelveGrid.vue";
|
||||
import WidgetItemSearch from "../item-search/WidgetItemSearch.vue";
|
||||
import WidgetSettings from "../settings/SettingsWindow.vue";
|
||||
import WidgetXpTracker from "../leveling/WidgetXpTracker.vue";
|
||||
import WidgetLibrary from "../library/WidgetLibrary.vue";
|
||||
|
||||
type WidgetComponent = Component & { widget: WidgetSpec };
|
||||
|
||||
@@ -33,3 +34,4 @@ registry.widgets.push(PriceCheckWindow as unknown as WidgetComponent);
|
||||
registry.widgets.push(WidgetItemCheck as unknown as WidgetComponent);
|
||||
registry.widgets.push(WidgetImageStrip as unknown as WidgetComponent);
|
||||
registry.widgets.push(WidgetDelveGrid as unknown as WidgetComponent);
|
||||
registry.widgets.push(WidgetLibrary as unknown as WidgetComponent);
|
||||
|
||||
@@ -141,6 +141,7 @@ import SettingsStashSearch from "../stash-search/stash-search-editor.vue";
|
||||
import SettingsStopwatch from "../stopwatch/settings-stopwatch.vue";
|
||||
import SettingsItemSearch from "../item-search/settings-item-search.vue";
|
||||
import SettingsLeveling from "../leveling/settings-leveling.vue";
|
||||
import SettingsLibrary from "../library/settings-library.vue";
|
||||
import { disableWidget, enableWidget, findWidget } from "./utils";
|
||||
|
||||
function shuffle<T>(array: T[]): T[] {
|
||||
@@ -260,6 +261,23 @@ export default defineComponent({
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() =>
|
||||
configClone.value?.enableAlphas &&
|
||||
configClone.value?.alphas.includes("library"),
|
||||
(curr) => {
|
||||
if (curr === undefined) return;
|
||||
const library = findWidget("library", configClone.value!);
|
||||
if (!library) return;
|
||||
|
||||
if (curr) {
|
||||
enableWidget(library);
|
||||
} else {
|
||||
disableWidget(library);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const menuItems = computed(() =>
|
||||
flatJoin(
|
||||
menuByType(configWidget.value?.wmType).map((group) =>
|
||||
@@ -329,6 +347,8 @@ function menuByType(type?: string) {
|
||||
return [[SettingsPricecheck]];
|
||||
case "item-search":
|
||||
return [[SettingsItemSearch]];
|
||||
case "library":
|
||||
return [[SettingsLibrary]];
|
||||
default:
|
||||
return [
|
||||
[SettingsHotkeys, SettingsChat],
|
||||
|
||||
@@ -103,20 +103,19 @@
|
||||
<div class="italic text-gray-500">
|
||||
{{ t(":client_log_explain") }}
|
||||
</div>
|
||||
<div class="mb-4" :class="{ 'p-2 bg-orange-800 rounded': enableAlphas }">
|
||||
<div class="mb-4" :class="{ 'p-2 bg-slate-800 rounded': enableAlphas }">
|
||||
<ui-checkbox class="mb-4" v-model="enableAlphas">{{
|
||||
t(":enable_alphas")
|
||||
}}</ui-checkbox>
|
||||
<div v-if="enableAlphas" class="mt-1">No alphas available right now</div>
|
||||
<div v-if="enableAlphas" class="mt-1">
|
||||
{{ t(":alphas_warning") }}
|
||||
</div>
|
||||
<ui-checkbox v-model="libraryAlpha" v-if="enableAlphas">{{
|
||||
t(":alpha_library")
|
||||
}}</ui-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from "vue";
|
||||
import { defineComponent, computed, ref, watch } from "vue";
|
||||
import { useI18nNs } from "@/web/i18n";
|
||||
import UiRadio from "@/web/ui/UiRadio.vue";
|
||||
import UiCheckbox from "@/web/ui/UiCheckbox.vue";
|
||||
@@ -129,6 +128,22 @@ export default defineComponent({
|
||||
props: configProp(),
|
||||
setup(props) {
|
||||
const { t } = useI18nNs("settings");
|
||||
const libraryAlpha = ref(
|
||||
AppConfig().enableAlphas && AppConfig().alphas.includes("library"),
|
||||
);
|
||||
watch(
|
||||
libraryAlpha,
|
||||
(value) => {
|
||||
if (value) {
|
||||
props.config.alphas.push("library");
|
||||
} else {
|
||||
props.config.alphas = props.config.alphas.filter(
|
||||
(alpha) => alpha !== "library",
|
||||
);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
return {
|
||||
t,
|
||||
@@ -203,6 +218,7 @@ export default defineComponent({
|
||||
windowTitle: configModelValue(() => props.config, "windowTitle"),
|
||||
enableAlphas: configModelValue(() => props.config, "enableAlphas"),
|
||||
readClientLog: configModelValue(() => props.config, "readClientLog"),
|
||||
libraryAlpha,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -149,6 +149,17 @@ module.exports = {
|
||||
800: "#86198f",
|
||||
900: "#701a75",
|
||||
},
|
||||
slate: {
|
||||
100: "#f1f5f9",
|
||||
200: "#e2e8f0",
|
||||
300: "#cbd5e1",
|
||||
400: "#94a3b8",
|
||||
500: "#64748b",
|
||||
600: "#475569",
|
||||
700: "#334155",
|
||||
800: "#1e293b",
|
||||
900: "#0f172a",
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {},
|
||||
|
||||
Reference in New Issue
Block a user