Skip to content
Open
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
5 changes: 3 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,11 @@ <h5 id="loader" style="display: none">(loading)</h5>
<div id="canvas-wrapper">
<canvas id="canvas"></canvas>
</div>
<div id="search-wrap">

<div id="search-wrap">
<input type="text" id="search-bar" /><br/>
<div id="search-info">...</div>
<div id="autocomplete"></div>
<div id="search-info">Type to search...</div>
</div>
</main>

Expand Down
104 changes: 104 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ let loading = 0

/** @type {PlistAndImageComboDeal} */
let currentCombo = null
let autocompleteResults = []
let autocompleteIndex = -1
let spriteNameCache = []


/** @type {PlistDict} */
let currentDict = null
Expand Down Expand Up @@ -243,6 +247,11 @@ function updateCanvasSize() {
async function updateCurrentCombo() {
currentDict = null
currentCombo = await autoGetImageAndPlist()
if (currentCombo) {
spriteNameCache = currentCombo.plist.map(p => p.key)
} else {
spriteNameCache = []
}
}

function draw() {
Expand Down Expand Up @@ -632,6 +641,78 @@ function searchCurrentCombo(name, exact = false) {
}
}

function fuzzyScore(query, target) {
query = query.toLowerCase()
target = target.toLowerCase()

if (target.startsWith(query)) return 1000 - target.length

let qi = 0
let score = 0
for (let i = 0; i < target.length && qi < query.length; i++) {
if (target[i] === query[qi]) {
score += 10
qi++
}
}
if (qi !== query.length) return -1
return score - target.length
}

function updateAutocomplete(query) {
const container = document.querySelector("#autocomplete")
if (!query || !currentCombo) {
container.classList.add("autocomplete-hidden")
return
}

let scored = spriteNameCache
.map(name => ({ name, score: fuzzyScore(query, name) }))
.filter(x => x.score >= 0)
.sort((a, b) => b.score - a.score)
.slice(0, 20)

autocompleteResults = scored.map(x => x.name)
autocompleteIndex = -1

if (autocompleteResults.length === 0) {
container.classList.add("autocomplete-hidden")
return
}

container.innerHTML = ""
for (let name of autocompleteResults) {
const div = document.createElement("div")
div.className = "autocomplete-item"
div.textContent = name
div.addEventListener("mousedown", () => {
selectAutocomplete(name)
})
container.appendChild(div)
}

container.classList.remove("autocomplete-hidden")
}

function selectAutocomplete(name) {
const searchBar = document.querySelector("#search-bar")
searchBar.value = name
document.querySelector("#autocomplete").classList.add("autocomplete-hidden")

currentDictLocked = true
currentDict = searchCurrentCombo(name, true).result
if (currentDict) updateInfoAndPreview()
}

function moveAutocomplete(dir) {
const items = document.querySelectorAll(".autocomplete-item")
if (items.length === 0) return

autocompleteIndex = (autocompleteIndex + dir + items.length) % items.length
items.forEach(el => el.classList.remove("active"))
items[autocompleteIndex].classList.add("active")
}

function populateSelectionFromURL() {
let hash = new URL(window.location.href).hash
if (hash == "") return
Expand Down Expand Up @@ -761,6 +842,7 @@ function populateSelectionFromURL() {
if (searchTerm == "") {
currentDictLocked = false
document.querySelector("#search-info").innerText = "Type to search..."
updateAutocomplete("")
return
}

Expand All @@ -776,9 +858,31 @@ function populateSelectionFromURL() {
document.querySelector("#search-info").innerText = `No results!`
}
}

updateAutocomplete(searchTerm)
})

window.addEventListener("keydown", event => {
const container = document.querySelector("#autocomplete")

if (!container.classList.contains("autocomplete-hidden")) {
if (event.code === "ArrowDown") {
event.preventDefault()
moveAutocomplete(1)
return
}
if (event.code === "ArrowUp") {
event.preventDefault()
moveAutocomplete(-1)
return
}
if (event.code === "Enter" && autocompleteIndex >= 0) {
event.preventDefault()
selectAutocomplete(autocompleteResults[autocompleteIndex])
return
}
}

if (event.code == "KeyF" && event.ctrlKey) {
event.preventDefault()
searchBar.value = ""
Expand Down
29 changes: 25 additions & 4 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,14 @@ canvas#preview, canvas#preview2 {

#search-wrap {
position: fixed;
top: -70px;
top: 5px;
right: 5px;

transition: translate .2s linear;

background-color: #ffffff8a;
}

#search-wrap:has(#search-bar:focus) {
translate: 0px 75px;
}

#search-bar {
width: 250px;
Expand All @@ -187,6 +184,30 @@ label {
user-select: none;
}

#autocomplete {
width: 250px;
max-height: calc(100vh - 120px);
overflow-y: auto;
background: #ffffffee;
border: 1px solid #ccc;
font-family: monospace;
font-size: 14px;
}

.autocomplete-item {
padding: 4px 6px;
cursor: pointer;
}

.autocomplete-item.active {
background-color: #287dff;
color: white;
}

.autocomplete-hidden {
display: none;
}

#physical-buttons {
display: flex;
flex-direction: row;
Expand Down