Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ read_globals = {
-- Cross-addon references
"EasyFindDevDB",

-- Blizzard settings frames
"SettingsPanel", "InterfaceOptionsFrame",

-- Constants, Enums, Mixins
"Enum", "Settings", "BackdropTemplateMixin",
"SOUNDKIT", "UIDROPDOWNMENU_OPEN_MENU", "UISpecialFrames",
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ All notable changes to EasyFind will be documented in this file.

---

## [1.3.1] - 2026-03-17

### Added
- **EasyFind-Only Navigation Options**: Minimap arrow glow and guide circle now have sub-options to restrict behavior to EasyFind-placed waypoints only, ignoring other sources
- **Independent Map Navigation Modes**: Local and global map search bars now have separate direct-open toggles instead of a shared setting
- **Expanded POI Coverage**: Added portals in Tirisfal Glades, decor specialists, and additional innkeepers, auction houses, and mailboxes across multiple zones

### Changed
- **Slash Commands**: `/ef` now opens options directly. `/ef toggle` (shorthand `/ef t`) replaces `/ef show`/`/ef hide`. Added `/ef help` for a command overview
- **Options Panel Dropdowns**: Redesigned with WoW-style open/close arrows and indented child options

### Fixed
- **New Map POI Categories**: Decor specialists, crafting order NPCs, rostrum, pet and riding trainers, and training dummies now properly appear in map search results
- **Tooltip Clarity**: Map search bar editbox now shows bar identity (Zone/Global Search) and the current mode. Mode toggle button describes only the toggle action

---

## [1.3.0] - 2026-03-15

### Added
Expand Down
54 changes: 30 additions & 24 deletions Core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ local DB_DEFAULTS = {
iconScale = 0.8,
uiSearchScale = 1.0,
mapSearchScale = 1.0,
mapSearchWidth = 1.0,
uiSearchWidth = 1.0,
mapSearchWidth = 0.88,
uiSearchWidth = 0.88,
uiResultsScale = 1.0,
uiResultsWidth = 350,
uiResultsWidth = 300,
mapResultsScale = 1.0,
mapResultsWidth = 1.0,
mapResultsWidth = 300,
searchBarOpacity = 0.75, -- ns.DEFAULT_OPACITY
fontSize = 1.0, -- UI search font size multiplier (0.5-2.0)
mapFontSize = 1.0, -- Map search font size multiplier (0.5-2.0)
fontSize = 0.9, -- UI search font size multiplier (0.5-2.0)
mapFontSize = 0.9, -- Map search font size multiplier (0.5-2.0)
uiSearchPosition = nil, -- {point, relPoint, x, y}
mapSearchPosition = nil, -- x offset from map left edge
globalSearchPosition = nil, -- x offset from map right edge (windowed)
Expand All @@ -52,7 +52,8 @@ local DB_DEFAULTS = {
mapSearchYOffset = 0, -- y offset for search bars relative to map bottom
hideSearchBarsMaximized = false, -- Hide search bars when map is full screen
directOpen = false, -- Open panels directly instead of step-by-step
navigateToZonesDirectly = false, -- Clicking a zone goes directly to it
localMapDirectOpen = false, -- Zone bar: navigate directly (no zone highlighting)
globalMapDirectOpen = false, -- Global bar: navigate directly (no zone highlighting)
smartShow = false, -- Hide search bar until mouse hovers nearby
mapSmartShow = false, -- Hide map search bars until mouse hovers nearby
resultsTheme = "Retail", -- "Classic" or "Retail"
Expand All @@ -66,12 +67,15 @@ local DB_DEFAULTS = {
pinnedUIItems = {}, -- Pinned UI search results (persist across sessions)
pinnedMapItems = {}, -- Pinned map search results (persist across sessions)
pinsCollapsed = false, -- Whether the "Pinned Paths" header is collapsed
mapPinsCollapsed = false, -- Whether the map search "Pinned" header is collapsed
showLoginMessage = true, -- Show "EasyFind loaded!" message on login
blinkingPins = false, -- Pulse map pins and highlights in sync with indicator bob
mapPinHighlight = true, -- Show yellow highlight box around map pins
arrivalDistance = 10, -- Yards - auto-clear waypoint when player is this close
minimapArrowGlow = true, -- Pulsing glow on minimap perimeter arrow
glowOnlyEasyFind = false, -- Only show glow for waypoints placed by EasyFind
minimapGuideCircle = true, -- Near-track ring + arrow around player on minimap
circleOnlyEasyFind = false, -- Only show guide circle for EasyFind waypoints
guideCircleScale = 1.0, -- Scale multiplier for guide circle ring+arrow
minimapPinGlow = true, -- Pulsing glow on map pin when guide circle shrinks onto it
autoPinClear = true, -- Auto-clear map pin when player arrives
Expand All @@ -80,7 +84,7 @@ local DB_DEFAULTS = {
mapResultsAbove = false, -- Show map search results above the search bar
panelOpacity = 0.9, -- Options panel background opacity
showMinimapButton = true, -- Show toggle button on minimap
minimapButtonAngle = 220, -- Position angle (degrees) around minimap edge
minimapButtonAngle = 200, -- Position angle (degrees) around minimap edge
globalSearchFilters = { -- Global search category filters (all enabled by default)
zones = true,
dungeons = true,
Expand Down Expand Up @@ -110,7 +114,7 @@ local DB_MIGRATIONS = {
if not db.mapMaxResults then db.mapMaxResults = db.maxResults end
db.maxResults = nil
end
if db.uiResultsWidth == 1.0 then db.uiResultsWidth = 350 end
if db.uiResultsWidth == 1.0 then db.uiResultsWidth = 300 end
end,
}

Expand Down Expand Up @@ -217,12 +221,6 @@ local function OnInitialize()

EasyFind.db = EasyFindDB

-- Sync navigateToZonesDirectly with directOpen when map search is enabled
local filters = EasyFind.db.uiSearchFilters
if filters and filters.map ~= false then
EasyFind.db.navigateToZonesDirectly = EasyFind.db.directOpen or false
end

-- Read version from TOC for What's New detection
ns.version = C_AddOns.GetAddOnMetadata(ADDON_NAME, "Version")

Expand All @@ -232,11 +230,9 @@ local function OnInitialize()
msg = msg and msg:lower():trim() or ""
if msg == "o" or msg == "options" or msg == "config" or msg == "settings" then
EasyFind:OpenOptions()
elseif msg == "hide" then
if ns.UI then ns.UI:Hide() end
elseif msg == "show" then
if ns.UI then ns.UI:Show() end
elseif msg == "clear" then
elseif msg == "toggle" or msg == "t" then
if ns.UI then ns.UI:Toggle() end
elseif msg == "c" or msg == "clear" then
EasyFind:ClearAll()
elseif msg:find("^test ") then
-- /ef test Interface\\Path\\To\\Texture
Expand All @@ -254,7 +250,7 @@ local function OnInitialize()
sf:SetBackdrop(nil)
EasyFind:Print("Border hidden - /reload to restore")
end
elseif msg == "reset" then
elseif msg == "r" or msg == "reset" then
if ns.Options then
ns.Options:Initialize()
StaticPopup_Show("EASYFIND_RESET_ALL")
Expand All @@ -270,8 +266,18 @@ local function OnInitialize()
end
elseif msg == "whatsnew" then
if ns.UI then ns.UI:ShowWhatsNew(ns.version) end
elseif msg == "help" or msg == "h" or msg == "?" then
EasyFind:Print("Commands:")
EasyFind:Print(" /ef: open options panel")
EasyFind:Print(" /ef toggle: show/hide search bar")
EasyFind:Print(" /ef clear: dismiss highlights, pins, breadcrumbs")
EasyFind:Print(" /ef reset: reset all settings")
EasyFind:Print(" /ef bug: report a bug")
EasyFind:Print(" /ef feature: request a feature")
elseif msg == "" then
EasyFind:OpenOptions()
else
EasyFind:Print("Usage: /ef show | hide | clear | options | reset | bug | feature")
print("|cFFFFFF00Type '/ef help' for a list of commands.|r")
end
end

Expand Down Expand Up @@ -400,7 +406,6 @@ function EasyFind:ClearAll()
ns.MapSearch:ClearZoneHighlight()
ns.MapSearch.pendingWaypoint = nil
end
EasyFind:Print("Active highlights cleared.")
end

function EasyFind:StartGuide(guideData)
Expand Down Expand Up @@ -566,7 +571,7 @@ local function CreateMinimapButton()
mmBtn:SetScript("OnLeave", GameTooltip_Hide)

minimapButton = mmBtn
PositionMinimapButton(EasyFind.db.minimapButtonAngle or 220)
PositionMinimapButton(EasyFind.db.minimapButtonAngle or 200)
return mmBtn
end

Expand All @@ -576,6 +581,7 @@ function EasyFind:UpdateMinimapButton()
CreateMinimapButton()
end
minimapButton:Show()
PositionMinimapButton(EasyFind.db.minimapButtonAngle or 200)
elseif minimapButton then
minimapButton:Hide()
end
Expand Down
42 changes: 22 additions & 20 deletions Database.lua
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ function Database:FlattenTree(tree, parentPath, parentSteps, parentButtonFrame,
if node.icon then entry.icon = node.icon end
if node.available then entry.available = node.available end
if node.canQueue then entry.canQueue = true end
if node.children then entry.isContainer = true end

uiSearchData[#uiSearchData + 1] = entry

Expand Down Expand Up @@ -1558,9 +1559,8 @@ local FUZZY_BLOCKLIST = {
["pve"] = { ["pvp"] = true },
}

--- Check if `query` appears at a word boundary in `text`.
--- A word boundary is the start of the string or right after a space/punctuation.
--- Returns true if found at a boundary, false if only found mid-word or not at all.
-- A word boundary is the start of the string or right after a space/punctuation.
-- Returns true if found at a boundary, false if only found mid-word or not at all.
function Database:FindAtWordBoundary(text, query)
-- Check at start of string
if ssub(text, 1, #query) == query then return true end
Expand All @@ -1581,11 +1581,11 @@ function Database:FindAtWordBoundary(text, query)
end
end

--- Score how well `query` matches as initials/abbreviation of words in `text`.
--- "rb" → "rated battlegrounds" = 130 (each char matches a word start)
--- "raba" → "random battleground" = 125 (prefix of words)
--- "ranb" → "random battleground" = 115 (longer prefix matching)
--- Returns 0 if no reasonable initials match found.
-- Score how well `query` matches as initials/abbreviation of words in `text`.
-- "rb" → "rated battlegrounds" = 130 (each char matches a word start)
-- "raba" → "random battleground" = 125 (prefix of words)
-- "ranb" → "random battleground" = 115 (longer prefix matching)
-- Returns 0 if no reasonable initials match found.
function Database:ScoreInitials(text, query)
-- Use cached word split (input is already lowercase)
local words = GetWords(text)
Expand Down Expand Up @@ -1649,9 +1649,9 @@ function Database:ScoreInitials(text, query)
return 0
end

--- Score fuzzy/typo matching using Damerau-Levenshtein distance.
--- Only applied to individual words in `text` that are similar in length to `query`.
--- Returns a score > 0 if a close match is found, 0 otherwise.
-- Score fuzzy/typo matching using Damerau-Levenshtein distance.
-- Only applied to individual words in `text` that are similar in length to `query`.
-- Returns a score > 0 if a close match is found, 0 otherwise.

function Database:ScoreFuzzy(text, query, queryLen)
local bestScore = 0
Expand All @@ -1678,17 +1678,18 @@ function Database:ScoreFuzzy(text, query, queryLen)
return bestScore
end

-- Check if all characters of `query` appear in order within `word`.
-- Requires first character match to avoid spurious hits like "ahn'" in "magtheridon's".
function Database:IsSubsequence(word, query, queryLen)
if ssub(word, 1, 1) ~= ssub(query, 1, 1) then return false end
local wi = 1
local wordLen = #word
local firstPos
for qi = 1, queryLen do
local qc = ssub(query, qi, qi)
local found = false
while wi <= wordLen do
if ssub(word, wi, wi) == qc then
if not firstPos then firstPos = wi end
wi = wi + 1
found = true
break
Expand All @@ -1697,11 +1698,12 @@ function Database:IsSubsequence(word, query, queryLen)
end
if not found then return false end
end
return true
-- Reject sparse matches: "inn" in "instance" matches i(1)n(2)n(6) but span 6 > 5
return (wi - 1) - firstPos + 1 <= queryLen * 2 - 1
end

--- Damerau-Levenshtein distance (supports transpositions).
--- Capped: returns early if distance exceeds 2 (saves CPU).
-- Damerau-Levenshtein distance (supports transpositions).
-- Capped: returns early if distance exceeds 2 (saves CPU).
function Database:DamerauLevenshtein(s1, s2, len1, len2)
if mabs(len1 - len2) > 2 then return 3 end -- too different, skip

Expand Down Expand Up @@ -1735,9 +1737,9 @@ function Database:DamerauLevenshtein(s1, s2, len1, len2)
return prev[len2]
end

--- Unified name scoring: exact → starts-with → word-boundary → substring → initials → fuzzy.
--- All search features (UI, map zone, map POI) use this single function.
--- Returns a score ≥ 0. Caller decides the minimum threshold.
-- Unified name scoring: exact → starts-with → word-boundary → substring → initials → fuzzy.
-- All search features (UI, map zone, map POI) use this single function.
-- Returns a score ≥ 0. Caller decides the minimum threshold.
function Database:ScoreName(nameLower, query, queryLen, optQueryWords)
-- Trim trailing whitespace so "windrnr " behaves like "windrnr"
if ssub(query, queryLen, queryLen) == " " then
Expand Down Expand Up @@ -1880,8 +1882,8 @@ function Database:ScoreName(nameLower, query, queryLen, optQueryWords)
return score
end

--- Unified keyword scoring: additive score from matching against a list of keywords.
--- Returns a total score to ADD to the name score.
-- Unified keyword scoring: additive score from matching against a list of keywords.
-- Returns a total score to ADD to the name score.
function Database:ScoreKeywords(keywordsLower, query, queryLen, optQueryWords)
if not keywordsLower then return 0 end

Expand Down
2 changes: 1 addition & 1 deletion EasyFind.toc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Title: EasyFind
## Notes: Search and locate UI elements and map locations with ease
## Author: justawower
## Version: 1.3.0
## Version: 1.3.1
## IconTexture: 136460
## Category: UI
## AddonCompartmentFunc: EasyFind_OnAddonCompartmentClick
Expand Down
Loading
Loading