Rune finder
<details>
<summary>About this app</summary>
Find Unicode characters by name.
For example, for `face cat`
```
U+1F431 🐱 CAT FACE
U+1F638 😸 GRINNING CAT FACE WITH SMILING EYES
U+1F639 😹 CAT FACE WITH TEARS OF JOY
U+1F63A 😺 SMILING CAT FACE WITH OPEN MOUTH
U+1F63B 😻 SMILING CAT FACE WITH HEART-SHAPED EYES
U+1F63C 😼 CAT FACE WITH WRY SMILE
U+1F63D 😽 KISSING CAT FACE WITH CLOSED EYES
U+1F63E 😾 POUTING CAT FACE
U+1F63F 😿 CRYING CAT FACE
U+1F640 🙀 WEARY CAT FACE
```
</details>
Nothing here...
App source
This app is built from the following notebook source:
rune_finder.livemd
<!-- livebook:{"app_settings":{"access_type":"public","output_type":"rich","show_source":true,"slug":"rune-finder"},"file_entries":[{"name":"UnicodeData.txt","type":"url","url":"https://raw.githubusercontent.com/ramalho/rf/master/elixir/UnicodeData.txt"}]} -->
# Rune finder
```elixir
Mix.install([
{:kino, "~> 0.12.3"}
])
```
## About this
```elixir
import Kino.Shorts
```
````elixir
markdown("""
<details>
<summary>About this app</summary>
Find Unicode characters by name.
For example, for `face cat`
```
U+1F431 🐱 CAT FACE
U+1F638 😸 GRINNING CAT FACE WITH SMILING EYES
U+1F639 😹 CAT FACE WITH TEARS OF JOY
U+1F63A 😺 SMILING CAT FACE WITH OPEN MOUTH
U+1F63B 😻 SMILING CAT FACE WITH HEART-SHAPED EYES
U+1F63C 😼 CAT FACE WITH WRY SMILE
U+1F63D 😽 KISSING CAT FACE WITH CLOSED EYES
U+1F63E 😾 POUTING CAT FACE
U+1F63F 😿 CRYING CAT FACE
U+1F640 🙀 WEARY CAT FACE
```
</details>
""")
````
## Code
```elixir
content =
Kino.FS.file_path("UnicodeData.txt")
|> File.read!()
```
```elixir
defmodule RuneFinder do
defp to_rune(code, name) do
rune = <<String.to_integer(code, 16)::utf8>>
"U+#{code}\t#{rune}\t#{name}"
end
defp select(line_stream, query_words) do
Enum.map(line_stream, fn line ->
[code, name | _] = String.split(line, ";")
if MapSet.subset?(query_words, tokenize(name)), do: to_rune(code, name)
end)
|> Enum.reject(&(&1 == nil))
end
defp find(query_words) do
Kino.FS.file_path("UnicodeData.txt")
|> File.stream!()
|> select(query_words)
end
defp tokenize(text) do
text
|> String.replace("-", " ")
|> String.split()
|> MapSet.new()
end
def main(args) do
args
|> String.upcase()
|> tokenize
|> find
end
end
```
```elixir
form =
Kino.Control.form(
[
name: Kino.Input.text("Unicode characters name", default: "face cat")
],
submit: "Find"
)
```
```elixir
output_frame = frame()
Kino.listen(form, fn event ->
origin = event.origin
Kino.Frame.clear(output_frame, to: origin)
case event.data.name do
"" ->
Kino.Frame.render(output_frame, "Please provide words to find.", to: origin)
_ ->
runes = RuneFinder.main(event.data.name)
Enum.each(runes, fn rune ->
Kino.Frame.append(output_frame, text(rune), to: origin)
end)
Kino.Frame.append(output_frame, text("\n#{Enum.count(runes)} found"), to: origin)
end
end)
output_frame
```