diff --git a/AGENTS.md b/AGENTS.md index 8ae14ea..a7fb234 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,14 @@ After synchronization, check whether the reported issue still exists and inspect Cthulhu is a Go Linux screen reader. `cmd/` contains executables; `internal/` owns reader subsystems. Embedded audio lives in `assets/sounds/`, Tentacle libraries in `sdk/`, native harnesses in `tests/`, and parity records in `testdata/`. Go tests sit beside implementation files. Arch packaging lives in `distro-packages/Arch-Linux/cthulhu-git/`. +Reader-independent Go code shared with Fenrir lives in +`git.stormux.org/storm/screenreader-common`; check it before duplicating pure +data or behavior. The emoji catalog is shared there. Keep Cthulhu's picker form, +AT-SPI insertion, clipboard choice, speech, and Braille integration here. For +shared changes, test the common package, tag a version, and update the pinned +module in `go.mod`; keep local `go.work` or `replace` paths out of commits. +The common module must remain compatible with Cthulhu's Go minimum. + ## Build, Test, and Development Commands Use Go 1.24+ with CGO and development libraries listed by the Arch `PKGBUILD`. diff --git a/go.mod b/go.mod index 45dad23..5568516 100644 --- a/go.mod +++ b/go.mod @@ -7,3 +7,5 @@ require github.com/pelletier/go-toml/v2 v2.4.3 require github.com/godbus/dbus/v5 v5.1.0 require github.com/gorilla/websocket v1.5.3 + +require git.stormux.org/storm/screenreader-common v0.1.1 diff --git a/go.sum b/go.sum index 6bfd03e..c7b3142 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +git.stormux.org/storm/screenreader-common v0.1.1 h1:r/qcWUv4ODZ+XzXw1J+qe4hVnyr7MqH0W7L5tp6XOcE= +git.stormux.org/storm/screenreader-common v0.1.1/go.mod h1:iLmD0KXRvR4ar3U80JMwbaNUOyR82/gY7xHUY+zeIoY= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= diff --git a/internal/emoji/catalog.go b/internal/emoji/catalog.go index 9a9f41a..0bb1f74 100644 --- a/internal/emoji/catalog.go +++ b/internal/emoji/catalog.go @@ -1,555 +1,34 @@ -// Emoji catalog adapted from Fenrir for Cthulhu. // Copyright (c) 2026 Storm Dragon +// Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later -// Package emoji provides the bundled picker catalog and its internal UI form. +// Package emoji adapts the shared catalog to Cthulhu's internal UI form. package emoji import ( "sort" "git.stormux.org/storm/cthulhu/internal/interaction" + commonemoji "git.stormux.org/storm/screenreader-common/emoji" ) const MenuTitle = "Emoji Menu" -// Catalog values originate in Python Fenrir commands/vmenu-profiles/KEY/emoji. -// Duplicate aliases are omitted; names describe the stored Unicode sequence. -var categories = []struct { - id, label string - choices []interaction.Item -}{ - {"people", "People", []interaction.Item{ - {ID: "๐Ÿ˜ ", Label: "Angry"}, - {ID: "๐Ÿ˜", Label: "Beaming"}, - {ID: "๐Ÿ˜˜", Label: "Blowing kiss"}, - {ID: "๐Ÿ˜Ž", Label: "Cool"}, - {ID: "๐Ÿ˜ญ", Label: "Crying"}, - {ID: "๐Ÿ˜ˆ", Label: "Devil"}, - {ID: "๐Ÿ˜ต", Label: "Dizzy"}, - {ID: "๐Ÿฆต", Label: "Leg"}, - {ID: "๐Ÿง‘โ€๐Ÿš€", Label: "Astronaut"}, - {ID: "๐Ÿคฏ", Label: "Exploding head"}, - {ID: "๐Ÿคฌ", Label: "Face with symbols"}, - {ID: "๐Ÿ˜€", Label: "Grinning"}, - {ID: "๐Ÿ˜…", Label: "Grinning sweat"}, - {ID: "๐Ÿ˜", Label: "Heart eyes"}, - {ID: "๐Ÿฅฐ", Label: "Hearts around"}, - {ID: "๐Ÿ‘ฟ", Label: "Imp"}, - {ID: "๐Ÿ˜‚", Label: "Laughing"}, - {ID: "๐Ÿคข", Label: "Nauseated"}, - {ID: "๐Ÿฅบ", Label: "Pleading"}, - {ID: "๐Ÿ’ฉ", Label: "Poop"}, - {ID: "๐Ÿคฃ", Label: "Rolling laughing"}, - {ID: "๐Ÿ˜ข", Label: "Sad"}, - {ID: "๐Ÿ˜‹", Label: "Savoring food"}, - {ID: "๐Ÿ˜ฑ", Label: "Screaming"}, - {ID: "๐Ÿ˜Š", Label: "Smiling"}, - {ID: "๐Ÿ˜", Label: "Smirking"}, - {ID: "๐Ÿคฉ", Label: "Star-struck"}, - {ID: "๐Ÿ‘", Label: "Thumbs up"}, - {ID: "๐Ÿ˜‰", Label: "Winking"}, - {ID: "๐Ÿง‘โ€๐Ÿ’ป", Label: "Technologist"}, - {ID: "๐Ÿฅณ", Label: "Partying face"}, - {ID: "๐Ÿค”", Label: "Thinking face"}, - {ID: "๐Ÿซ ", Label: "Melting face"}, - {ID: "๐Ÿฅฒ", Label: "Smiling face with tear"}, - {ID: "๐Ÿค ", Label: "Cowboy hat face"}, - {ID: "๐Ÿฅฑ", Label: "Yawning face"}, - {ID: "๐Ÿซก", Label: "Saluting face"}, - {ID: "๐Ÿค", Label: "Handshake"}, - {ID: "๐Ÿ‘", Label: "Clapping hands"}, - {ID: "๐Ÿ™Œ", Label: "Raising hands"}, - {ID: "๐Ÿซถ", Label: "Heart hands"}, - {ID: "๐Ÿ‘Œ", Label: "OK hand"}, - {ID: "๐Ÿ‘Ž", Label: "Thumbs down"}, - {ID: "๐Ÿ’ช", Label: "Flexed biceps"}, - {ID: "๐Ÿฆถ", Label: "Foot"}, - {ID: "๐Ÿ‘ถ", Label: "Baby"}, - {ID: "๐Ÿ‘ฝ", Label: "Alien"}, - {ID: "๐Ÿ‘ผ", Label: "Baby Angel"}, - {ID: "๐Ÿ‘ˆ", Label: "Backhand Index Pointing Left"}, - {ID: "๐Ÿ‘‰", Label: "Backhand Index Pointing Right"}, - {ID: "๐Ÿค™", Label: "Call Me Hand"}, - {ID: "๐Ÿคก", Label: "Clown Face"}, - {ID: "๐Ÿคž", Label: "Crossed Fingers"}, - {ID: "๐Ÿง", Label: "Elf"}, - {ID: "๐Ÿง", Label: "Face With Monocle"}, - {ID: "๐Ÿ˜›", Label: "Face With Tongue"}, - {ID: "๐Ÿงš", Label: "Fairy"}, - {ID: "๐Ÿงž", Label: "Genie"}, - {ID: "๐Ÿ‘ป", Label: "Ghost"}, - {ID: "๐Ÿ‘บ", Label: "Goblin"}, - {ID: "๐Ÿ˜ƒ", Label: "Grinning Face With Big Eyes"}, - {ID: "๐Ÿ˜„", Label: "Grinning Face With Smiling Eyes"}, - {ID: "๐ŸคŸ", Label: "Love You Gesture"}, - {ID: "๐Ÿง™", Label: "Mage"}, - {ID: "๐Ÿงœ", Label: "Merperson"}, - {ID: "๐Ÿคถ", Label: "Mrs. Claus"}, - {ID: "๐Ÿค“", Label: "Nerd Face"}, - {ID: "๐Ÿ‘น", Label: "Ogre"}, - {ID: "๐ŸคŒ", Label: "Pinched Fingers"}, - {ID: "๐Ÿค", Label: "Pinching Hand"}, - {ID: "๐Ÿคš", Label: "Raised Back Of Hand"}, - {ID: "โœ‹", Label: "Raised Hand"}, - {ID: "๐Ÿค–", Label: "Robot"}, - {ID: "๐Ÿ™‚", Label: "Slightly Smiling Face"}, - {ID: "๐Ÿ˜‡", Label: "Smiling Face With Halo"}, - {ID: "๐Ÿฆธ", Label: "Superhero"}, - {ID: "๐Ÿฆน", Label: "Supervillain"}, - {ID: "๐Ÿ™ƒ", Label: "Upside Down Face"}, - {ID: "๐Ÿง›", Label: "Vampire"}, - {ID: "๐Ÿ––", Label: "Vulcan Salute"}, - {ID: "๐Ÿ‘‹", Label: "Waving Hand"}, - {ID: "๐Ÿคช", Label: "Zany Face"}, - {ID: "๐ŸงŸ", Label: "Zombie"}, - {ID: "๐Ÿคญ", Label: "Face With Hand Over Mouth"}, - {ID: "๐Ÿ‘‚", Label: "Ear"}, - {ID: "๐Ÿ‘ƒ", Label: "Nose"}, - {ID: "๐Ÿ‘ฃ", Label: "Footprints"}, - {ID: "๐Ÿ‘€", Label: "Eyes"}, - {ID: "๐Ÿง ", Label: "Brain"}, - {ID: "๐Ÿฆท", Label: "Tooth"}, - {ID: "๐Ÿฆพ", Label: "Mechanical Arm"}, - {ID: "๐Ÿฆฟ", Label: "Mechanical Leg"}, - }}, - {"nature", "Nature", []interaction.Item{ - {ID: "๐Ÿฆ‹", Label: "Butterfly"}, - {ID: "๐Ÿฑ", Label: "Cat"}, - {ID: "๐ŸŒธ", Label: "Cherry blossom"}, - {ID: "๐Ÿถ", Label: "Dog"}, - {ID: "๐ŸŒ™", Label: "Moon"}, - {ID: "๐ŸŒˆ", Label: "Rainbow"}, - {ID: "๐ŸŒน", Label: "Rose"}, - {ID: "โ˜€๏ธ", Label: "Sun"}, - {ID: "๐ŸŒป", Label: "Sunflower"}, - {ID: "๐ŸŒณ", Label: "Tree"}, - {ID: "๐Ÿบ", Label: "Wolf"}, - {ID: "๐Ÿ", Label: "Bee"}, - {ID: "๐Ÿž", Label: "Lady beetle"}, - {ID: "๐Ÿข", Label: "Turtle"}, - {ID: "๐Ÿ", Label: "Snake"}, - {ID: "๐Ÿ™", Label: "Octopus"}, - {ID: "๐Ÿฌ", Label: "Dolphin"}, - {ID: "๐Ÿณ", Label: "Spouting whale"}, - {ID: "๐Ÿ ", Label: "Tropical fish"}, - {ID: "๐Ÿฆˆ", Label: "Shark"}, - {ID: "๐Ÿ˜", Label: "Elephant"}, - {ID: "๐Ÿฆ’", Label: "Giraffe"}, - {ID: "๐Ÿฆ“", Label: "Zebra"}, - {ID: "๐ŸฆŒ", Label: "Deer"}, - {ID: "๐Ÿด", Label: "Horse face"}, - {ID: "๐Ÿฎ", Label: "Cow face"}, - {ID: "๐Ÿท", Label: "Pig face"}, - {ID: "๐Ÿญ", Label: "Mouse face"}, - {ID: "๐Ÿน", Label: "Hamster"}, - {ID: "๐ŸฆŠ", Label: "Fox"}, - {ID: "๐Ÿป", Label: "Bear"}, - {ID: "๐Ÿฆก", Label: "Badger"}, - {ID: "๐Ÿฆ‡", Label: "Bat"}, - {ID: "๐Ÿฆซ", Label: "Beaver"}, - {ID: "๐Ÿ—", Label: "Boar"}, - {ID: "๐Ÿช", Label: "Camel"}, - {ID: "๐Ÿฟ๏ธ", Label: "Chipmunk"}, - {ID: "๐Ÿ„", Label: "Cow"}, - {ID: "๐Ÿ•", Label: "Dog"}, - {ID: "๐Ÿ‘", Label: "Ewe"}, - {ID: "๐Ÿ", Label: "Goat"}, - {ID: "๐Ÿฆ", Label: "Gorilla"}, - {ID: "๐Ÿฆฎ", Label: "Guide Dog"}, - {ID: "๐Ÿฆ”", Label: "Hedgehog"}, - {ID: "๐Ÿฆ›", Label: "Hippopotamus"}, - {ID: "๐Ÿฆ˜", Label: "Kangaroo"}, - {ID: "๐Ÿจ", Label: "Koala"}, - {ID: "๐Ÿ†", Label: "Leopard"}, - {ID: "๐Ÿฆ", Label: "Lion"}, - {ID: "๐Ÿฆ™", Label: "Llama"}, - {ID: "๐Ÿต", Label: "Monkey Face"}, - {ID: "๐Ÿฆง", Label: "Orangutan"}, - {ID: "๐Ÿฆฆ", Label: "Otter"}, - {ID: "๐Ÿ‚", Label: "Ox"}, - {ID: "๐Ÿผ", Label: "Panda"}, - {ID: "๐Ÿพ", Label: "Paw Prints"}, - {ID: "๐Ÿฉ", Label: "Poodle"}, - {ID: "๐Ÿฆ", Label: "Raccoon"}, - {ID: "๐Ÿ", Label: "Ram"}, - {ID: "๐Ÿ€", Label: "Rat"}, - {ID: "๐Ÿฆ", Label: "Rhinoceros"}, - {ID: "๐Ÿ•โ€๐Ÿฆบ", Label: "Service Dog"}, - {ID: "๐Ÿฆจ", Label: "Skunk"}, - {ID: "๐Ÿฆฅ", Label: "Sloth"}, - {ID: "๐Ÿฏ", Label: "Tiger Face"}, - {ID: "๐Ÿฆ„", Label: "Unicorn"}, - {ID: "๐Ÿƒ", Label: "Water Buffalo"}, - {ID: "๐Ÿฆข", Label: "Swan"}, - {ID: "๐Ÿฆ‰", Label: "Owl"}, - {ID: "๐Ÿฆš", Label: "Peacock"}, - {ID: "๐Ÿฆœ", Label: "Parrot"}, - {ID: "๐Ÿ•Š๏ธ", Label: "Dove"}, - {ID: "๐Ÿฆ…", Label: "Eagle"}, - {ID: "๐Ÿฆ†", Label: "Duck"}, - {ID: "๐Ÿธ", Label: "Frog"}, - {ID: "๐ŸŠ", Label: "Crocodile"}, - {ID: "๐ŸฆŽ", Label: "Lizard"}, - {ID: "๐Ÿฒ", Label: "Dragon Face"}, - {ID: "๐Ÿ‰", Label: "Dragon"}, - {ID: "๐Ÿ‹", Label: "Whale"}, - {ID: "๐ŸŸ", Label: "Fish"}, - {ID: "๐Ÿก", Label: "Blowfish"}, - {ID: "๐ŸŒ", Label: "Snail"}, - {ID: "๐Ÿ›", Label: "Bug"}, - {ID: "๐Ÿœ", Label: "Ant"}, - {ID: "๐Ÿชฒ", Label: "Beetle"}, - {ID: "๐Ÿฆ—", Label: "Cricket"}, - {ID: "๐Ÿฆ‚", Label: "Scorpion"}, - {ID: "๐ŸฆŸ", Label: "Mosquito"}, - {ID: "๐Ÿฆ ", Label: "Microbe"}, - {ID: "๐Ÿ’", Label: "Bouquet"}, - {ID: "๐Ÿ’ฎ", Label: "White Flower"}, - {ID: "๐Ÿต๏ธ", Label: "Rosette"}, - {ID: "๐Ÿฅ€", Label: "Wilted Flower"}, - {ID: "๐ŸŒบ", Label: "Hibiscus"}, - {ID: "๐ŸŒผ", Label: "Blossom"}, - {ID: "๐ŸŒท", Label: "Tulip"}, - {ID: "๐ŸŒฑ", Label: "Seedling"}, - {ID: "๐ŸŒฒ", Label: "Evergreen Tree"}, - {ID: "๐ŸŒด", Label: "Palm Tree"}, - {ID: "๐ŸŒต", Label: "Cactus"}, - {ID: "๐ŸŒพ", Label: "Sheaf Of Rice"}, - {ID: "๐ŸŒฟ", Label: "Herb"}, - {ID: "๐Ÿ€", Label: "Four Leaf Clover"}, - {ID: "๐Ÿ", Label: "Maple Leaf"}, - {ID: "๐Ÿ‚", Label: "Fallen Leaf"}, - {ID: "๐Ÿƒ", Label: "Leaf Fluttering In Wind"}, - {ID: "๐Ÿ„", Label: "Mushroom"}, - {ID: "๐ŸŒฐ", Label: "Chestnut"}, - {ID: "๐ŸŒ‘", Label: "New Moon"}, - {ID: "๐ŸŒ”", Label: "Waxing Gibbous Moon"}, - {ID: "๐ŸŒ•", Label: "Full Moon"}, - {ID: "๐ŸŒž", Label: "Sun With Face"}, - {ID: "โ˜๏ธ", Label: "Cloud"}, - {ID: "๐ŸŒช๏ธ", Label: "Tornado"}, - {ID: "๐ŸŒฌ๏ธ", Label: "Wind Face"}, - {ID: "โ˜”", Label: "Umbrella With Rain Drops"}, - {ID: "โ„๏ธ", Label: "Snowflake"}, - {ID: "โ˜„๏ธ", Label: "Comet"}, - {ID: "๐Ÿ’ง", Label: "Droplet"}, - {ID: "๐ŸŒŠ", Label: "Water Wave"}, - }}, - {"food", "Food", []interaction.Item{ - {ID: "๐ŸŽ", Label: "Apple"}, - {ID: "๐Ÿฅ‘", Label: "Avocado"}, - {ID: "๐Ÿบ", Label: "Beer"}, - {ID: "๐ŸŽ‚", Label: "Birthday cake"}, - {ID: "โ˜•", Label: "Coffee"}, - {ID: "๐Ÿฉ", Label: "Donut"}, - {ID: "๐Ÿ”", Label: "Hamburger"}, - {ID: "๐Ÿ•", Label: "Pizza"}, - {ID: "๐Ÿ“", Label: "Strawberry"}, - {ID: "๐ŸŒฎ", Label: "Taco"}, - {ID: "๐Ÿ", Label: "Pear"}, - {ID: "๐ŸŠ", Label: "Tangerine"}, - {ID: "๐Ÿ‹", Label: "Lemon"}, - {ID: "๐ŸŒ", Label: "Banana"}, - {ID: "๐Ÿ‰", Label: "Watermelon"}, - {ID: "๐Ÿ‡", Label: "Grapes"}, - {ID: "๐Ÿ’", Label: "Cherries"}, - {ID: "๐Ÿ‘", Label: "Peach"}, - {ID: "๐Ÿฅญ", Label: "Mango"}, - {ID: "๐Ÿ", Label: "Pineapple"}, - {ID: "๐Ÿฅฅ", Label: "Coconut"}, - {ID: "๐Ÿฅ", Label: "Kiwi fruit"}, - {ID: "๐Ÿ…", Label: "Tomato"}, - {ID: "๐Ÿฅ•", Label: "Carrot"}, - {ID: "๐ŸŒฝ", Label: "Corn"}, - {ID: "๐Ÿฅ”", Label: "Potato"}, - {ID: "๐Ÿž", Label: "Bread"}, - {ID: "๐Ÿฅ", Label: "Croissant"}, - {ID: "๐Ÿฅž", Label: "Pancakes"}, - {ID: "๐Ÿง€", Label: "Cheese"}, - {ID: "๐Ÿฅ–", Label: "Baguette Bread"}, - {ID: "๐Ÿฑ", Label: "Bento Box"}, - {ID: "๐Ÿซ", Label: "Blueberries"}, - {ID: "๐Ÿฅฆ", Label: "Broccoli"}, - {ID: "๐Ÿงˆ", Label: "Butter"}, - {ID: "๐Ÿฅซ", Label: "Canned Food"}, - {ID: "๐Ÿ›", Label: "Curry Rice"}, - {ID: "๐Ÿก", Label: "Dango"}, - {ID: "๐ŸฅŸ", Label: "Dumpling"}, - {ID: "๐Ÿ†", Label: "Eggplant"}, - {ID: "๐Ÿฅ ", Label: "Fortune Cookie"}, - {ID: "๐Ÿค", Label: "Fried Shrimp"}, - {ID: "๐Ÿ", Label: "Green Apple"}, - {ID: "๐Ÿฅ—", Label: "Green Salad"}, - {ID: "๐Ÿฆž", Label: "Lobster"}, - {ID: "๐Ÿฅฎ", Label: "Moon Cake"}, - {ID: "๐Ÿข", Label: "Oden"}, - {ID: "๐Ÿฆช", Label: "Oyster"}, - {ID: "๐Ÿฟ", Label: "Popcorn"}, - {ID: "๐Ÿฅจ", Label: "Pretzel"}, - {ID: "๐Ÿ™", Label: "Rice Ball"}, - {ID: "๐Ÿ˜", Label: "Rice Cracker"}, - {ID: "๐Ÿ ", Label: "Roasted Sweet Potato"}, - {ID: "๐Ÿง‚", Label: "Salt"}, - {ID: "๐Ÿ", Label: "Spaghetti"}, - {ID: "๐Ÿœ", Label: "Steaming Bowl"}, - {ID: "๐Ÿฃ", Label: "Sushi"}, - {ID: "๐Ÿฅก", Label: "Takeout Box"}, - {ID: "๐Ÿฅฏ", Label: "Bagel"}, - {ID: "๐Ÿง‡", Label: "Waffle"}, - {ID: "๐Ÿฅ˜", Label: "Shallow Pan Of Food"}, - {ID: "๐Ÿซ•", Label: "Fondue"}, - {ID: "๐Ÿจ", Label: "Ice Cream"}, - {ID: "๐Ÿง", Label: "Shaved Ice"}, - {ID: "๐Ÿช", Label: "Cookie"}, - {ID: "๐Ÿง", Label: "Cupcake"}, - {ID: "๐Ÿฅง", Label: "Pie"}, - {ID: "๐Ÿซ", Label: "Chocolate Bar"}, - {ID: "๐Ÿฌ", Label: "Candy"}, - {ID: "๐Ÿญ", Label: "Lollipop"}, - {ID: "๐Ÿฎ", Label: "Custard"}, - {ID: "๐Ÿฏ", Label: "Honey Pot"}, - {ID: "๐Ÿผ", Label: "Baby Bottle"}, - {ID: "๐Ÿฅ›", Label: "Glass Of Milk"}, - {ID: "๐Ÿถ", Label: "Sake"}, - {ID: "๐Ÿท", Label: "Wine Glass"}, - {ID: "๐Ÿธ", Label: "Cocktail Glass"}, - {ID: "๐Ÿน", Label: "Tropical Drink"}, - {ID: "๐Ÿฅ‚", Label: "Clinking Glasses"}, - {ID: "๐Ÿฅƒ", Label: "Tumbler Glass"}, - {ID: "๐Ÿฅค", Label: "Cup With Straw"}, - }}, - {"activity", "Activity", []interaction.Item{ - {ID: "โšฝ", Label: "Soccer ball"}, - {ID: "๐Ÿ€", Label: "Basketball"}, - {ID: "๐Ÿˆ", Label: "American football"}, - {ID: "โšพ", Label: "Baseball"}, - {ID: "๐ŸŽพ", Label: "Tennis ball"}, - {ID: "๐Ÿ", Label: "Volleyball"}, - {ID: "๐Ÿฅ", Label: "Flying disc"}, - {ID: "๐Ÿ‰", Label: "Rugby football"}, - {ID: "๐ŸŽฑ", Label: "Pool 8 ball"}, - {ID: "๐Ÿ“", Label: "Table tennis"}, - {ID: "๐Ÿธ", Label: "Badminton"}, - {ID: "๐Ÿ’", Label: "Ice hockey"}, - {ID: "๐Ÿฅ…", Label: "Goal net"}, - {ID: "๐Ÿฅ", Label: "Lacrosse"}, - {ID: "๐Ÿ", Label: "Cricket game"}, - {ID: "๐Ÿ›น", Label: "Skateboard"}, - {ID: "๐Ÿ›ท", Label: "Sled"}, - {ID: "๐Ÿ†", Label: "Trophy"}, - {ID: "๐ŸŽฎ", Label: "Video game"}, - {ID: "๐ŸŽฏ", Label: "Direct hit"}, - {ID: "๐Ÿฅ‡", Label: "1st Place Medal"}, - {ID: "๐Ÿฅˆ", Label: "2nd Place Medal"}, - {ID: "๐Ÿฅ‰", Label: "3rd Place Medal"}, - {ID: "๐ŸฅŒ", Label: "Curling Stone"}, - {ID: "๐Ÿคฟ", Label: "Diving Mask"}, - {ID: "๐ŸŽฃ", Label: "Fishing Pole"}, - {ID: "โ›ธ๏ธ", Label: "Ice Skate"}, - {ID: "๐Ÿช", Label: "Kite"}, - {ID: "๐ŸŒ๏ธ", Label: "Person Golfing"}, - {ID: "๐Ÿ„", Label: "Person Surfing"}, - {ID: "๐ŸŠ", Label: "Person Swimming"}, - {ID: "๐Ÿ›ผ", Label: "Roller Skate"}, - {ID: "๐ŸŽฝ", Label: "Running Shirt"}, - {ID: "โ›ท๏ธ", Label: "Skier"}, - {ID: "๐ŸŽฟ", Label: "Skis"}, - {ID: "๐Ÿ”๏ธ", Label: "Snow Capped Mountain"}, - {ID: "๐Ÿ‚", Label: "Snowboarder"}, - {ID: "๐Ÿ…", Label: "Sports Medal"}, - }}, - {"travel", "Travel", []interaction.Item{ - {ID: "๐Ÿš—", Label: "Automobile"}, - {ID: "๐ŸšŒ", Label: "Bus"}, - {ID: "๐Ÿš•", Label: "Taxi"}, - {ID: "๐Ÿš“", Label: "Police car"}, - {ID: "๐Ÿš‘", Label: "Ambulance"}, - {ID: "๐Ÿš’", Label: "Fire engine"}, - {ID: "๐Ÿšฒ", Label: "Bicycle"}, - {ID: "๐Ÿš€", Label: "Rocket"}, - {ID: "โœˆ๏ธ", Label: "Airplane"}, - {ID: "โš“", Label: "Anchor"}, - {ID: "๐Ÿ›ซ", Label: "Airplane Departure"}, - {ID: "๐Ÿ›บ", Label: "Auto Rickshaw"}, - {ID: "๐Ÿ›ถ", Label: "Canoe"}, - {ID: "๐Ÿšง", Label: "Construction"}, - {ID: "๐Ÿš", Label: "Helicopter"}, - {ID: "๐Ÿ›ด", Label: "Kick Scooter"}, - {ID: "๐Ÿšˆ", Label: "Light Rail"}, - {ID: "๐Ÿš", Label: "Minibus"}, - {ID: "๐Ÿš", Label: "Monorail"}, - {ID: "๐Ÿ๏ธ", Label: "Motorcycle"}, - {ID: "๐Ÿš", Label: "Oncoming Bus"}, - {ID: "๐Ÿ›ป", Label: "Pickup Truck"}, - {ID: "๐Ÿšƒ", Label: "Railway Car"}, - {ID: "โ›ต", Label: "Sailboat"}, - {ID: "๐Ÿšข", Label: "Ship"}, - {ID: "๐Ÿ›ฉ๏ธ", Label: "Small Airplane"}, - {ID: "๐Ÿšค", Label: "Speedboat"}, - {ID: "๐Ÿš‰", Label: "Station"}, - {ID: "๐Ÿšœ", Label: "Tractor"}, - {ID: "๐Ÿš†", Label: "Train"}, - {ID: "๐ŸšŽ", Label: "Trolleybus"}, - }}, - {"objects", "Objects", []interaction.Item{ - {ID: "โŒš", Label: "Watch"}, - {ID: "๐Ÿ“ฑ", Label: "Mobile phone"}, - {ID: "๐Ÿ’ป", Label: "Laptop"}, - {ID: "๐Ÿ–ฅ๏ธ", Label: "Desktop computer"}, - {ID: "๐Ÿ–จ๏ธ", Label: "Printer"}, - {ID: "โŒจ๏ธ", Label: "Keyboard"}, - {ID: "๐Ÿ–ฑ๏ธ", Label: "Computer mouse"}, - {ID: "๐Ÿ’พ", Label: "Floppy disk"}, - {ID: "๐Ÿ’ฟ", Label: "Optical disk"}, - {ID: "๐ŸŽฅ", Label: "Movie camera"}, - {ID: "๐Ÿงฎ", Label: "Abacus"}, - {ID: "๐Ÿช—", Label: "Accordion"}, - {ID: "๐Ÿช•", Label: "Banjo"}, - {ID: "๐Ÿ”‹", Label: "Battery"}, - {ID: "๐Ÿ“ท", Label: "Camera"}, - {ID: "๐Ÿ“ธ", Label: "Camera With Flash"}, - {ID: "๐Ÿ•ฏ๏ธ", Label: "Candle"}, - {ID: "๐ŸŽฌ", Label: "Clapper Board"}, - {ID: "๐Ÿช”", Label: "Diya Lamp"}, - {ID: "๐Ÿฅ", Label: "Drum"}, - {ID: "๐Ÿ“€", Label: "Dvd"}, - {ID: "๐Ÿ”Œ", Label: "Electric Plug"}, - {ID: "๐Ÿ“ฝ๏ธ", Label: "Film Projector"}, - {ID: "๐Ÿงฏ", Label: "Fire Extinguisher"}, - {ID: "๐Ÿ”ฆ", Label: "Flashlight"}, - {ID: "๐ŸŽธ", Label: "Guitar"}, - {ID: "๐ŸŽง", Label: "Headphone"}, - {ID: "๐Ÿ’ก", Label: "Light Bulb"}, - {ID: "๐Ÿช˜", Label: "Long Drum"}, - {ID: "๐ŸŽค", Label: "Microphone"}, - {ID: "๐ŸŽน", Label: "Musical Keyboard"}, - {ID: "๐ŸŽผ", Label: "Musical Score"}, - {ID: "๐Ÿ›ข๏ธ", Label: "Oil Drum"}, - {ID: "๐Ÿ“ป", Label: "Radio"}, - {ID: "๐ŸŽท", Label: "Saxophone"}, - {ID: "๐Ÿ›๏ธ", Label: "Shopping Bags"}, - {ID: "๐Ÿ›’", Label: "Shopping Cart"}, - {ID: "โ˜Ž๏ธ", Label: "Telephone"}, - {ID: "๐Ÿ“บ", Label: "Television"}, - {ID: "๐ŸŽบ", Label: "Trumpet"}, - {ID: "๐Ÿ“น", Label: "Video Camera"}, - {ID: "๐ŸŽป", Label: "Violin"}, - {ID: "๐Ÿ‘“", Label: "Glasses"}, - {ID: "๐Ÿ•ถ๏ธ", Label: "Sunglasses"}, - {ID: "๐Ÿฅฝ", Label: "Goggles"}, - {ID: "๐Ÿฅผ", Label: "Lab Coat"}, - {ID: "๐Ÿฆบ", Label: "Safety Vest"}, - {ID: "๐Ÿ‘”", Label: "Necktie"}, - {ID: "๐Ÿ‘–", Label: "Jeans"}, - {ID: "๐Ÿงฃ", Label: "Scarf"}, - {ID: "๐Ÿงค", Label: "Gloves"}, - {ID: "๐Ÿงฅ", Label: "Coat"}, - {ID: "๐Ÿงฆ", Label: "Socks"}, - {ID: "๐Ÿฅพ", Label: "Hiking Boot"}, - }}, - {"holidays", "Holidays", []interaction.Item{ - {ID: "๐Ÿ‘พ", Label: "Alien monster"}, - {ID: "๐Ÿˆโ€โฌ›", Label: "Black cat"}, - {ID: "๐Ÿฐ", Label: "Bunny"}, - {ID: "๐ŸŽ„", Label: "Christmas tree"}, - {ID: "โšฐ๏ธ", Label: "Coffin"}, - {ID: "๐Ÿฅš", Label: "Egg"}, - {ID: "๐ŸŽ†", Label: "Fireworks"}, - {ID: "๐ŸŽ", Label: "Gift"}, - {ID: "๐ŸŽƒ", Label: "Jack o'lantern"}, - {ID: "๐ŸŽ…", Label: "Santa"}, - {ID: "โ˜˜๏ธ", Label: "Shamrock"}, - {ID: "๐Ÿ’€", Label: "Skull"}, - {ID: "โ›„", Label: "Snowman"}, - {ID: "๐Ÿ•ท", Label: "Spider"}, - {ID: "๐Ÿฆƒ", Label: "Turkey"}, - {ID: "๐Ÿ•ธ๏ธ", Label: "Web"}, - {ID: "๐Ÿง™โ€โ™€๏ธ", Label: "Witch"}, - {ID: "๐Ÿงจ", Label: "Firecracker"}, - {ID: "๐ŸŽŠ", Label: "Confetti ball"}, - {ID: "๐ŸŽ‰", Label: "Party popper"}, - {ID: "๐ŸŽˆ", Label: "Balloon"}, - {ID: "๐ŸŽ€", Label: "Ribbon"}, - {ID: "๐Ÿช„", Label: "Magic wand"}, - {ID: "๐Ÿ”ฎ", Label: "Crystal ball"}, - {ID: "๐Ÿช…", Label: "Piรฑata"}, - {ID: "๐Ÿชฆ", Label: "Headstone"}, - }}, - {"symbols", "Symbols", []interaction.Item{ - {ID: "๐Ÿฆด", Label: "Bone"}, - {ID: "โ›“๏ธ", Label: "Chains"}, - {ID: "โœ…", Label: "Checkmark"}, - {ID: "๐Ÿ—ก๏ธ", Label: "Dagger"}, - {ID: "๐Ÿ”ฅ", Label: "Fire"}, - {ID: "๐Ÿ™", Label: "Folded hands"}, - {ID: "โค๏ธ", Label: "Heart"}, - {ID: "โšก", Label: "High voltage"}, - {ID: "๐Ÿ’ฏ", Label: "Hundred points"}, - {ID: "โœŒ๏ธ", Label: "Peace"}, - {ID: "๐Ÿค˜", Label: "Rock on"}, - {ID: "โ˜ ๏ธ", Label: "Skull crossbones"}, - {ID: "โœจ", Label: "Sparkles"}, - {ID: "โญ", Label: "Star"}, - {ID: "โš”๏ธ", Label: "Sword"}, - {ID: "๐Ÿ’”", Label: "Broken heart"}, - {ID: "โค๏ธโ€๐Ÿ”ฅ", Label: "Heart on fire"}, - {ID: "โค๏ธโ€๐Ÿฉน", Label: "Mending heart"}, - {ID: "๐Ÿ’›", Label: "Yellow heart"}, - {ID: "๐Ÿ’š", Label: "Green heart"}, - {ID: "๐Ÿ’™", Label: "Blue heart"}, - {ID: "๐Ÿ’œ", Label: "Purple heart"}, - {ID: "๐Ÿ–ค", Label: "Black heart"}, - {ID: "๐ŸคŽ", Label: "Brown heart"}, - {ID: "๐Ÿค", Label: "White heart"}, - {ID: "๐Ÿงก", Label: "Orange Heart"}, - {ID: "๐Ÿฉท", Label: "Pink Heart"}, - }}, - {"flags", "Flags", []interaction.Item{ - {ID: "๐Ÿ‡ฆ๐Ÿ‡ท", Label: "Argentina"}, - {ID: "๐Ÿ‡ฆ๐Ÿ‡บ", Label: "Australia"}, - {ID: "๐Ÿ‡ง๐Ÿ‡ช", Label: "Belgium"}, - {ID: "๐Ÿ‡ง๐Ÿ‡ท", Label: "Brazil"}, - {ID: "๐Ÿ‡จ๐Ÿ‡ฆ", Label: "Canada"}, - {ID: "๐Ÿ‡จ๐Ÿ‡ณ", Label: "China"}, - {ID: "๐Ÿ‡ฉ๐Ÿ‡ฐ", Label: "Denmark"}, - {ID: "๐Ÿ‡ซ๐Ÿ‡ฎ", Label: "Finland"}, - {ID: "๐Ÿ‡ซ๐Ÿ‡ท", Label: "France"}, - {ID: "๐Ÿ‡ฉ๐Ÿ‡ช", Label: "Germany"}, - {ID: "๐Ÿ‡ฌ๐Ÿ‡ท", Label: "Greece"}, - {ID: "๐Ÿ‡ฎ๐Ÿ‡ณ", Label: "India"}, - {ID: "๐Ÿ‡ฎ๐Ÿ‡ช", Label: "Ireland"}, - {ID: "๐Ÿ‡ฎ๐Ÿ‡ฑ", Label: "Israel"}, - {ID: "๐Ÿ‡ฎ๐Ÿ‡น", Label: "Italy"}, - {ID: "๐Ÿ‡ฏ๐Ÿ‡ต", Label: "Japan"}, - {ID: "๐Ÿ‡ฒ๐Ÿ‡ฝ", Label: "Mexico"}, - {ID: "๐Ÿ‡ณ๐Ÿ‡ฑ", Label: "Netherlands"}, - {ID: "๐Ÿ‡ณ๐Ÿ‡ด", Label: "Norway"}, - {ID: "๐Ÿ‡ต๐Ÿ‡ฑ", Label: "Poland"}, - {ID: "๐Ÿ‡ต๐Ÿ‡น", Label: "Portugal"}, - {ID: "๐Ÿ‡ท๐Ÿ‡บ", Label: "Russia"}, - {ID: "๐Ÿ‡ฟ๐Ÿ‡ฆ", Label: "South Africa"}, - {ID: "๐Ÿ‡ฐ๐Ÿ‡ท", Label: "South Korea"}, - {ID: "๐Ÿ‡ช๐Ÿ‡ธ", Label: "Spain"}, - {ID: "๐Ÿ‡ธ๐Ÿ‡ช", Label: "Sweden"}, - {ID: "๐Ÿ‡จ๐Ÿ‡ญ", Label: "Switzerland"}, - {ID: "๐Ÿ‡น๐Ÿ‡ท", Label: "Turkey"}, - {ID: "๐Ÿ‡บ๐Ÿ‡ฆ", Label: "Ukraine"}, - {ID: "๐Ÿ‡ฌ๐Ÿ‡ง", Label: "United Kingdom"}, - {ID: "๐Ÿ‡บ๐Ÿ‡ธ", Label: "United States"}, - }}, -} - // NewSession builds a private picker with independent category searches. func NewSession(copyChecked bool) (*interaction.Session, error) { + sharedCategories := commonemoji.Categories() + tabs := make([]interaction.GridTab, 1, len(sharedCategories)+1) + tabs[0] = interaction.GridTab{ID: "all", Label: "All"} var all []interaction.Item - for _, category := range categories { - all = append(all, category.choices...) + for _, category := range sharedCategories { + items := make([]interaction.Item, len(category.Entries)) + for index, entry := range category.Entries { + items[index] = interaction.Item{ID: entry.ID, Label: entry.Label} + } + all = append(all, items...) + tabs = append(tabs, interaction.GridTab{ID: category.ID, Label: category.Label, Items: items}) } sort.SliceStable(all, func(i, j int) bool { return all[i].Label < all[j].Label }) - tabs := []interaction.GridTab{{ID: "all", Label: "All", Items: all}} - for _, category := range categories { - tabs = append(tabs, interaction.GridTab{ID: category.id, Label: category.label, Items: category.choices}) - } + tabs[0].Items = all return interaction.NewGridSession(MenuTitle, tabs, 6, interaction.Element{ID: "copy", Label: "Copy selected emoji to clipboard", Kind: interaction.CheckBox, Checked: copyChecked}) } diff --git a/internal/emoji/catalog_test.go b/internal/emoji/catalog_test.go index 3b18902..a730cc7 100644 --- a/internal/emoji/catalog_test.go +++ b/internal/emoji/catalog_test.go @@ -7,13 +7,14 @@ import ( "testing" "git.stormux.org/storm/cthulhu/internal/interaction" + commonemoji "git.stormux.org/storm/screenreader-common/emoji" ) func TestCatalogUniquenessAndSequences(t *testing.T) { seen := map[string]bool{} strip := strings.NewReplacer("\ufe0e", "", "\ufe0f", "") - for _, category := range categories { - for _, item := range category.choices { + for _, category := range commonemoji.Categories() { + for _, item := range category.Entries { id := strip.Replace(item.ID) if seen[id] { t.Fatalf("duplicate presentation: %q", item.ID) @@ -21,10 +22,22 @@ func TestCatalogUniquenessAndSequences(t *testing.T) { seen[id] = true } } - if len(seen) != 501 || !seen["๐Ÿง‘โ€๐Ÿš€"] || !seen["๐Ÿ"] || !seen["๐ŸŽ‰"] { + if len(seen) != 506 || !seen["๐Ÿง‘โ€๐Ÿš€"] || !seen["๐Ÿ"] || !seen["๐ŸŽ‰"] || !seen["๐Ÿ’‹"] { t.Fatalf("catalog missing entries: %d", len(seen)) } } + +func TestKissMarkSearch(t *testing.T) { + session, err := NewSession(false) + if err != nil { + t.Fatal(err) + } + session.Handle(interaction.Event{Key: interaction.Tab}) + session.Handle(interaction.Event{Text: "Kiss mark"}) + if got := session.Values()["grid"].Selected; got != "๐Ÿ’‹" { + t.Fatalf("kiss mark selection = %q", got) + } +} func TestPickerSearchSelectionAndIndependentCategories(t *testing.T) { session, err := NewSession(true) if err != nil {