update
This commit is contained in:
parent
20dc07b0e3
commit
ed6bf05a59
@ -8,9 +8,13 @@
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"fs:default",
|
||||
{
|
||||
"identifier": "fs:scope",
|
||||
"allow": [{ "path": "$DOCUMENT/My Games/Path of Exile 2" }, { "path": "$DOCUMENT/My Games/Path of Exile 2/**" }]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-write-text-file",
|
||||
"allow": [{ "path": "$DOCUMENT/My Games/Path of Exile 2/*" }]
|
||||
"allow": [ { "path": "$DOCUMENT/My Games/Path of Exile 2/*" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
149
src/App.vue
149
src/App.vue
@ -1,16 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, Dialog, ConfirmPopup, Toast, Splitter, SplitterPanel, ScrollPanel, useToast } from 'primevue'
|
||||
import { Button, InputText, ButtonGroup, Dialog, Listbox, ConfirmPopup, Toast, Splitter, SplitterPanel, ScrollPanel, useToast } from 'primevue'
|
||||
import TreeNav from './components/TreeNav.vue'
|
||||
import FilterDetail from './components/FilterDetail.vue'
|
||||
import Info from './components/Info.vue'
|
||||
import { computed, ref, toRaw, watch } from 'vue'
|
||||
import * as O from 'fp-ts/Option'
|
||||
import * as A from 'fp-ts/lib/Array';
|
||||
import { pipe } from 'fp-ts/function'
|
||||
import FileSaver from 'file-saver'
|
||||
import { generateFilterText, type Filter, type FilterConfig, type FilterNode } from './models'
|
||||
import { filterToTreeNode, treeNodeToFilter } from './services/filter'
|
||||
import { exists, BaseDirectory, writeTextFile } from '@tauri-apps/plugin-fs';
|
||||
import { BaseDirectory, writeTextFile, lstat, readDir, readTextFile } from '@tauri-apps/plugin-fs';
|
||||
|
||||
const darkMode = ref(document.documentElement.classList.contains('dark'))
|
||||
|
||||
@ -26,14 +22,9 @@ watch(darkMode, (dark) => {
|
||||
localStorage.theme = dark ? 'dark' : 'light'
|
||||
})
|
||||
|
||||
let defaultFilters: Filter[] = []
|
||||
const nodes = ref(pipe(
|
||||
localStorage.getItem("filters"),
|
||||
O.fromNullable,
|
||||
O.map((value): Filter[] => JSON.parse(value)),
|
||||
O.getOrElse(() => defaultFilters),
|
||||
A.map(filterToTreeNode)
|
||||
))
|
||||
const filterName = ref()
|
||||
|
||||
const nodes = ref([] as FilterNode[])
|
||||
const selectedFilter = ref<FilterNode>()
|
||||
|
||||
function onDelete() {
|
||||
@ -47,31 +38,22 @@ function onDelete() {
|
||||
selectedFilter.value = undefined
|
||||
}
|
||||
|
||||
const previewText = ref()
|
||||
|
||||
function exportFilter() {
|
||||
let text = generateFilterText(toRaw(nodes.value).map(treeNodeToFilter))
|
||||
console.log(text)
|
||||
previewText.value = text
|
||||
exportDialogVisible.value = true;
|
||||
}
|
||||
|
||||
function download() {
|
||||
var blob = new Blob([previewText.value], { type: "text/plain;charset=utf-8" });
|
||||
FileSaver.saveAs(blob, "Clearfell Default.filter")
|
||||
}
|
||||
|
||||
const exportDialogVisible = ref(false);
|
||||
const POE2_FOLDER = 'My Games/Path of Exile 2'
|
||||
const PREFIX = 'Clearfell'
|
||||
const CONFIG_SUFFIX = "config.json"
|
||||
const FILTER_SUFFIX = 'filter'
|
||||
const BASE_DIR = BaseDirectory.Document
|
||||
|
||||
async function save(toastOnComplete?: boolean) {
|
||||
if (nodes.value) {
|
||||
if (nodes.value && !!filterName.value) {
|
||||
const filters = toRaw(nodes.value).map(treeNodeToFilter)
|
||||
localStorage.setItem("filters", JSON.stringify(filters))
|
||||
|
||||
let text = generateFilterText(filters)
|
||||
await writeTextFile('My Games/Path of Exile 2/Clearfell Default.filter', text, {
|
||||
baseDir: BaseDirectory.Document,
|
||||
});
|
||||
await Promise.all([writeTextFile(`${POE2_FOLDER}/${PREFIX} ${filterName.value}.${CONFIG_SUFFIX}`, JSON.stringify({ filters }, null, 2), {
|
||||
baseDir: BASE_DIR,
|
||||
}), writeTextFile(`${POE2_FOLDER}/${PREFIX} ${filterName.value}.${FILTER_SUFFIX}`, text, {
|
||||
baseDir: BASE_DIR,
|
||||
})])
|
||||
if (toastOnComplete) {
|
||||
toast.add({ severity: 'success', summary: 'Success', detail: 'Content Saved', life: 3000 });
|
||||
}
|
||||
@ -80,34 +62,99 @@ async function save(toastOnComplete?: boolean) {
|
||||
|
||||
setInterval(save, 5000)
|
||||
|
||||
const CONFIG_PATTERN = `^${PREFIX} (.*)\\.config\\.json$`
|
||||
|
||||
const filterNames = ref([] as string[])
|
||||
|
||||
async function fetchFilterNames() {
|
||||
const ls = await readDir(POE2_FOLDER, {
|
||||
baseDir: BASE_DIR
|
||||
})
|
||||
filterNames.value = ls.map(f => {
|
||||
if (f.isFile) {
|
||||
let match = f.name.match(CONFIG_PATTERN)
|
||||
if (match) {
|
||||
return match[1]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(f => f != null)
|
||||
}
|
||||
async function load() {
|
||||
await fetchFilterNames()
|
||||
loadDialogVisible.value = true
|
||||
}
|
||||
|
||||
async function saveAs() {
|
||||
await fetchFilterNames()
|
||||
saveDialogVisible.value = true
|
||||
}
|
||||
|
||||
const loadDialogVisible = ref(false)
|
||||
const saveDialogVisible = ref(false)
|
||||
|
||||
async function loadFilter() {
|
||||
const json = await readTextFile(`${POE2_FOLDER}/${PREFIX} ${loadName.value}.${CONFIG_SUFFIX}`, {
|
||||
baseDir: BASE_DIR,
|
||||
})
|
||||
const filters: Filter[] = JSON.parse(json).filters
|
||||
nodes.value = filters.map(f => filterToTreeNode(f))
|
||||
filterName.value = loadName.value
|
||||
loadDialogVisible.value = false
|
||||
loadName.value = undefined
|
||||
}
|
||||
async function saveFilter() {
|
||||
filterName.value = saveName.value
|
||||
await save(true)
|
||||
saveDialogVisible.value = false
|
||||
saveName.value = undefined
|
||||
}
|
||||
|
||||
const loadName = ref()
|
||||
const saveName = ref()
|
||||
|
||||
const overwrite = computed(() => filterNames.value.includes(saveName.value))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ConfirmPopup />
|
||||
<Toast />
|
||||
<Dialog v-model:visible="loadDialogVisible" modal header="Load Filter">
|
||||
<Listbox v-model="loadName" :options="filterNames" />
|
||||
<template #footer>
|
||||
<Button label="Cancel" severity="secondary" @click="loadDialogVisible = false" autofocus />
|
||||
<Button :disabled="!loadName" label="Load" severity="primary" @click="loadFilter" autofocus />
|
||||
</template>
|
||||
</Dialog>
|
||||
<Dialog v-model:visible="saveDialogVisible" modal header="Save Filter As">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label>Existing Filters:</label>
|
||||
<Listbox v-model="saveName" :options="filterNames" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 mt-4">
|
||||
<label>Save Name:</label>
|
||||
<InputText type="text" v-model="saveName" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button label="Cancel" severity="secondary" @click="saveDialogVisible = false" autofocus />
|
||||
<Button :disabled="!saveName" :label="overwrite ? 'Overwrite' : 'Save'"
|
||||
:severity="overwrite ? 'danger' : 'primary'" @click="saveFilter" autofocus />
|
||||
</template>
|
||||
</Dialog>
|
||||
<div class="flex flex-row gap-2 items-center flex-grow-0">
|
||||
<article class="prose dark:prose-invert p-4">
|
||||
<h1>Path of Exile 2 Loot Filter Config</h1>
|
||||
</article><Button :icon @click="darkMode = !darkMode" severity="secondary" variant="outlined" rounded />
|
||||
<Button icon="pi pi-save" label="Save" severity="secondary" variant="outlined" rounded @click="save(true)" />
|
||||
<Button icon="pi pi-file-export" label="Export" severity="primary" @click="exportFilter" />
|
||||
<Dialog v-model:visible="exportDialogVisible" modal header="Preview Filter Text">
|
||||
<template #default>
|
||||
<ScrollPanel class="h-[50vh] w-[50vw]">
|
||||
<pre>{{ previewText }}</pre>
|
||||
</ScrollPanel>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<Button type="button" label="Cancel" severity="secondary" @click="exportDialogVisible = false"></Button>
|
||||
<Button type="button" icon="pi pi-download" label="Download" @click="download"></Button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<!-- <Button icon = "pi pi-save" severity="secondary" variant="outlined" rounded /> -->
|
||||
</article>
|
||||
<ButtonGroup>
|
||||
<Button :icon @click="darkMode = !darkMode" severity="secondary" variant="outlined" />
|
||||
<Button icon="pi pi-file-import" label="Load" severity="secondary" variant="outlined" @click="load" />
|
||||
<Button icon="pi pi-save" label="Save As" severity="secondary" variant="outlined" @click="saveAs" />
|
||||
<Button :disabled="!filterName" icon="pi pi-save" label="Save" severity="primary" @click="save(true)" />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<Splitter class="flex-1 min-h-0">
|
||||
<SplitterPanel class="flex flex-col">
|
||||
<TreeNav :nodes :selectedNode="selectedFilter" @nodeSelect="selectedFilter = $event"
|
||||
<TreeNav :filterName :nodes :selectedNode="selectedFilter" @nodeSelect="selectedFilter = $event"
|
||||
@nodeUnselect="selectedFilter = undefined" />
|
||||
</SplitterPanel>
|
||||
<SplitterPanel class="flex flex-col">
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { toLines, type Filter, type FilterNode, type FilterRule } from '@/models';
|
||||
import { toDisplayLines, type Filter, type FilterNode, type FilterRule } from '@/models';
|
||||
import { computed, ref, watchEffect } from 'vue'
|
||||
import { Button, ColorPicker, ScrollPanel, Tabs, Tab, TabPanels, TabList, TabPanel, ToggleButton, InputText, InputNumber, Select, ToggleSwitch, MultiSelect } from 'primevue'
|
||||
import { useConfirm } from "primevue/useconfirm";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { CLASSES, COLORS, BASE_TYPES, RARITIES, SHAPES, OPERATORS } from '@/models/settings';
|
||||
import { CLASSES, COLORS, BASE_TYPES, RARITIES, SHAPES, OPERATORS, ARMOUR_TYPES } from '@/models/settings';
|
||||
|
||||
const confirm = useConfirm();
|
||||
const toast = useToast();
|
||||
@ -109,7 +109,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
<ToggleButton class="w-24 flex-shrink-0" onIcon="pi pi-eye" offIcon="pi pi-eye-slash" v-if="filter.type === 'leaf'"
|
||||
v-model="filter.show" onLabel="Show" offLabel="Hide" />
|
||||
|
||||
<InputText class="w-full" type="text" :placeholder="toLines(filter.rule).join(' ')" v-model="filter.name" />
|
||||
<InputText class="w-full" type="text" :placeholder="toDisplayLines(filter).join(' ')" v-model="filter.name" />
|
||||
<Button class="flex-shrink-0" icon="pi pi-trash" severity="danger" v-on:click="confirmDelete" />
|
||||
</div>
|
||||
|
||||
@ -129,7 +129,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
Class
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -140,16 +140,16 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11">
|
||||
<MultiSelect v-if="ruleWithLookup[1].class != 'inherit' && filter.rule.class"
|
||||
v-model="filter.rule.class" display="chip" :options="CLASSES" filter placeholder="Select Classes"
|
||||
class="w-full" :maxSelectedLabels=3 fluid />
|
||||
v-model="filter.rule.class" display="chip" :options="CLASSES.slice()" filter
|
||||
placeholder="Select Classes" class="w-full" :maxSelectedLabels=3 fluid />
|
||||
|
||||
<MultiSelect disabled v-if="ruleWithLookup[1].class == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].class" display="chip" :options="CLASSES" filter
|
||||
:model-value="ruleWithLookup[0].class" display="chip" :options="CLASSES.slice()" filter
|
||||
placeholder="Select Classes" class="w-full" :maxSelectedLabels=3 fluid />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
BaseType
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -160,15 +160,29 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11">
|
||||
<MultiSelect v-if="ruleWithLookup[1].base_type != 'inherit' && filter.rule.base_type"
|
||||
v-model="filter.rule.base_type" display="chip" :options="BASE_TYPES" filter
|
||||
v-model="filter.rule.base_type" display="chip" :options="BASE_TYPES.slice()" filter
|
||||
placeholder="Select Base Types" class="w-full" :maxSelectedLabels=3 fluid />
|
||||
<MultiSelect disabled v-if="ruleWithLookup[1].base_type == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].base_type" display="chip" :options="BASE_TYPES" filter
|
||||
:model-value="ruleWithLookup[0].base_type" display="chip" :options="BASE_TYPES.slice()" filter
|
||||
placeholder="Select Base Types" class="w-full" :maxSelectedLabels=3 fluid />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-if="filter.type === 'leaf'">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
ArmourType
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
<ToggleSwitch class="align-middle" :model-value="filter.leafRule.armour_type != undefined"
|
||||
@update:model-value="filter.leafRule.armour_type = $event ? [] : undefined" />
|
||||
</td>
|
||||
<td class="h-11">
|
||||
<MultiSelect v-if="filter.leafRule.armour_type" v-model="filter.leafRule.armour_type" display="chip"
|
||||
:options="ARMOUR_TYPES.slice()" filter placeholder="Select Base Types" class="w-full"
|
||||
:maxSelectedLabels=3 fluid />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
Rarity
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -179,17 +193,17 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].rarity != 'inherit' && filter.rule.rarity"
|
||||
v-model="filter.rule.rarity[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.rarity[0]" :options="OPERATORS.slice()" />
|
||||
<Select v-if="ruleWithLookup[1].rarity != 'inherit' && filter.rule.rarity"
|
||||
v-model="filter.rule.rarity[1]" :options="RARITIES" />
|
||||
v-model="filter.rule.rarity[1]" :options="RARITIES.slice()" />
|
||||
<Select disabled v-if="ruleWithLookup[1].rarity == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].rarity![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].rarity![0]" :options="OPERATORS.slice()" />
|
||||
<Select disabled v-if="ruleWithLookup[1].rarity == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].rarity![1]" :options="RARITIES" />
|
||||
:model-value="ruleWithLookup[0].rarity![1]" :options="RARITIES.slice()" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
Sockets
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -200,17 +214,17 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].sockets != 'inherit' && filter.rule.sockets"
|
||||
v-model="filter.rule.sockets[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.sockets[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid v-if="ruleWithLookup[1].sockets != 'inherit' && filter.rule.sockets"
|
||||
v-model="filter.rule.sockets[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].sockets == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].sockets![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].sockets![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].sockets == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].sockets![1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
Quality
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -221,17 +235,17 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].quality != 'inherit' && filter.rule.quality"
|
||||
v-model="filter.rule.quality[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.quality[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid v-if="ruleWithLookup[1].quality != 'inherit' && filter.rule.quality"
|
||||
v-model="filter.rule.quality[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].quality == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].quality![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].quality![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].quality == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].quality![1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
StackSize
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -242,18 +256,18 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].stack_size != 'inherit' && filter.rule.stack_size"
|
||||
v-model="filter.rule.stack_size[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.stack_size[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid
|
||||
v-if="ruleWithLookup[1].stack_size != 'inherit' && filter.rule.stack_size"
|
||||
v-model="filter.rule.stack_size[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].stack_size == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].stack_size![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].stack_size![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].stack_size == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].stack_size![1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
AreaLevel
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -264,18 +278,18 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].area_level != 'inherit' && filter.rule.area_level"
|
||||
v-model="filter.rule.area_level[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.area_level[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid
|
||||
v-if="ruleWithLookup[1].area_level != 'inherit' && filter.rule.area_level"
|
||||
v-model="filter.rule.area_level[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].area_level == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].area_level![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].area_level![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].area_level == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].area_level![1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
DropLevel
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -286,18 +300,18 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].drop_level != 'inherit' && filter.rule.drop_level"
|
||||
v-model="filter.rule.drop_level[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.drop_level[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid
|
||||
v-if="ruleWithLookup[1].drop_level != 'inherit' && filter.rule.drop_level"
|
||||
v-model="filter.rule.drop_level[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].drop_level == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].drop_level![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].drop_level![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].drop_level == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].drop_level![1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
ItemLevel
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -308,18 +322,33 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].item_level != 'inherit' && filter.rule.item_level"
|
||||
v-model="filter.rule.item_level[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.item_level[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid
|
||||
v-if="ruleWithLookup[1].item_level != 'inherit' && filter.rule.item_level"
|
||||
v-model="filter.rule.item_level[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].item_level == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].item_level![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].item_level![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].item_level == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].item_level![1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-if="filter.type === 'leaf'">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
(Area - Drop)Level
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
<ToggleSwitch class="align-middle" :model-value="filter.leafRule.area_minus_drop_level != undefined"
|
||||
@update:model-value="filter.leafRule.area_minus_drop_level = $event ? ['>', 0] : undefined" />
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="filter.leafRule.area_minus_drop_level"
|
||||
v-model="filter.leafRule.area_minus_drop_level[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid v-if="filter.leafRule.area_minus_drop_level"
|
||||
v-model="filter.leafRule.area_minus_drop_level[1]" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
WaystoneTier
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -330,12 +359,12 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].waystone_tier != 'inherit' && filter.rule.waystone_tier"
|
||||
v-model="filter.rule.waystone_tier[0]" :options="OPERATORS" />
|
||||
v-model="filter.rule.waystone_tier[0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber class="w-16" fluid
|
||||
v-if="ruleWithLookup[1].waystone_tier != 'inherit' && filter.rule.waystone_tier"
|
||||
v-model="filter.rule.waystone_tier[1]" />
|
||||
<Select disabled v-if="ruleWithLookup[1].waystone_tier == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].waystone_tier![0]" :options="OPERATORS" />
|
||||
:model-value="ruleWithLookup[0].waystone_tier![0]" :options="OPERATORS.slice()" />
|
||||
<InputNumber disabled class="w-16" fluid v-if="ruleWithLookup[1].waystone_tier == 'inherit'"
|
||||
:model-value="ruleWithLookup[0].waystone_tier![1]" />
|
||||
</td>
|
||||
@ -353,7 +382,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
SetFontSize
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -372,7 +401,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
SetTextColor
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -391,7 +420,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
SetBackgroundColor
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -410,7 +439,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
SetBorderColor
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -429,7 +458,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
PlayAlertSound
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -452,7 +481,7 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
MinimapIcon
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -466,19 +495,19 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
v-if="ruleWithLookup[1].minimap_icon != 'inherit' && filter.rule.minimap_icon"
|
||||
v-model="filter.rule.minimap_icon[0]" />
|
||||
<Select v-if="ruleWithLookup[1].minimap_icon != 'inherit' && filter.rule.minimap_icon"
|
||||
v-model="filter.rule.minimap_icon[1]" :options="COLORS" />
|
||||
v-model="filter.rule.minimap_icon[1]" :options="COLORS.slice()" />
|
||||
<Select v-if="ruleWithLookup[1].minimap_icon != 'inherit' && filter.rule.minimap_icon"
|
||||
v-model="filter.rule.minimap_icon[2]" :options="SHAPES" />
|
||||
v-model="filter.rule.minimap_icon[2]" :options="SHAPES.slice()" />
|
||||
<InputNumber class="w-16" fluid v-if="ruleWithLookup[1].minimap_icon == 'inherit'" disabled
|
||||
:model-value="ruleWithLookup[0].minimap_icon![0]" />
|
||||
<Select v-if="ruleWithLookup[1].minimap_icon == 'inherit'" disabled
|
||||
:model-value="ruleWithLookup[0].minimap_icon![1]" :options="COLORS" />
|
||||
:model-value="ruleWithLookup[0].minimap_icon![1]" :options="COLORS.slice()" />
|
||||
<Select v-if="ruleWithLookup[1].minimap_icon == 'inherit'" disabled
|
||||
:model-value="ruleWithLookup[0].minimap_icon![2]" :options="SHAPES" />
|
||||
:model-value="ruleWithLookup[0].minimap_icon![2]" :options="SHAPES.slice()" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pr-4">
|
||||
<td class="pr-4 whitespace-nowrap">
|
||||
PlayEffect
|
||||
</td>
|
||||
<td class="pr-4">
|
||||
@ -489,9 +518,9 @@ function mergeLookup([childValue, childLookup]: any, [parentValue, parentLookup]
|
||||
</td>
|
||||
<td class="h-11 flex gap-2">
|
||||
<Select v-if="ruleWithLookup[1].play_effect != 'inherit' && filter.rule.play_effect"
|
||||
v-model="filter.rule.play_effect" :options="COLORS" />
|
||||
v-model="filter.rule.play_effect" :options="COLORS.slice()" />
|
||||
<Select v-if="ruleWithLookup[1].play_effect == 'inherit'" disabled
|
||||
:model-value="ruleWithLookup[0].play_effect" :options="COLORS" />
|
||||
:model-value="ruleWithLookup[0].play_effect" :options="COLORS.slice()" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { defaultGroup, defaultLeaf, toLines, type FilterNode } from '@/models';
|
||||
import { defaultGroup, defaultLeaf, toDisplayLines, type FilterNode } from '@/models';
|
||||
import { filterToTreeNode } from '@/services/filter';
|
||||
import { Button, Menu, Chip } from 'primevue';
|
||||
import { ref } from 'vue'
|
||||
@ -42,7 +42,7 @@ const toggle = (event: MouseEvent) => {
|
||||
<template>
|
||||
<div class="flex items-center w-full flex-1">
|
||||
<div class="flex-1 flex truncate">
|
||||
<Chip v-if="!node.data.name" class="text-xs py-1.5 px-2" v-for="line in toLines(node.data.rule)">
|
||||
<Chip v-if="!node.data.name" class="text-xs py-1.5 px-2" v-for="line in toDisplayLines(node.data)">
|
||||
<span class="truncate max-w-40">{{ line }}</span>
|
||||
</Chip>
|
||||
<label v-else>{{ node.data.name }}</label>
|
||||
|
||||
@ -10,6 +10,7 @@ import { filterToTreeNode } from '@/services/filter';
|
||||
const props = defineProps<{
|
||||
nodes: FilterNodeType[]
|
||||
selectedNode?: FilterNodeType
|
||||
filterName?: string
|
||||
}>()
|
||||
const emit = defineEmits(['nodeSelect', 'nodeUnselect'])
|
||||
const selectedKey = ref()
|
||||
@ -144,7 +145,7 @@ const expandedKeys
|
||||
<template>
|
||||
<div class="flex flex-shrink-0 p-4 items-center">
|
||||
<article class="prose dark:prose-invert flex-1">
|
||||
<h2>Rules:</h2>
|
||||
<h2>{{ filterName }}</h2>
|
||||
</article>
|
||||
<ButtonGroup>
|
||||
<Button class="flex-shrink-0" :disabled="selectedPosition == undefined || selectedPosition[0] <= 0"
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { show } from 'fp-ts'
|
||||
import * as uuid from 'uuid'
|
||||
import type {
|
||||
ARMOUR_TYPES,
|
||||
BASE_TYPES,
|
||||
CLASSES,
|
||||
COLORS,
|
||||
OPERATORS,
|
||||
RARITIES,
|
||||
SHAPES,
|
||||
} from './settings'
|
||||
|
||||
interface _Filter {
|
||||
id: string
|
||||
@ -16,6 +24,7 @@ export function defaultLeaf(): FilterLeaf {
|
||||
enabled: true,
|
||||
rule: {},
|
||||
show: true,
|
||||
leafRule: {},
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +46,7 @@ export interface FilterConfig {
|
||||
export type FilterLeaf = _Filter & {
|
||||
type: 'leaf'
|
||||
show: boolean
|
||||
leafRule: LeafRule
|
||||
}
|
||||
|
||||
export type FilterGroup = _Filter & {
|
||||
@ -46,39 +56,37 @@ export type FilterGroup = _Filter & {
|
||||
|
||||
export type Filter = FilterLeaf | FilterGroup
|
||||
|
||||
export type ItemClass = any // Replace with actual type definition
|
||||
export type ItemBaseType = any // Replace with actual type definition
|
||||
export type Op = any // Replace with actual type definition
|
||||
export type Level = RangedNumber<1, 100> // Replace with actual type definition
|
||||
export type ItemRarity = string // Replace with actual type definition
|
||||
export type GameColor = any // Replace with actual type definition
|
||||
export type MinimapIconShape = any // Replace with actual type definition
|
||||
|
||||
export interface RangedNumber<T extends number, U extends number> {
|
||||
value: number
|
||||
min: T
|
||||
max: U
|
||||
}
|
||||
|
||||
export interface Color {
|
||||
r: number
|
||||
g: number
|
||||
b: number
|
||||
}
|
||||
|
||||
export interface FilterRule {
|
||||
class?: string[] //
|
||||
base_type?: string[] //
|
||||
area_level?: [string, number] //
|
||||
drop_level?: [string, number] //
|
||||
item_level?: [string, number] //
|
||||
rarity?: [string, string] //
|
||||
sockets?: [string, number] //
|
||||
quality?: [string, number] //
|
||||
stack_size?: [string, number] //
|
||||
export type Class = (typeof CLASSES)[number]
|
||||
export type BaseType = (typeof BASE_TYPES)[number]
|
||||
export type ArmourType = (typeof ARMOUR_TYPES)[number]
|
||||
export type Op = (typeof OPERATORS)[number]
|
||||
export type Rarity = (typeof RARITIES)[number]
|
||||
export type GameColor = (typeof COLORS)[number]
|
||||
export type Shape = (typeof SHAPES)[number]
|
||||
|
||||
export interface LeafRule {
|
||||
armour_type?: ArmourType[] // special addition
|
||||
area_minus_drop_level?: [Op, number] // special addition
|
||||
}
|
||||
|
||||
export interface FilterRule {
|
||||
class?: Class[] //
|
||||
base_type?: BaseType[] //
|
||||
area_level?: [Op, number] //
|
||||
drop_level?: [Op, number] //
|
||||
item_level?: [Op, number] //
|
||||
rarity?: [Op, Rarity] //
|
||||
sockets?: [Op, number] //
|
||||
quality?: [Op, number] //
|
||||
stack_size?: [Op, number] //
|
||||
// waystones
|
||||
waystone_tier?: [string, number] //
|
||||
waystone_tier?: [Op, number] //
|
||||
|
||||
// effects
|
||||
set_font_size?: number //
|
||||
@ -86,8 +94,8 @@ export interface FilterRule {
|
||||
set_border_color?: Color //
|
||||
set_background_color?: Color //
|
||||
play_alert_sound?: [number, number] //
|
||||
play_effect?: string //
|
||||
minimap_icon?: [number, string, string] //
|
||||
play_effect?: GameColor //
|
||||
minimap_icon?: [number, GameColor, Shape] //
|
||||
}
|
||||
|
||||
export interface FilterNode {
|
||||
@ -97,7 +105,22 @@ export interface FilterNode {
|
||||
children?: FilterNode[]
|
||||
}
|
||||
|
||||
export function toLines(rule: FilterRule): string[] {
|
||||
export function toDisplayLines(filter: Filter) {
|
||||
let r: string[] = toLines(filter.rule)
|
||||
if (filter.type === 'leaf') {
|
||||
if (filter.leafRule.armour_type) {
|
||||
r.push(`ArmourType ${filter.leafRule.armour_type.map((c) => `"${c}"`).join(' ')}`)
|
||||
}
|
||||
if (filter.leafRule.area_minus_drop_level) {
|
||||
r.push(
|
||||
`Area-DropLevel ${filter.leafRule.area_minus_drop_level[0]} ${filter.leafRule.area_minus_drop_level[1]}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
export function toLines(rule: FilterRuleRaw): string[] {
|
||||
let r: string[] = []
|
||||
if (rule.class && rule.class.length > 0) {
|
||||
r.push(`Class ${rule.class.map((c) => `"${c}"`).join(' ')}`)
|
||||
@ -105,6 +128,15 @@ export function toLines(rule: FilterRule): string[] {
|
||||
if (rule.base_type && rule.base_type.length > 0) {
|
||||
r.push(`BaseType ${rule.base_type.map((c) => `"${c}"`).join(' ')}`)
|
||||
}
|
||||
if (rule.base_armour) {
|
||||
r.push(`BaseArmour ${rule.base_armour[0]} ${rule.base_armour[1]}`)
|
||||
}
|
||||
if (rule.base_evasion) {
|
||||
r.push(`BaseEvasion ${rule.base_evasion[0]} ${rule.base_evasion[1]}`)
|
||||
}
|
||||
if (rule.base_energy_shield) {
|
||||
r.push(`BaseEnergyShield ${rule.base_energy_shield[0]} ${rule.base_energy_shield[1]}`)
|
||||
}
|
||||
if (rule.rarity) {
|
||||
r.push(`Rarity ${rule.rarity[0]} ${rule.rarity[1]}`)
|
||||
}
|
||||
@ -174,7 +206,7 @@ export function generateFilterText(filters: Filter[]): string {
|
||||
.join('\n\n')
|
||||
}
|
||||
|
||||
function flatten(filters: Filter[]): Omit<FilterLeaf, 'id' | 'name' | 'type' | 'enabled'>[] {
|
||||
function flatten(filters: Filter[]): { show: boolean; rule: FilterRuleRaw }[] {
|
||||
return filters.flatMap((f) => {
|
||||
if (!f.enabled) {
|
||||
return []
|
||||
@ -194,7 +226,85 @@ function flatten(filters: Filter[]): Omit<FilterLeaf, 'id' | 'name' | 'type' | '
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return [{ show: f.show, rule: f.rule }]
|
||||
return expandLeaf(f).map((r) => ({ show: f.show, rule: r }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type FilterRuleRaw = FilterRule & {
|
||||
base_armour?: [Op, number]
|
||||
base_evasion?: [Op, number]
|
||||
base_energy_shield?: [Op, number]
|
||||
}
|
||||
|
||||
function expandLeaf(leaf: FilterLeaf): FilterRuleRaw[] {
|
||||
let result = [leaf.rule]
|
||||
if (leaf.leafRule.area_minus_drop_level) {
|
||||
result = []
|
||||
for (let area_level = 1; area_level <= 100; area_level++) {
|
||||
let rule = { ...leaf.rule }
|
||||
let op: Op
|
||||
switch (leaf.leafRule.area_minus_drop_level[0]) {
|
||||
case '<':
|
||||
op = '>'
|
||||
break
|
||||
case '<=':
|
||||
op = '>='
|
||||
break
|
||||
case '>':
|
||||
op = '<'
|
||||
break
|
||||
case '>=':
|
||||
op = '<='
|
||||
break
|
||||
default:
|
||||
op = leaf.leafRule.area_minus_drop_level[0]
|
||||
}
|
||||
rule.area_level = ['==', area_level]
|
||||
rule.drop_level = [op, Math.max(0, area_level - leaf.leafRule.area_minus_drop_level[1])]
|
||||
result.push(rule)
|
||||
}
|
||||
}
|
||||
|
||||
if (leaf.leafRule.armour_type) {
|
||||
result = result.flatMap((r) => {
|
||||
return leaf.leafRule.armour_type!.map((t) => {
|
||||
let rule: FilterRuleRaw = { ...r }
|
||||
switch (t) {
|
||||
case 'Armour':
|
||||
rule.base_armour = ['>', 0]
|
||||
rule.base_evasion = ['<=', 0]
|
||||
rule.base_energy_shield = ['<=', 0]
|
||||
break
|
||||
case 'Evasion':
|
||||
rule.base_armour = ['<=', 0]
|
||||
rule.base_evasion = ['>', 0]
|
||||
rule.base_energy_shield = ['<=', 0]
|
||||
break
|
||||
case 'EnergyShield':
|
||||
rule.base_armour = ['<=', 0]
|
||||
rule.base_evasion = ['<=', 0]
|
||||
rule.base_energy_shield = ['>', 0]
|
||||
break
|
||||
case 'Armour + Evasion':
|
||||
rule.base_armour = ['>', 0]
|
||||
rule.base_evasion = ['>', 0]
|
||||
rule.base_energy_shield = ['<=', 0]
|
||||
break
|
||||
case 'Armour + EnergyShield':
|
||||
rule.base_armour = ['>', 0]
|
||||
rule.base_evasion = ['<=', 0]
|
||||
rule.base_energy_shield = ['>', 0]
|
||||
break
|
||||
case 'Evasion + EnergyShield':
|
||||
rule.base_armour = ['<=', 0]
|
||||
rule.base_evasion = ['>', 0]
|
||||
rule.base_energy_shield = ['>', 0]
|
||||
break
|
||||
}
|
||||
return rule
|
||||
})
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export const OPERATORS = ['=', '==', '!=', '<', '<=', '>', '>=']
|
||||
export const OPERATORS = ['=', '==', '!=', '<', '<=', '>', '>='] as const
|
||||
|
||||
export const COLORS = [
|
||||
'Red',
|
||||
@ -12,7 +12,7 @@ export const COLORS = [
|
||||
'Orange',
|
||||
'Pink',
|
||||
'Purple',
|
||||
]
|
||||
] as const
|
||||
|
||||
export const SHAPES = [
|
||||
'Circle',
|
||||
@ -27,9 +27,9 @@ export const SHAPES = [
|
||||
'Kite',
|
||||
'Pentagon',
|
||||
'UpsideDownHouse',
|
||||
]
|
||||
] as const
|
||||
|
||||
export const RARITIES = ['Normal', 'Magic', 'Rare', 'Unique']
|
||||
export const RARITIES = ['Normal', 'Magic', 'Rare', 'Unique'] as const
|
||||
|
||||
export const CLASSES = [
|
||||
'Currency',
|
||||
@ -62,7 +62,7 @@ export const CLASSES = [
|
||||
'Helmet',
|
||||
'Shield',
|
||||
'Quiver',
|
||||
]
|
||||
] as const
|
||||
|
||||
export const BASE_TYPES = [
|
||||
'Exalted Orb',
|
||||
@ -85,4 +85,13 @@ export const BASE_TYPES = [
|
||||
'Orb of Transmutation',
|
||||
'Scroll of Wisdom',
|
||||
'Portal Scroll',
|
||||
]
|
||||
] as const
|
||||
|
||||
export const ARMOUR_TYPES = [
|
||||
'Armour',
|
||||
'Evasion',
|
||||
'EnergyShield',
|
||||
'Armour + Evasion',
|
||||
'Armour + EnergyShield',
|
||||
'Evasion + EnergyShield',
|
||||
] as const
|
||||
|
||||
Loading…
Reference in New Issue
Block a user