-
-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Search history completions #423
Draft
MareStare
wants to merge
26
commits into
philomena-dev:master
Choose a base branch
from
MareStare:feat/search-history
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
325056a
to
729ebf6
Compare
…e bugs, don't select item on hover, just highlight it slightly. Simplify history store to use a flat string list.
…s in backend. Add autocomplete to the tags page. File names cleanup
47ee7d3
to
b413a2d
Compare
…here enter moves focus to the next input instead of submitting the form
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Before you begin
Closes #419
This is WIP, I'll add some details about the mechanics of the feature to the PR description once I'm ready, I'll request a review.
FIXME
TODO
Search History
Added support for search history recording and completions to several inputs. When the input is empty and focused we display 10 last recently used queries. If the user types at least one non-whitespace character we display at most 3 history suggestions at the top based on the prefix match.
Updated the UI look of the completions that you'll see on various screenshots below. Of the not I replaced fat arrow character
⇒
with a thin arrow→
for aliases, and to make that work I changed the font to the monospace font used in other places on the frontend.I also did an extensive rewrite of the existing autocomplete code to make it more organized and type-coherent.
Data Attribute Config
Search history can be enabled for certain
<input>
or<textarea>
elements with the new data attributedata-autocomplete-history-id={history_id}
. The{history_id}
should be some human-readable word that will be used a prefix in the local storage key, which is formatted as{history_id}-history
. I enabled this only for the main search input in the header page with thehistory_id = 'search'
, which results in asearch-history
key inlocalStorage
.Several inputs can share the same search history if the use the same
history_id
.Storage Format
The history is stored as a single JSON object with a flat list of history records of the following form in
localStorage
:The more recently used history records appear at the head of the array, and the least recently used items are at the tail. The ordering is maintained when new items are inserted or existing items are used repeatedly. I considered storing some additional useful info like
created_at
,updated_at
, number ofsubmits
etc for each record, but then decided that to be an unnecessary overhead at least at this stage.Shortucts
Form Submission
Clicking or pressing Enter on the history item submits the form right away. If the user wants to continue typing after the history item and prevent submitting the form, they can do so by using Shift+Enter or with a Shift+LMB. This is a big deviation from the tag's completions because I believe most of the time the intention of selecting the history item is to submit it immediately. This decision is arguable, but we can tweak this behavior as needed based on feedback.
An opposite shortcut Ctrl+Enter or Ctrl+LMB can now be used both with history and tag suggestions to submit the form immediately with the provided suggestion inserted.
Navigation
Ctrl+Down - Jump to the next block of suggestions. This is intended to skip history suggestions and jump to the first tag suggestion.
Ctrl+Up - Jump to the prev block of suggestions. E.g. if you are in the tag suggestions, that will jump to the last history suggestion. I don't see a real use case for this key binding, added it just for the symmetry.
Deviations from Current Behavior
Now there are tag suggestions when the cursor is right before the first character of the term:

Hovering over the item in the completions list doesn't move the selection. Instead, it renders a visual-only highlight. This behaviour was borrowed from VSCode's completion behaviour. The left side is the old implementation and the right side is the new implementation:
completions-comparison.mp4
Server-Side Completions
I enabled server-side tag completions on all inputs (including the main search). Plus I had to update its backend implementation to return a structured list of suggestions of the following form:
where previous server-side suggestions API was returning this shape of the response:
The problem with that API was that it was doing the suggestion label rendering on the backend side. So we had to keep the frontend rendering of suggestions produced by the local top-50K autocomplete index in sync with the backend which we obviously failed to do for example, when we added the change to display aliased tags with an arrow.
So the new API can now be invoked with the
vsn=2
parameter (analogous to the local autocomplete index), while the old API is left in code for backwards compatibility for some time to let users re-download the new frontend version.Debouncing and Caching
Today, there is an annoying implementation behaviour of debouncing and caching for server-side completions. We debounce user input with a 300ms threshold, then send a server-side completions request and cache its result. However, if the user changes the input and then returns back to the cached query, the user still needs to wait for a 300ms debounce threshold before the cached results are displayed.
I fixed this behaviour and also moved this logic into a generic
DebouncedCache
class. It also manages anAbortController/AbortSignal
for the server-side request, so that it can cancel it if the user typed some more characters after the server-side request was initiated. So this change eliminates this race condition, where previously the user could see stale sever-side completion results while they were typing. It isn't a critical bug though, but still nice to see it fixed.Here is an example of how it works. You can see here how two requests were aborted because I typed in the middle of the active request:

Some debug-level logs are output when this condition is experienced (ignore the

refresh
logs, they were temporary used for debugging):HTTP Client wrapper
I added a new
http-client.ts
file with a generic HTTP Client implementation that may be useful for any request from the frontend. It provides a nice API with inbuilt error handling, exponential retries with backoff and jitter, escaping of URL query parameters, and it also adds some useful diagnostic info into the request headers like theX-Request-Id
,X-Retry-Sequence-Id
andX-Retry-Attempt
.Other Changes
Bumped TS standard lib types to ES2021 to get support for Error causes
Added autocomplete to the

/tags
page. Doesn't record history. Conditionally enabled (disabled by default) for now just like the main search for images in the header.Image Count Rendering
I've decided to format the number in full with groups of 3 digits separated by a short space. I think this way of rendering looks nicer than using
M / K
contractions, since this way all the digits are aligned and it's easy to see the difference between the order of numbers between different tags. I think this way of formatting numbers in full may scale even to 100 000 000 images. Also, this way of displaying the image count will make derpibooru's completions UI style more unique (less of a copy-paste from danbooru).However, I understand that this is subjective and given enough opinions, we can change the formatting easily.
UI Extremes
Overly long suggestions are truncated with an ellipsis. The overall max width of the completions box can be at most 70% of the viewport.
Here is the demo of handling extreme suggestion / image counts lengths:
extremes-demo-1080p.mp4
Note that the position of the completions box stays static when browser is resized. It also works this way in the implementation in master, and I don't think it's a big issue. The box is rerendered next time the user types something. I haven't found a simple solution for how to fix this yet.
Future Extensions
These changes aren't implemented and are left for the scope of the followup PRs:
document.execCommand
, but that API is deprecated with no alternatives (!)