extract default config into widgets

This commit is contained in:
Alexander Drozdov
2025-01-10 09:16:05 +02:00
committed by kvan7
parent f07df3aa10
commit 9904142fb2
13 changed files with 343 additions and 286 deletions

View File

@@ -6,7 +6,7 @@ import type * as widget from "./overlay/widgets";
import type { StashSearchWidget } from "./stash-search/widget";
import type { ItemCheckWidget } from "./item-check/widget";
import type { ItemSearchWidget } from "./item-search/widget";
import type { FilterGeneratorWidget } from "./filter-generator/widget";
import { registry as widgetRegistry } from "./overlay/widget-registry.js";
const _config = shallowRef<Config | null>(null);
let _lastSavedConfig: Config | null = null;
@@ -189,243 +189,25 @@ export const defaultConfig = (): Config => ({
preferredTradeSite: "default",
realm: "pc-ggg",
fontSize: 16,
enableAlphas: false,
alphas: [],
widgets: [
// --- REQUIRED ---
{
wmId: 1,
wmType: "menu",
wmTitle: "",
wmWants: "show",
wmZorder: 1,
wmFlags: ["invisible-on-blur", "skip-menu"],
anchor: {
pos: "tl",
x: 5,
y: 5,
},
alwaysShow: false,
} as widget.WidgetMenu,
{
wmId: 2,
wmType: "price-check",
wmTitle: "",
wmWants: "hide",
wmZorder: "exclusive",
wmFlags: ["hide-on-blur", "skip-menu"],
showRateLimitState: false,
apiLatencySeconds: 2,
collapseListings: "api",
smartInitialSearch: true,
lockedInitialSearch: true,
activateStockFilter: false,
builtinBrowser: false,
usePseudo: false,
hotkey: "D",
hotkeyHold: "Ctrl",
hotkeyLocked: "Ctrl + Alt + D",
showSeller: false,
searchStatRange: 10,
showCursor: true,
requestPricePrediction: false,
rememberCurrency: false,
defaultAllSelected: false,
itemHoverTooltip: "keybind",
autoFillEmptyRuneSockets: "Iron Rune",
} as widget.PriceCheckWidget,
{
wmId: 3,
wmType: "item-check",
wmTitle: "",
wmWants: "hide",
wmZorder: "exclusive",
wmFlags: ["hide-on-blur", "skip-menu"],
hotkey: null,
wikiKey: null,
poedbKey: null,
craftOfExileKey: null,
stashSearchKey: null,
maps: {
profile: 1,
showNewStats: false,
selectedStats: [
{
matcher: "#% maximum Player Resistances",
decision: "w--",
},
{
matcher: "Monsters reflect #% of Physical Damage",
decision: "d--",
},
{
matcher: "Monsters reflect #% of Elemental Damage",
decision: "d--",
},
{
matcher: "Area contains two Unique Bosses",
decision: "g--",
},
],
},
} as ItemCheckWidget,
{
wmId: 4,
wmType: "delve-grid",
wmTitle: "",
wmWants: "hide",
wmZorder: 4,
wmFlags: ["hide-on-focus", "skip-menu"],
toggleKey: null,
} as widget.DelveGridWidget,
{
wmId: 5,
wmType: "settings",
wmTitle: "",
wmWants: "hide",
wmZorder: "exclusive",
wmFlags: ["invisible-on-blur", "ignore-ui-visibility"],
widgets: widgetRegistry.widgets.reduce<widget.Widget[]>(
(widgets, { widget }) => {
const res: widget.Widget[] = [];
if (widget.instances === "single") {
res.push(widget.initInstance!());
} else if (
widget.instances === "multi" &&
widget.defaultInstances != null
) {
res.push(...widget.defaultInstances());
}
for (const config of res) {
config.wmId = widgets.length + 1;
widgets.push(config);
}
return widgets;
},
{
wmId: 6,
wmType: "item-search",
wmTitle: "",
wmWants: "hide",
wmZorder: 6,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tl",
x: 10,
y: 20,
},
} as ItemSearchWidget,
// --- DEFAULT ---
{
wmId: 101,
wmType: "stash-search",
wmTitle: "Map rolling",
wmWants: "hide",
wmZorder: 101,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tl",
x: 35,
y: 46,
},
entries: [
{ id: 1, name: "", text: '"Pack Size: +3"', hotkey: null },
{ id: 2, name: "", text: "Reflect", hotkey: null },
{ id: 3, name: "", text: '"Cannot Leech Life"', hotkey: null },
{ id: 4, name: "", text: '"Cannot Leech Mana"', hotkey: null },
],
} as StashSearchWidget,
{
wmId: 102,
wmType: "stash-search",
wmTitle: "Dump sorting",
wmWants: "hide",
wmZorder: 102,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tl",
x: 34,
y: 56,
},
entries: [
{ id: 1, name: "", text: "Currency", hotkey: null },
{ id: 2, name: "", text: '"Divination Card"', hotkey: null },
{ id: 3, name: "", text: "Fossil", 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 },
],
} as StashSearchWidget,
{
wmId: 103,
wmType: "image-strip",
wmTitle: "Cheat sheets",
wmWants: "hide",
wmZorder: 103,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tc",
x: 50,
y: 10,
},
images: [{ id: 1, url: "syndicate.jpg" }],
} as widget.ImageStripWidget,
{
wmId: 104,
wmType: "filter-generator",
wmTitle: "Filter generator",
wmWants: "hide",
wmZorder: 104,
wmFlags: ["invisible-on-blur"],
filtersFolder: "",
selectedFilterFile: "",
filterStrategy: "before",
anchor: {
pos: "tl",
x: 34,
y: 56,
},
entries: [
{
id: 1,
name: "Scroll of Wisdom",
identifiers: [{ key: "BaseType", value: "Scroll of Wisdom" }],
action: "hide",
},
{
id: 2,
name: "Flasks",
identifiers: [{ key: "BaseType", value: "Life Flask,Mana Flask" }],
action: "hide",
},
{
id: 3,
name: "People get those for Headhunter unique",
identifiers: [
{ key: "Class", value: "Belts" },
{ key: "BaseType", value: "Heavy Belt" },
{ key: "Rarity", value: "Normal" },
],
action: "interesting",
},
{
id: 4,
name: "People get those for Astramentis unique",
identifiers: [
{ key: "Class", value: "Amulets" },
{ key: "BaseType", value: "Stellar Amulet" },
{ key: "Rarity", value: "Normal" },
],
action: "interesting",
},
{
id: 5,
name: "Low level area items",
identifiers: [
{
key: "Class",
value:
"Body Armours,Helmets,Boots,Gloves,Shields,Foci,One Hand Maces,Two Hand Maces,Quarterstaves,Bows,Crossbows",
},
{ key: "AreaLevel", value: "< 65" },
{ key: "Rarity", value: "Normal,Magic,Rare" },
{ key: "Quality", value: "= 0" },
{ key: "Sockets", value: "= 0" },
],
action: "hide",
},
],
} as FilterGeneratorWidget,
],
[],
),
});
function upgradeConfig(_config: Config): Config {

View File

@@ -12,13 +12,55 @@
</Widget>
</template>
<script lang="ts">
import type { WidgetSpec } from "../overlay/interfaces";
import type { ItemCheckWidget } from "./widget.js";
export default {
widget: {
type: "item-check",
instances: "single",
initInstance: (): ItemCheckWidget => {
return {
wmId: 0,
wmType: "item-check",
wmTitle: "",
wmWants: "hide",
wmZorder: "exclusive",
wmFlags: ["hide-on-blur", "skip-menu"],
hotkey: null,
wikiKey: null,
poedbKey: null,
craftOfExileKey: null,
stashSearchKey: null,
maps: {
profile: 1,
showNewStats: false,
selectedStats: [
{ matcher: "#% maximum Player Resistances", decision: "w--" },
{
matcher: "Monsters reflect #% of Physical Damage",
decision: "d--",
},
{
matcher: "Monsters reflect #% of Elemental Damage",
decision: "d--",
},
{ matcher: "Area contains two Unique Bosses", decision: "g--" },
],
},
};
},
} satisfies WidgetSpec,
};
</script>
<script setup lang="ts">
import { computed, inject, ref } from "vue";
import { MainProcess } from "@/web/background/IPC";
import { ItemCategory, ItemRarity, parseClipboard, ParsedItem } from "@/parser";
import { registerActions } from "./hotkeyable-actions";
import type { WidgetManager } from "../overlay/interfaces";
import type { ItemCheckWidget } from "./widget.js";
import Widget from "../overlay/Widget.vue";
import MapCheck from "../map-check/MapCheck.vue";

View File

@@ -129,6 +129,31 @@ import {
} from "@/assets/data";
import { AppConfig } from "@/web/Config";
import { CurrencyValue } from "@/web/background/Prices";
import type { WidgetSpec } from "../overlay/interfaces";
import { ItemSearchWidget } from "./widget.js";
export default {
widget: {
type: "item-search",
instances: "single",
initInstance: (): ItemSearchWidget => {
return {
wmId: 0,
wmType: "item-search",
wmTitle: "",
wmWants: "hide",
wmZorder: null,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tl",
x: 10,
y: 20,
},
ocrGemsKey: null,
};
},
} satisfies WidgetSpec,
};
interface SelectedItem {
info: BaseType;
@@ -231,7 +256,6 @@ function fuzzyFindHeistGem(badStr: string) {
import { shallowRef, computed, nextTick, inject } from "vue";
import { useI18nNs } from "@/web/i18n";
import { WidgetManager } from "../overlay/interfaces";
import { ItemSearchWidget } from "./widget.js";
import { usePoeninja } from "@/web/background/Prices";
import { Host } from "@/web/background/IPC";
import { createVirtualItem, ItemRarity } from "@/parser/ParsedItem";

View File

@@ -15,7 +15,7 @@
v-show="isVisible(widget.wmId)"
:config="widget"
:id="`widget-${widget.wmId}`"
:is="`widget-${widget.wmType}`"
:is="registry.getWidgetComponent(widget.wmType)"
/>
</template>
<pre
@@ -67,18 +67,9 @@ import {
import { useI18n } from "vue-i18n";
import { Host } from "@/web/background/IPC";
import { Widget, WidgetManager } from "./interfaces";
import WidgetTimer from "./WidgetTimer.vue";
import WidgetStashSearch from "../stash-search/WidgetStashSearch.vue";
import WidgetMenu from "./WidgetMenu.vue";
import PriceCheckWindow from "@/web/price-check/PriceCheckWindow.vue";
import WidgetItemCheck from "@/web/item-check/WidgetItemCheck.vue";
import WidgetImageStrip from "./WidgetImageStrip.vue";
import WidgetDelveGrid from "./WidgetDelveGrid.vue";
import WidgetItemSearch from "../item-search/WidgetItemSearch.vue";
import WidgetSettings from "../settings/SettingsWindow.vue";
import { registry } from "./widget-registry.js";
import { AppConfig, saveConfig, pushHostConfig } from "@/web/Config";
import LoadingAnimation from "./LoadingAnimation.vue";
import WidgetFilterGenerator from "../filter-generator/WidgetFilterGenerator.vue";
// ---
import { usePoeninja } from "@/web/background/Prices";
import { useLeagues } from "@/web/background/Leagues";
@@ -88,17 +79,7 @@ type WMID = Widget["wmId"];
export default defineComponent({
components: {
WidgetTimer,
WidgetStashSearch,
WidgetMenu,
WidgetPriceCheck: PriceCheckWindow,
WidgetItemCheck,
WidgetImageStrip,
WidgetDelveGrid,
WidgetItemSearch,
WidgetSettings,
LoadingAnimation,
WidgetFilterGenerator,
},
setup() {
usePoeninja();
@@ -387,6 +368,7 @@ export default defineComponent({
showEditingNotification: computed(
() => !active.value && showEditingNotification.value,
),
registry,
};
},
});

View File

@@ -28,6 +28,29 @@
</Widget>
</template>
<script lang="ts">
import type { WidgetSpec } from '../overlay/interfaces'
import { DelveGridWidget } from './interfaces'
export default {
widget: {
type: 'delve-grid',
instances: 'single',
initInstance: (): DelveGridWidget => {
return {
wmId: 0,
wmType: 'delve-grid',
wmTitle: '',
wmWants: 'hide',
wmZorder: null,
wmFlags: ['hide-on-focus', 'skip-menu'],
toggleKey: null
}
}
} satisfies WidgetSpec
}
</script>
<script setup lang="ts">
import { computed, inject } from "vue";
import { Host } from "@/web/background/IPC";

View File

@@ -70,11 +70,41 @@
</Widget>
</template>
<script lang="ts">
import type { WidgetSpec, ImageStripWidget } from "../overlay/interfaces";
export default {
widget: {
type: "image-strip",
instances: "multi",
trNameKey: "image_strip.name",
defaultInstances: (): ImageStripWidget[] => {
return [
{
wmId: 0,
wmType: "image-strip",
wmTitle: "Cheat sheets",
wmWants: "hide",
wmZorder: null,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tc",
x: 50,
y: 10,
},
images: [{ id: 1, url: "syndicate.jpg" }],
},
];
},
} satisfies WidgetSpec,
};
</script>
<script setup lang="ts">
import { inject, nextTick } from "vue";
import { useI18n } from "vue-i18n";
import { Host } from "@/web/background/IPC";
import { WidgetManager, ImageStripWidget } from "./interfaces";
import { WidgetManager } from "./interfaces";
import DndContainer from "vuedraggable";
import Widget from "./Widget.vue";

View File

@@ -38,33 +38,19 @@
</template>
<template #content>
<div class="flex flex-col justify-center text-base">
<!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap">Chromatic calculator</button> -->
<!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap">Screen saver</button> -->
<!-- add widget -->
<div
class="text-gray-600 text-sm px-1 select-none whitespace-nowrap"
>
{{ t(":add") }}
</div>
<button
v-for="spec in instantiableWidgets"
:key="spec.type"
class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
@click="createOfType('timer')"
@click="createOfType(spec.type)"
>
{{ t("stopwatch.name") }}
{{ t(spec.trNameKey ?? spec.type) }}
</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> -->
</div>
</template>
</ui-popover>
@@ -91,14 +77,39 @@
import { defineComponent, PropType, computed, inject } from "vue";
import UiToggle from "@/web/ui/UiToggle.vue";
import UiPopover from "@/web/ui/Popover.vue";
import { Widget as IWidget, WidgetManager, WidgetMenu } from "./interfaces";
import {
Widget as IWidget,
WidgetManager,
WidgetMenu,
WidgetSpec,
} from "./interfaces";
import { registry } from "./widget-registry";
import { Host } from "@/web/background/IPC";
import Widget from "./Widget.vue";
import { useI18nNs } from "@/web/i18n";
import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue";
export default defineComponent({
components: { Widget, UiToggle, UiPopover, ConversionWarningBanner },
widget: {
type: "menu",
instances: "single",
initInstance: (): WidgetMenu => {
return {
wmId: 0,
wmType: "menu",
wmTitle: "",
wmWants: "show",
wmZorder: 1,
wmFlags: ["invisible-on-blur", "skip-menu"],
anchor: {
pos: "tl",
x: 5,
y: 5,
},
alwaysShow: false,
};
},
} satisfies WidgetSpec,
components: { Widget, UiToggle, UiPopover },
props: {
config: {
type: Object as PropType<WidgetMenu>,
@@ -124,6 +135,11 @@ export default defineComponent({
return {
t,
widgets,
instantiableWidgets: computed(() => {
return registry.widgets
.filter(({ widget }) => widget.instances === "multi")
.map(({ widget }) => widget);
}),
createOfType(type: string) {
wm.create(type);
},

View File

@@ -42,9 +42,14 @@ import { useI18n } from "vue-i18n";
import Widget from "./Widget.vue";
import { MainProcess } from "@/web/background/IPC";
import { Duration } from "luxon";
import { WidgetManager, StopwatchWidget } from "./interfaces";
import { WidgetManager, StopwatchWidget, WidgetSpec } from "./interfaces";
export default defineComponent({
widget: {
type: "timer",
instances: "multi",
trNameKey: "stopwatch.name",
} satisfies WidgetSpec,
components: { Widget },
props: {
config: {

View File

@@ -0,0 +1,31 @@
import type { Component } from 'vue'
import type { WidgetSpec } from './interfaces'
import WidgetTimer from './WidgetTimer.vue'
import WidgetStashSearch from '../stash-search/WidgetStashSearch.vue'
import WidgetMenu from './WidgetMenu.vue'
import PriceCheckWindow from '@/web/price-check/PriceCheckWindow.vue'
import WidgetItemCheck from '@/web/item-check/WidgetItemCheck.vue'
import WidgetImageStrip from './WidgetImageStrip.vue'
import WidgetDelveGrid from './WidgetDelveGrid.vue'
import WidgetItemSearch from '../item-search/WidgetItemSearch.vue'
import WidgetSettings from '../settings/SettingsWindow.vue'
type WidgetComponent = Component & { widget: WidgetSpec }
export const registry = {
widgets: [] as WidgetComponent[],
getWidgetComponent (wmType: string) {
return this.widgets.find(component => component.widget.type === wmType)
}
}
registry.widgets.push(WidgetMenu as unknown as WidgetComponent)
registry.widgets.push(WidgetTimer as unknown as WidgetComponent)
registry.widgets.push(WidgetStashSearch as unknown as WidgetComponent)
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(WidgetItemSearch as unknown as WidgetComponent)
registry.widgets.push(WidgetSettings as unknown as WidgetComponent)

View File

@@ -7,6 +7,14 @@ export interface Widget {
wmFlags: Array<WellKnownFlag | string>;
}
export interface WidgetSpec {
type: string
instances: 'single' | 'multi'
trNameKey?: string
initInstance?: () => Widget
defaultInstances?: () => Widget[]
}
export type WellKnownFlag =
| "uninitialized"
| "skip-menu"

View File

@@ -146,7 +146,11 @@ import UnidentifiedResolver from "./unidentified-resolver/UnidentifiedResolver.v
import CheckPositionCircle from "./CheckPositionCircle.vue";
import AppTitleBar from "@/web/ui/AppTitlebar.vue";
import ItemQuickPrice from "@/web/ui/ItemQuickPrice.vue";
import { PriceCheckWidget, WidgetManager } from "../overlay/interfaces";
import {
PriceCheckWidget,
WidgetManager,
WidgetSpec,
} from "../overlay/interfaces";
import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue";
type ParseError = {
@@ -156,6 +160,35 @@ type ParseError = {
};
export default defineComponent({
widget: {
type: "price-check",
instances: "single",
initInstance: (): PriceCheckWidget => {
return {
wmId: 0,
wmType: "price-check",
wmTitle: "",
wmWants: "hide",
wmZorder: "exclusive",
wmFlags: ["hide-on-blur", "skip-menu"],
showRateLimitState: false,
apiLatencySeconds: 2,
collapseListings: "api",
smartInitialSearch: true,
lockedInitialSearch: true,
activateStockFilter: false,
builtinBrowser: false,
hotkey: "D",
hotkeyHold: "Ctrl",
hotkeyLocked: "Ctrl + Alt + D",
showSeller: false,
searchStatRange: 10,
showCursor: true,
requestPricePrediction: false,
rememberCurrency: false,
};
},
} satisfies WidgetSpec,
components: {
AppTitleBar,
CheckedItem,

View File

@@ -123,7 +123,11 @@ import {
} from "@/web/Config";
import { APP_PATRONS } from "@/assets/data";
import { Host } from "@/web/background/IPC";
import type { Widget, WidgetManager } from "@/web/overlay/interfaces";
import type {
Widget,
WidgetManager,
WidgetSpec,
} from "@/web/overlay/interfaces";
import AppTitleBar from "@/web/ui/AppTitlebar.vue";
import SettingsHotkeys from "./hotkeys.vue";
import SettingsChat from "./chat.vue";
@@ -161,6 +165,20 @@ function quit() {
}
export default defineComponent({
widget: {
type: "settings",
instances: "single",
initInstance: () => {
return {
wmId: 0,
wmType: "settings",
wmTitle: "",
wmWants: "hide",
wmZorder: "exclusive",
wmFlags: ["invisible-on-blur", "ignore-ui-visibility"],
};
},
} satisfies WidgetSpec,
components: { AppTitleBar, ConversionWarningBanner },
props: {
config: {

View File

@@ -29,13 +29,76 @@
</Widget>
</template>
<script lang="ts">
import type { WidgetSpec } from "../overlay/interfaces.js";
import type { StashSearchWidget } from "./widget.js";
export default {
widget: {
type: "stash-search",
instances: "multi",
trNameKey: "stash_search.name",
defaultInstances: (): StashSearchWidget[] => {
return [
{
wmId: 0,
wmType: "stash-search",
wmTitle: "Map rolling",
wmWants: "hide",
wmZorder: null,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tl",
x: 35,
y: 46,
},
enableHotkeys: true,
entries: [
{ id: 1, name: "", text: '"Pack Size: +3"', hotkey: null },
{ id: 2, name: "", text: "Reflect", hotkey: null },
{ id: 3, name: "", text: '"Cannot Leech Life"', hotkey: null },
{ id: 4, name: "", text: '"Cannot Leech Mana"', hotkey: null },
],
},
{
wmId: 0,
wmType: "stash-search",
wmTitle: "Dump sorting",
wmWants: "hide",
wmZorder: null,
wmFlags: ["invisible-on-blur"],
anchor: {
pos: "tl",
x: 34,
y: 56,
},
enableHotkeys: true,
entries: [
{ id: 1, name: "", text: "Currency", hotkey: null },
{ id: 2, name: "", text: '"Divination Card"', hotkey: null },
{ id: 3, name: "", text: "Fossil", 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 },
],
},
];
},
} satisfies WidgetSpec,
};
</script>
<script setup lang="ts">
import { inject, computed, watch } from "vue";
import { useI18n } from "vue-i18n";
import { MainProcess } from "@/web/background/IPC";
import { pushHostConfig } from "@/web/Config";
import type { WidgetManager } from "../overlay/interfaces.js";
import type { StashSearchWidget } from "./widget.js";
import Widget from "../overlay/Widget.vue";
import UiToggle from "@/web/ui/UiToggle.vue";