diff --git a/frontend/src/components/EmojiIconPicker.jsx b/frontend/src/components/EmojiIconPicker.jsx
index 618cf1b..e072d44 100644
--- a/frontend/src/components/EmojiIconPicker.jsx
+++ b/frontend/src/components/EmojiIconPicker.jsx
@@ -1,5 +1,6 @@
-import { useState, useId } from 'react'
+import { useState, useId, useMemo, useEffect } from 'react'
import { ChevronDown, ChevronUp } from 'lucide-react'
+import { haystackForEmoji, matchesEmojiSearch } from './emojiIconPickerKeywords.js'
/**
* Kuratierte Emoji-Gruppen (viele Einzel-Glyphen für breite OS-Unterstützung).
@@ -222,10 +223,29 @@ export default function EmojiIconPicker({
}) {
const uid = useId()
const inputId = idProp || `emoji-icon-${uid}`
+ const searchInputId = `${inputId}-picker-search`
const [open, setOpen] = useState(defaultExpanded)
+ const [pickerSearch, setPickerSearch] = useState('')
const groups =
extraGroups.length > 0 ? [...EMOJI_ICON_GROUPS, ...extraGroups] : EMOJI_ICON_GROUPS
+ useEffect(() => {
+ if (!open) setPickerSearch('')
+ }, [open])
+
+ const filteredGroups = useMemo(() => {
+ const q = pickerSearch
+ if (!q.trim()) {
+ return groups
+ }
+ return groups
+ .map((g) => ({
+ ...g,
+ emojis: g.emojis.filter((em) => matchesEmojiSearch(haystackForEmoji(em, g.label), q))
+ }))
+ .filter((g) => g.emojis.length > 0)
+ }, [groups, pickerSearch])
+
const handleInput = (e) => {
onChange(e.target.value.slice(0, maxLength))
}
@@ -308,11 +328,40 @@ export default function EmojiIconPicker({
background: 'var(--surface2)',
borderRadius: 12,
border: '1px solid var(--border)',
- maxHeight: 'min(72vh, 420px)',
+ maxHeight: 'min(72vh, 460px)',
overflowY: 'auto'
}}
>
- {groups.map((group, gi) => (
+
+ In Vorschlägen suchen
+
+ setPickerSearch(e.target.value)}
+ placeholder="z. B. rollschuh, karate, apfel…"
+ disabled={disabled}
+ autoComplete="off"
+ spellCheck={false}
+ inputMode="search"
+ style={{ width: '100%', marginBottom: 12, boxSizing: 'border-box' }}
+ />
+ {filteredGroups.length === 0 && pickerSearch.trim() && (
+
+ Keine Treffer für „{pickerSearch.trim()}“. Andere Begriffe probieren oder oben ein Emoji
+ einfügen (z. B. Win + .).
+
+ )}
+ {filteredGroups.map((group, gi) => (
))}
-
+
Du kannst auch direkt in das Feld tippen oder das Betriebssystem-Emoji-Menü nutzen
- (z. B. Win + . unter Windows).{' '}
- Inline Skating: Es gibt kein separates Inliner-Emoji; 🛼 (Rollschuh) wird dafür oft genutzt.{' '}
+ (z. B. Win + . unter Windows). Mehrere Wörter verfeinern die Suche: jedes muss in den
+ Stichwörtern vorkommen.{' '}
+ Inline Skating: Es gibt kein separates Inliner-Emoji; 🛼 (Rollschuh) wird dafür oft genutzt – Suchbegriffe: rollschuh, inline, inliner.{' '}
Karate / Kampfsport mit Gi: 🥋 (gemeinsames Unicode-Symbol für u. a. Karate, Judo, Ju-Jitsu).
diff --git a/frontend/src/components/emojiIconPickerKeywords.js b/frontend/src/components/emojiIconPickerKeywords.js
new file mode 100644
index 0000000..e707eb4
--- /dev/null
+++ b/frontend/src/components/emojiIconPickerKeywords.js
@@ -0,0 +1,529 @@
+/**
+ * Suchbegriffe für EmojiIconPicker (kleingeschrieben, DE + ggf. EN).
+ * Gruppenüberschriften werden zusätzlich immer indexiert.
+ * @type {Record}
+ */
+export const EMOJI_KEYWORDS = {
+ // —— Rollen / Skates (wichtig für „Inline“, „Rollschuh“) ——
+ '🛼':
+ 'rollschuh rollschuhe inline inliner skaten roller quad rollschlittschuh blade blades rollerblade skates',
+
+ // —— Sport (typisch DE-Gruppe) ——
+ '⚽': 'fußball fussball soccer football',
+ '🤾': 'handball',
+ '🤾♂️': 'handball mann',
+ '🤾♀️': 'handball frau',
+ '🥋': 'karate judo ju jitsu jujitsu kampfsport gi anzug martial arts',
+ '🏐': 'volleyball beach',
+ '🏀': 'basketball',
+ '🎾': 'tennis',
+ '🏓': 'tischtennis pingpong',
+ '🏸': 'badminton federball',
+ '🏒': 'eishockey hockey',
+ '🏑': 'feldhockey hockey',
+ '🥍': 'lacrosse',
+ '🏈': 'american football football nfl',
+ '🏉': 'rugby',
+ '⚾': 'baseball',
+ '🥎': 'softball',
+ '⛹️': 'basketball person',
+ '⛹️♂️': 'basketball mann',
+ '⛹️♀️': 'basketball frau',
+ '🥅': 'tor goal netz',
+ '⛷️': 'ski skifahren skilaufen alpin',
+ '🎿': 'ski skier langlauf',
+ '🏂': 'snowboard',
+ '🛷': 'schlitten rodeln',
+ '⛸️': 'eislauf schlittschuh eisbahn kunstlauf',
+ '🥌': 'curling',
+ '🚴': 'rad fahrrad rennrad cycling',
+ '🚴♂️': 'rad fahrrad mann',
+ '🚴♀️': 'rad fahrrad frau',
+ '🚵': 'mountainbike mtb rad',
+ '🚵♂️': 'mountainbike mann',
+ '🚵♀️': 'mountainbike frau',
+ '🏃': 'laufen joggen jogging marathon laufsport',
+ '🏃♂️': 'laufen mann',
+ '🏃♀️': 'laufen frau',
+ '🏃➡️': 'laufen sprint',
+ '🚶': 'gehen spazieren walking',
+ '🥾': 'wandern hiking bergsport bergtouren',
+ '🏊': 'schwimmen schwimm sport',
+ '🏊♂️': 'schwimmen mann',
+ '🏊♀️': 'schwimmen frau',
+ '🤽': 'wasserball',
+ '🤽♂️': 'wasserball mann',
+ '🤽♀️': 'wasserball frau',
+ '🤿': 'tauchen schnorcheln diving',
+ '🏄': 'surfen wellen',
+ '🏄♂️': 'surfen mann',
+ '🏄♀️': 'surfen frau',
+ '🚣': 'rudern boot row',
+ '🚣♂️': 'rudern mann',
+ '🚣♀️': 'rudern frau',
+ '⛵': 'segeln segelboot yacht',
+ '🧗': 'klettern bouldern bergsteigen',
+ '🧗♂️': 'klettern mann',
+ '🧗♀️': 'klettern frau',
+ '🏋️': 'krafttraining gewichte fitnessstudio gym hanteln',
+ '🏋️♂️': 'krafttraining mann',
+ '🏋️♀️': 'krafttraining frau',
+ '🤸': 'turnen gymnastics',
+ '🤸♂️': 'turnen mann',
+ '🤸♀️': 'turnen frau',
+ '🥊': 'boxen boxing',
+ '🤼': 'ringen wrestling',
+ '🤺': 'fechten fencing',
+ '🏇': 'reiten pferd reitsport galopp derby',
+ '⛳': 'golf golfplatz loch',
+ '🏌️': 'golf',
+ '🏌️♂️': 'golf mann',
+ '🏌️♀️': 'golf frau',
+ '💃': 'tanzen tanz salsa tanzsport',
+ '🕺': 'tanzen mann disco',
+ '🧘': 'yoga pilates meditation entspannung',
+ '🧘♂️': 'yoga mann meditation',
+ '🧘♀️': 'yoga frau meditation',
+ '🛹': 'skateboard longboard street',
+ '🎯': 'dart darts schießen treffer zielscheibe',
+ '🎳': 'bowling kegeln',
+ '🏟️': 'stadion arena',
+ '🏆': 'pokal sieg trophy',
+ '🥇': 'gold medaille erste platz',
+ '🥈': 'silber medaille',
+ '🥉': 'bronze medaille',
+
+ // —— Weitere Sport / Hobby ——
+ '🎱': 'billard pool snooker',
+ '🎣': 'angeln fishing',
+ '🤹': 'jonglieren zirkus',
+ '🪁': 'drachen steigen lassen drachen',
+ '🥏': 'frisbee ultimate',
+ '🛶': 'kanu kayak paddeln',
+ '🏹': 'bogenschießen pfeil bogen',
+ '🌊': 'welle meer wasser',
+ '🏖️': 'strand beach',
+ '🛣️': 'straße laufen strecke',
+ '🧭': 'kompass navigation orientierung',
+ '🏕️': 'camping zelten outdoor',
+ '⛺': 'zelt camping',
+
+ // —— Yoga / Geist ——
+ '🪷': 'lotus blume meditation',
+ '☯️': 'yin yang',
+ '🕉️': 'hindu om meditation',
+ '🙏': 'beten danken namaste',
+ '🧎': 'knien',
+ '🧍': 'stehen',
+ '💭': 'gedanke idee',
+ '📿': 'gebetskette',
+ '🎼': 'noten musik',
+ '🎹': 'klavier keyboard piano',
+ '🥁': 'schlagzeug trommel',
+ '🎸': 'gitarre',
+ '🎺': 'trompete blasinstrument',
+ '🔔': 'glocke',
+ '✨': 'sterne glitzer',
+ '🌟': 'stern glanz',
+ '💫': 'schwindel meteor',
+ '🔮': 'kristallkugel',
+
+ // —— Outdoor —— (Stichworte zu Bergen/Wetter)
+ '⛰️': 'berg berge alpen',
+ '🏔️': 'schneeberg gipfel',
+ '🗻': 'fuji vulkan berg',
+ '🌋': 'vulkan lava',
+ '🗺️': 'karte landkarte',
+ '🌲': 'wald tannenbaum',
+ '🌳': 'baum baumpark',
+ '🌴': 'palme urlaub',
+ '🍃': 'blatt grün frühling',
+ '🍂': 'herbst blatt',
+ '🌿': 'kraut pflanze',
+ '☘️': 'klee glück',
+ '🪨': 'stein fels',
+ '🏞️': 'nationalpark natur',
+ '🏜️': 'wüste',
+ '🌅': 'sonnenaufgang morgenrot',
+ '🌄': 'sonne berg horizont',
+ '🌈': 'regenbogen',
+ '⛅': 'wolke wetter',
+ '🌤️': 'sonne wolke',
+ '☀️': 'sonne sonnenschein',
+ '🌙': 'mond nacht',
+ '⭐': 'stern',
+ '🌠': 'sternschnuppe',
+ '❄️': 'schnee winter frost',
+ '☃️': 'schneemann',
+ '⛄': 'schneemann kalt',
+
+ // —— Körper / Medizin ——
+ '💪': 'muskel kraft arm bizeps fit',
+ '🦾': 'prothese arm roboter',
+ '🦵': 'bein knie',
+ '🦶': 'fuß zehen',
+ '🖐️': 'hand fünf',
+ '✋': 'hand stop',
+ '👣': 'fußspuren laufen',
+ '❤️': 'herz liebe health',
+ '🩷': 'herz pink',
+ '💙': 'herz blau',
+ '💚': 'herz grün vegan gesund',
+ '🫀': 'herz organ anatomie',
+ '🫁': 'lunge atem',
+ '🧠': 'gehirn denken kognition',
+ '👁️': 'auge sehen',
+ '👂': 'ohr hören',
+ '🦷': 'zahn zahnarzt',
+ '🦴': 'knochen skelett',
+ '🧬': 'dna genetik',
+ '⚕️': 'medizin asclepius arzt',
+ '🩺': 'stethoskop arzt',
+ '🩹': 'pflaster verband',
+ '🩼': 'krücke verletzt',
+ '💊': 'tablette medikament',
+ '🌡️': 'fieberthermometer temperatur',
+ '🔬': 'mikroskop labor',
+ '🧪': 'reagenzglas chemie',
+ '🧫': 'petrischale',
+ '♿': 'rollstuhl barrierefrei',
+ '⚖️': 'waage gerechtigkeit gewicht',
+ '📏': 'lineal messen',
+ '📐': 'winkel geometrie',
+
+ // —— Ernährung (Auswahl häufige Begriffe) ——
+ '🍎': 'apfel apple obst',
+ '🍐': 'birne pear',
+ '🍊': 'orange mandarine zitrus',
+ '🍋': 'zitrone lemon',
+ '🍌': 'banane banana',
+ '🍉': 'wassermelone melone',
+ '🍇': 'trauben weintrauben',
+ '🍓': 'erdbeere',
+ '🫐': 'heidelbeeren blaubeeren',
+ '🍒': 'kirschen',
+ '🍑': 'pfirsich',
+ '🥭': 'mango',
+ '🍍': 'ananas',
+ '🥝': 'kiwi',
+ '🍅': 'tomate',
+ '🥑': 'avocado',
+ '🥦': 'brokkoli',
+ '🥬': 'salat blattspinat',
+ '🥒': 'gurke',
+ '🌶️': 'chili scharf peperoni',
+ '🫑': 'paprika',
+ '🌽': 'mais',
+ '🥕': 'möhre karotte',
+ '🫒': 'olive',
+ '🧄': 'knoblauch',
+ '🧅': 'zwiebel',
+ '🥔': 'kartoffel',
+ '🍠': 'süßkartoffel',
+ '🥐': 'croissant frühstück',
+ '🍞': 'brot toast',
+ '🥖': 'baguette',
+ '🥨': 'breze brezel',
+ '🧀': 'käse',
+ '🥚': 'ei eier',
+ '🍳': 'braten pfanne frühstück',
+ '🧈': 'butter',
+ '🥞': 'pfannkuchen pancakes',
+ '🧇': 'waffel waffle',
+ '🥓': 'speck bacon',
+ '🥩': 'steak fleisch',
+ '🍗': 'hähnchenkeule geflügel',
+ '🍖': 'fleisch knochen',
+ '🌭': 'hotdog wurst',
+ '🍔': 'burger hamburger',
+ '🍟': 'pommes fritten',
+ '🍕': 'pizza',
+ '🫓': 'fladenbrot naan',
+ '🥙': 'döner wrap',
+ '🌮': 'taco',
+ '🌯': 'burrito',
+ '🥗': 'salat bowl',
+ '🍝': 'pasta spaghetti',
+ '🍜': 'suppe nudeln ramen',
+ '🍲': 'eintopf topf',
+ '🍛': 'curry reis',
+ '🍣': 'sushi',
+ '🍱': 'bento lunchbox',
+ '🥟': 'dumpling gyoza',
+ '🦪': 'austern',
+ '🍤': 'garnelen tempura',
+ '🍙': 'onigiri reisbällchen',
+ '🍚': 'reis bowl',
+ '🍘': 'reiscracker',
+ '🍥': 'narutomaki fischkuchen',
+ '🥠': 'glückskeks',
+ '🥮': 'mondkuchen',
+ '🍢': 'oden spieß',
+ '🍡': 'dango',
+ '🍧': 'wassereis shaved ice',
+ '🍨': 'eiscreme',
+ '🍦': 'softeis eis',
+ '🥧': 'kuchen tarte pie',
+ '🧁': 'cupcake muffin',
+ '🍰': 'torte kuchen slice',
+ '🎂': 'geburtstag torte',
+ '🍮': 'pudding dessert',
+ '🍭': 'lutscher lolly',
+ '🍬': 'bonbon süßigkeit',
+ '🍫': 'schokolade riegel',
+ '🍿': 'popcorn kino',
+ '🍩': 'donut',
+ '🍪': 'keks cookie',
+ '🌰': 'kastanie',
+ '🥜': 'erdnuss nüsse',
+ '🍯': 'honig',
+ '🥛': 'milch',
+ '🍼': 'baby flasche',
+ '🫖': 'teekanne tee',
+ '☕': 'kaffee espresso',
+ '🍵': 'matcha tee grüntee',
+ '🧃': 'saft packung',
+ '🥤': 'becher strohhalm',
+ '🧋': 'bubble tea boba',
+ '🍶': 'sake',
+ '🍺': 'bier krug',
+ '🍻': 'anstoßen bier',
+ '🥂': 'sekt champagner anstoßen',
+ '🍷': 'wein glas',
+ '🥃': 'whiskey tumbler',
+ '🍸': 'cocktail martini',
+ '🍹': 'cocktail drink',
+ '🧉': 'mate',
+ '🍾': 'sekt flasche party',
+ '💧': 'wasser trinken hydrieren',
+ '🧊': 'eiswürfel kalt',
+
+ // —— Schlaf ——
+ '😴': 'schlafen müde schlaf',
+ '🛌': 'bett schlafen',
+ '🛏️': 'bett',
+ '💤': 'zzz schnarchen',
+ '🌛': 'mond halbmond',
+ '🌜': 'mond mondgesicht',
+ '💆': 'massage wellness',
+ '🧴': 'lotion cream',
+ '🛁': 'badewanne bad',
+ '🚿': 'dusche',
+ '🪥': 'zahnbürste hygiene',
+ '🩴': 'badelatschen flipflop',
+ '🕯️': 'kerze ruhe',
+
+ // —— Smileys ——
+ '😊': 'lächeln glücklich',
+ '🙂': 'leichtes lächeln',
+ '😌': 'zufrieden erleichtert',
+ '😎': 'cool sonnenbrille',
+ '🤩': 'star augen begeistert',
+ '🥳': 'party hut feier',
+ '😤': 'stolz triumphiert',
+ '💯': 'hundert prozent perfekt',
+ '🙌': 'jubel hände',
+ '👏': 'applaus klatschen',
+ '🤝': 'handschlag deal',
+ '👍': 'daumen hoch gut',
+ '👎': 'daumen runter schlecht',
+ '✊': 'faust solidarität',
+ '🤛': 'faust links',
+ '🤜': 'faust rechts',
+ '😅': 'schweiß awkward',
+ '🤔': 'nachdenken frage',
+ '🧐': 'monokel prüfen',
+ '😇': 'heilig engel',
+
+ // —— Tiere (Stichworte) ——
+ '🐶': 'hund dog',
+ '🐱': 'katze cat',
+ '🐭': 'maus mouse',
+ '🐹': 'hamster',
+ '🐰': 'hase kaninchen',
+ '🦊': 'fuchs',
+ '🐻': 'bär',
+ '🐼': 'panda',
+ '🐨': 'koala',
+ '🐯': 'tiger',
+ '🦁': 'löwe',
+ '🐮': 'kuh rind',
+ '🐷': 'schwein',
+ '🐸': 'frosch',
+ '🐵': 'affe',
+ '🐔': 'huhn',
+ '🐧': 'pinguin',
+ '🐦': 'vogel',
+ '🐤': 'küken',
+ '🦆': 'ente',
+ '🦅': 'adler',
+ '🦉': 'eule',
+ '🦇': 'fledermaus',
+ '🐺': 'wolf',
+ '🐗': 'wildschwein keiler',
+ '🐴': 'pferd pony',
+ '🦄': 'einhorn',
+ '🐝': 'biene',
+ '🐛': 'raupe',
+ '🦋': 'schmetterling',
+ '🐌': 'schnecke',
+ '🐞': 'marienkäfer',
+ '🐜': 'ameise',
+ '🦗': 'grille',
+ '🕷️': 'spinne',
+ '🦂': 'skorpion',
+ '🐢': 'schildkröte',
+ '🐍': 'schlange',
+ '🦎': 'eidechse',
+ '🦖': 't-rex dinosaurier',
+ '🦕': 'dino langhals',
+ '🐙': 'oktopus krake',
+ '🦑': 'tintenfisch',
+ '🦐': 'garnele',
+ '🦞': 'hummer',
+ '🐠': 'tropenfisch',
+ '🐟': 'fisch',
+ '🐬': 'delfin',
+ '🐳': 'wal bläst',
+ '🐋': 'wal',
+ '🦈': 'hai',
+ '🐊': 'krokodil alligator',
+
+ // —— Symbole ——
+ '📊': 'diagramm balken statistik',
+ '📈': 'chart steigend trend',
+ '📉': 'chart fallend',
+ '🧮': 'abacus rechenbrett',
+ '📋': 'clipboard checkliste',
+ '📌': 'pin pinnwand',
+ '📍': 'ort marker standort',
+ '🔖': 'lesezeichen bookmark',
+ '🏷️': 'etikett label',
+ '✏️': 'bleistift schreiben',
+ '✒️': 'feder',
+ '🖊️': 'kugelschreiber',
+ '📎': 'büroklammer',
+ '🔗': 'link verknüpfung',
+ '⛓️': 'kette',
+ '🔒': 'schloss zu sicherheit',
+ '🔓': 'schloss offen',
+ '🔑': 'schlüssel',
+ '🗝️': 'altschlüssel',
+ '🔨': 'hammer',
+ '🛠️': 'werkzeug',
+ '⚙️': 'zahnrad einstellungen',
+ '🧰': 'werkzeugkasten',
+ '💡': 'idee glühbirne lampe',
+ '🔦': 'taschenlampe',
+ '🏮': 'laterne',
+ '🪔': 'öllampe diwali',
+ '📣': 'megaphone megafon',
+ '📢': 'lautsprecher',
+ '🔔': 'benachrichtigung glocke',
+ '🔕': 'lautlos stumm',
+ '⏱️': 'stoppuhr zeit',
+ '⏰': 'wecker uhr',
+ '🕐': 'uhr eins zeit',
+ '📅': 'kalender datum',
+ '🗓️': 'kalender spiral',
+ '✅': 'häkchen erledigt ok',
+ '☑️': 'box angehakt',
+ '✔️': 'check mark',
+ '❌': 'kreuz nein fehler',
+ '⭕': 'kreis groß',
+ '❗': 'ausrufezeichen wichtig',
+ '❓': 'fragezeichen',
+ '💬': 'sprechblase chat',
+ '🗨️': 'sprechblase links',
+ '📝': 'memo notizen',
+ '📖': 'buch lesen',
+ '🪄': 'zauberstab magie',
+ '🎪': 'zirkus zelt',
+ '🎭': 'theater masken',
+ '🎬': 'film klappe',
+ '🎨': 'palette malen kunst',
+ '🖼️': 'bild rahmen',
+ '🧩': 'puzzle teil',
+ '♟️': 'schach bauer',
+ '🎲': 'würfel zufall spiel',
+ '🧸': 'teddy bär spielzeug',
+
+ // —— Fahrzeuge ——
+ '🚗': 'auto pkw',
+ '🚕': 'taxi',
+ '🚙': 'suv geländewagen',
+ '🚌': 'bus',
+ '🚎': 'oberleitungsbus trolley',
+ '🏎️': 'rennwagen formel',
+ '🚓': 'polizei streifenwagen',
+ '🚑': 'krankenwagen rettung',
+ '🚒': 'feuerwehr',
+ '🚐': 'kleinbus',
+ '🛻': 'pickup',
+ '🚚': 'lkw lieferwagen',
+ '🚛': 'sattelzug',
+ '🚜': 'traktor',
+ '🛵': 'roller motorroller',
+ '🏍️': 'motorrad',
+ '🛺': 'rikscha',
+ '🚲': 'fahrrad',
+ '🛴': 'tretroller scooter',
+ '🚁': 'helikopter hubschrauber',
+ '✈️': 'flugzeug',
+ '🛫': 'abflug',
+ '🛬': 'landung',
+ '🪂': 'fallschirm parachute',
+ '🚀': 'rakete startup',
+ '🛶': 'boot kanu',
+ '🚤': 'speedboot',
+ '🛥️': 'motorboot',
+ '🛳️': 'kreuzfahrtschiff',
+ '⛴️': 'fähre ferry',
+ '🚢': 'frachtschiff',
+ '⚓': 'anker hafen',
+ '🗼': 'turm fernsehturm'
+}
+
+/**
+ * @param {string} groupLabel
+ */
+export function slugifyGroupLabel(groupLabel) {
+ return groupLabel
+ .toLowerCase()
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .replace(/[()]/g, ' ')
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
+ .replace(/\s+/g, ' ')
+ .trim()
+}
+
+/**
+ * @param {string} emoji
+ * @param {string} groupLabel
+ */
+export function haystackForEmoji(emoji, groupLabel) {
+ const extra = EMOJI_KEYWORDS[emoji] || ''
+ const slug = slugifyGroupLabel(groupLabel)
+ return `${extra} ${slug}`.replace(/\s+/g, ' ').trim()
+}
+
+/**
+ * Alle Such-Tokens müssen im Haystack vorkommen (UND).
+ * @param {string} haystack
+ * @param {string} query
+ */
+export function matchesEmojiSearch(haystack, query) {
+ const q = query.trim().toLowerCase()
+ if (!q) return true
+ const norm = haystack
+ .toLowerCase()
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ const tokens = q
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .split(/\s+/)
+ .filter(Boolean)
+ return tokens.every((t) => norm.includes(t))
+}