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
34 changes: 26 additions & 8 deletions apps/tuya.ai/ai_components/ai_ui/include/ai_ui_icon_font.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
* @file ai_ui_icon_font.h
* @brief Icon and font management interface definitions.
*
* This header provides function declarations for managing fonts and icons
* used in AI UI, including text fonts, icon fonts, emoji fonts, and WiFi icons.
* DuckyClaw override of TuyaOpen's ai_ui_icon_font.h.
* Expands FONT_EMO_ICON_MAX_NUM to cover all 27 canonical emotion names
* plus 27 raw UTF-8 emoji fallback entries (UTF-8 variant only), so that
* the display layer correctly handles:
* - All 27 emotion names returned by the AI model.
* - Raw UTF-8 emoji characters (e.g. "😊") if the LLM sends those instead
* of a canonical name.
* - Any emotion string outside the defined set falls back to NEUTRAL.
*
* @copyright Copyright (c) 2021-2025 Tuya Inc. All Rights Reserved.
* @copyright Copyright (c) 2021-2026 Tuya Inc. All Rights Reserved.
*
*/

Expand All @@ -25,7 +31,19 @@ extern "C" {
/***********************************************************
************************macro define************************
***********************************************************/
#define FONT_EMO_ICON_MAX_NUM 7

/* 27 canonical emotion names. For the Font Awesome variant the list stops
* here. For the UTF-8 emoji variant an additional 27 raw-emoji entries are
* appended so that a bare emoji character (e.g. "😊") returned by the LLM is
* also matched. */
#define FONT_EMO_ICON_NAME_NUM 27

#if defined(FONT_EMO_AWESOME) && (FONT_EMO_AWESOME == 1)
#define FONT_EMO_ICON_MAX_NUM FONT_EMO_ICON_NAME_NUM
#else
/* name entries + UTF-8 emoji fallback entries */
#define FONT_EMO_ICON_MAX_NUM (FONT_EMO_ICON_NAME_NUM * 2)
#endif

/***********************************************************
***********************typedef define***********************
Expand All @@ -40,7 +58,7 @@ typedef struct {
lv_font_t *icon;
const lv_font_t *emoji;
AI_UI_EMOJI_LIST_T *emoji_list;
}AI_UI_FONT_LIST_T;
} AI_UI_FONT_LIST_T;

/***********************************************************
********************function declaration********************
Expand Down Expand Up @@ -69,7 +87,7 @@ lv_font_t *ai_ui_get_emo_font(void);
/**
* @brief Get emoji list for UI display.
*
* @return Pointer to emoji list structure.
* @return Pointer to emoji list structure (FONT_EMO_ICON_MAX_NUM entries).
*/
AI_UI_EMOJI_LIST_T *ai_ui_get_emo_list(void);

Expand All @@ -81,10 +99,10 @@ AI_UI_EMOJI_LIST_T *ai_ui_get_emo_list(void);
*/
char *ai_ui_get_wifi_icon(AI_UI_WIFI_STATUS_E status);

#endif

#ifdef __cplusplus
}
#endif

#endif /* ENABLE_LIBLVGL */

#endif /* __AI_UI_ICON_FONT_H__ */
166 changes: 139 additions & 27 deletions apps/tuya.ai/ai_components/ai_ui/src/ai_ui_icon_font.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,22 @@
* @file ai_ui_icon_font.c
* @brief Icon and font management implementation.
*
* This file provides functions for managing fonts and icons used in AI UI,
* including text fonts, icon fonts, emoji fonts, and WiFi icons.
* DuckyClaw override of TuyaOpen's ai_ui_icon_font.c.
*
* @copyright Copyright (c) 2021-2025 Tuya Inc. All Rights Reserved.
* Changes from upstream TuyaOpen:
* 1. Expanded sg_emo_list / sg_awesome_emo_list from 7 to all 27 emotions
* defined in skill_emotion.h, so every AI-returned emotion name is
* displayed correctly.
* 2. Added 27 raw UTF-8 emoji fallback entries in sg_emo_list (non-awesome
* variant). If the LLM returns a bare emoji character such as "😊"
* instead of the canonical name "HAPPY", the character is still matched
* and the correct icon is shown.
* 3. FONT_EMO_ICON_MAX_NUM is now 27 (Font Awesome) or 54 (UTF-8) so the
* search loop in __ui_set_emotion covers the full list.
* 4. Any emotion string that is not in the list falls back to NEUTRAL
* (the first entry), which is the existing fallback behaviour.
*
* @copyright Copyright (c) 2021-2026 Tuya Inc. All Rights Reserved.
*
*/

Expand All @@ -21,12 +33,10 @@
************************macro define************************
***********************************************************/


/***********************************************************
***********************typedef define***********************
***********************************************************/


/***********************************************************
***********************variable define**********************
***********************************************************/
Expand Down Expand Up @@ -64,27 +74,125 @@ LV_FONT_DECLARE(font_awesome_30_4);
#endif

#if defined(FONT_EMO_AWESOME) && (FONT_EMO_AWESOME == 1)

LV_FONT_DECLARE(font_awesome_30_1);
static AI_UI_EMOJI_LIST_T sg_awesome_emo_list[] = {
{"NEUTRAL", FONT_AWESOME_EMOJI_NEUTRAL},
{"SAD", FONT_AWESOME_EMOJI_SAD},
{"ANGRY", FONT_AWESOME_EMOJI_ANGRY},

/* ---------------------------------------------------------------------------
* Font Awesome emoji list – 27 canonical emotion names.
* For emotions without a dedicated FA glyph the closest available symbol
* is used so that the display always shows something meaningful.
* --------------------------------------------------------------------------- */
static AI_UI_EMOJI_LIST_T sg_awesome_emo_list[FONT_EMO_ICON_NAME_NUM] = {
{"NEUTRAL", FONT_AWESOME_EMOJI_NEUTRAL},
{"HAPPY", FONT_AWESOME_EMOJI_HAPPY},
{"LAUGHING", FONT_AWESOME_EMOJI_LAUGHING},
{"FUNNY", FONT_AWESOME_EMOJI_FUNNY},
{"SAD", FONT_AWESOME_EMOJI_SAD},
{"ANGRY", FONT_AWESOME_EMOJI_ANGRY},
{"FEARFUL", FONT_AWESOME_EMOJI_CRYING}, /* closest: crying/fearful */
{"LOVING", FONT_AWESOME_EMOJI_LOVING},
{"EMBARRASSED", FONT_AWESOME_EMOJI_EMBARRASSED},
{"SURPRISE", FONT_AWESOME_EMOJI_SURPRISED},
{"CONFUSED", FONT_AWESOME_EMOJI_CONFUSED},
{"SHOCKED", FONT_AWESOME_EMOJI_SHOCKED},
{"THINKING", FONT_AWESOME_EMOJI_THINKING},
{"HAPPY", FONT_AWESOME_EMOJI_HAPPY},
{"WINK", FONT_AWESOME_EMOJI_WINKING},
{"COOL", FONT_AWESOME_EMOJI_COOL},
{"RELAXED", FONT_AWESOME_EMOJI_RELAXED},
{"DELICIOUS", FONT_AWESOME_EMOJI_DELICIOUS},
{"KISSY", FONT_AWESOME_EMOJI_KISSY},
{"CONFIDENT", FONT_AWESOME_EMOJI_CONFIDENT},
{"SLEEP", FONT_AWESOME_EMOJI_SLEEPY},
{"SILLY", FONT_AWESOME_EMOJI_SILLY},
{"CONFUSED", FONT_AWESOME_EMOJI_CONFUSED},
{"TOUCH", FONT_AWESOME_EMOJI_WINKING}, /* closest: friendly/wink */
{"DISAPPOINTED", FONT_AWESOME_EMOJI_SAD}, /* closest: sad */
{"ANNOYED", FONT_AWESOME_EMOJI_CONFUSED}, /* closest: confused */
{"WAKEUP", FONT_AWESOME_EMOJI_SURPRISED}, /* closest: surprised */
{"LEFT", FONT_AWESOME_EMOJI_NEUTRAL}, /* directional – use neutral */
{"RIGHT", FONT_AWESOME_EMOJI_NEUTRAL}, /* directional – use neutral */
};
#else
static AI_UI_EMOJI_LIST_T sg_emo_list[] = {
{"NEUTRAL", "😶"},
{"SAD", "😔"},
{"ANGRY", "😠"},
{"SURPRISE", "😯"},
{"CONFUSED", "😏"},
{"THINKING", "🤔"},
{"HAPPY", "🙂"},

#else /* UTF-8 emoji variant */

/* ---------------------------------------------------------------------------
* UTF-8 emoji list.
*
* The first FONT_EMO_ICON_NAME_NUM (27) entries map canonical emotion NAME
* strings (e.g. "HAPPY") to the corresponding UTF-8 emoji character.
*
* The following FONT_EMO_ICON_NAME_NUM (27) entries map the raw UTF-8 emoji
* characters (e.g. "🙂") directly, allowing the display layer to handle the
* case where the LLM returns a bare emoji instead of a canonical name.
*
* Any input not matched by any entry falls back to sg_emo_list[0].emo_icon
* (NEUTRAL / 😶) in __ui_set_emotion.
* --------------------------------------------------------------------------- */
static AI_UI_EMOJI_LIST_T sg_emo_list[FONT_EMO_ICON_MAX_NUM] = {
/* --- canonical name → UTF-8 emoji --- */
{"NEUTRAL", "😶"}, /* U+1F636 */
{"HAPPY", "🙂"}, /* U+1F642 */
{"LAUGHING", "😆"}, /* U+1F606 */
{"FUNNY", "😂"}, /* U+1F602 */
{"SAD", "😔"}, /* U+1F614 */
{"ANGRY", "😠"}, /* U+1F620 */
{"FEARFUL", "😭"}, /* U+1F62D */
{"LOVING", "😍"}, /* U+1F60D */
{"EMBARRASSED", "😳"}, /* U+1F633 */
{"SURPRISE", "😯"}, /* U+1F62F */
{"SHOCKED", "😱"}, /* U+1F631 */
{"THINKING", "🤔"}, /* U+1F914 */
{"WINK", "😉"}, /* U+1F609 */
{"COOL", "😎"}, /* U+1F60E */
{"RELAXED", "😌"}, /* U+1F60C */
{"DELICIOUS", "🤤"}, /* U+1F924 */
{"KISSY", "😘"}, /* U+1F618 */
{"CONFIDENT", "😏"}, /* U+1F60F */
{"SLEEP", "😴"}, /* U+1F634 */
{"SILLY", "😜"}, /* U+1F61C */
{"CONFUSED", "🙄"}, /* U+1F644 */
{"TOUCH", "🤝"}, /* U+1F91D */
{"DISAPPOINTED", "😞"}, /* U+1F61E */
{"ANNOYED", "😒"}, /* U+1F612 */
{"WAKEUP", "😲"}, /* U+1F632 */
{"LEFT", "👈"}, /* U+1F448 */
{"RIGHT", "👉"}, /* U+1F449 */

/* --- raw UTF-8 emoji → same UTF-8 emoji (fallback for bare-emoji input) ---
*
* When the LLM returns a raw emoji character instead of the canonical name
* the strcmp() in __ui_set_emotion() will match one of these entries and
* display the correct icon. The emo_icon pointer re-uses the same string
* literal, so no extra memory is needed. */
{"😶", "😶"}, /* NEUTRAL */
{"🙂", "🙂"}, /* HAPPY */
{"😆", "😆"}, /* LAUGHING */
{"😂", "😂"}, /* FUNNY */
{"😔", "😔"}, /* SAD */
{"😠", "😠"}, /* ANGRY */
{"😭", "😭"}, /* FEARFUL */
{"😍", "😍"}, /* LOVING */
{"😳", "😳"}, /* EMBARRASSED */
{"😯", "😯"}, /* SURPRISE */
{"😱", "😱"}, /* SHOCKED */
{"🤔", "🤔"}, /* THINKING */
{"😉", "😉"}, /* WINK */
{"😎", "😎"}, /* COOL */
{"😌", "😌"}, /* RELAXED */
{"🤤", "🤤"}, /* DELICIOUS */
{"😘", "😘"}, /* KISSY */
{"😏", "😏"}, /* CONFIDENT */
{"😴", "😴"}, /* SLEEP */
{"😜", "😜"}, /* SILLY */
{"🙄", "🙄"}, /* CONFUSED */
{"🤝", "🤝"}, /* TOUCH */
{"😞", "😞"}, /* DISAPPOINTED */
{"😒", "😒"}, /* ANNOYED */
{"😲", "😲"}, /* WAKEUP */
{"👈", "👈"}, /* LEFT */
{"👉", "👉"}, /* RIGHT */
};
#endif

#endif /* FONT_EMO_AWESOME */

/***********************************************************
***********************function define**********************
Expand All @@ -103,15 +211,14 @@ lv_font_t *ai_ui_get_text_font(void)
#elif defined(FONT_TEXT_SIZE_18_2) && (FONT_TEXT_SIZE_18_2 == 1)
font = (lv_font_t *)&font_puhui_18_2;
#elif defined(FONT_TEXT_SIZE_20_4) && (FONT_TEXT_SIZE_20_4 == 1)
font = (lv_font_t *)&font_puhui_20_4;
font = (lv_font_t *)&font_puhui_20_4;
#elif defined(FONT_TEXT_SIZE_30_4) && (FONT_TEXT_SIZE_30_4 == 1)
font = (lv_font_t *)&font_puhui_30_4;
#endif

return font;
}


/**
* @brief Get icon font for UI display.
*
Expand All @@ -121,7 +228,7 @@ lv_font_t *ai_ui_get_icon_font(void)
{
lv_font_t *font = NULL;

#if defined(FONT_ICON_SIZE_14_1) && (FONT_ICON_SIZE_14_1 == 1)
#if defined(FONT_ICON_SIZE_14_1) && (FONT_ICON_SIZE_14_1 == 1)
font = (lv_font_t *)&font_awesome_14_1;
#elif defined(FONT_ICON_SIZE_16_4) && (FONT_ICON_SIZE_16_4 == 1)
font = (lv_font_t *)&font_awesome_16_4;
Expand Down Expand Up @@ -159,15 +266,20 @@ lv_font_t *ai_ui_get_emo_font(void)
/**
* @brief Get emoji list for UI display.
*
* @return Pointer to emoji list structure.
* Returns a pointer to the full emoji mapping table. The table has
* FONT_EMO_ICON_MAX_NUM entries:
* - Font Awesome variant: 27 entries (canonical name → FA glyph).
* - UTF-8 variant: 54 entries (27 name entries + 27 raw-emoji entries).
*
* @return Pointer to emoji list array.
*/
AI_UI_EMOJI_LIST_T *ai_ui_get_emo_list(void)
{
AI_UI_EMOJI_LIST_T *emo_list = NULL;

#if defined(FONT_EMO_AWESOME) && (FONT_EMO_AWESOME == 1)
emo_list = sg_awesome_emo_list;
#else
#else
emo_list = sg_emo_list;
#endif

Expand Down Expand Up @@ -205,4 +317,4 @@ char *ai_ui_get_wifi_icon(AI_UI_WIFI_STATUS_E status)
return wifi_icon;
}

#endif
#endif /* ENABLE_LIBLVGL */
Loading