feat: emoji suggestion via Gemini AI and theme cycle toggle#26
Conversation
Raise tsconfig target es5→es2015: Intl.Segmenter spread and /u regex flag both require ES2015+. Fix useRef missing initial value for React 19 strict types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UserDropdown and ToggleDarkLightMode cycle light→dark→auto instead of toggling light↔dark only; uses stored colorScheme not computed - TabTransactions uses useComputedColorScheme so indicator color renders correctly when scheme is "auto" - MantineProvider gets defaultColorScheme="auto" to follow OS pref Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Adds AI-backed emoji suggestions and updates theme cycling behavior, alongside participant/payment and UI refinements.
Changes:
- Adds Gemini-based emoji suggestion API and client hook integration for transaction avatars.
- Updates theme toggles to cycle through light, dark, and auto.
- Adjusts participant avatar/payment behavior and trims the random emoji pools.
Reviewed changes
Copilot reviewed 13 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
tsconfig.json |
Raises TypeScript target to ES2015. |
src/utils/randomEmoji.ts |
Replaces large emoji lists with curated pools. |
src/hooks/useEmojiSuggestions.ts |
Adds debounced emoji suggestion fetching hook. |
src/components/UserSelectionModal.tsx |
Adds dotted loader style to save button. |
src/components/UserDropdown.tsx |
Changes theme menu item to cycle light/dark/auto. |
src/components/ToggleDarkLightMode.tsx |
Updates standalone theme toggle to cycle modes. |
src/components/EmojiActionButtion.tsx |
Adds AI suggestion auto-apply, loader, and cycle/randomize button. |
src/components/AddParticipantModal/index.tsx |
Allows blank IBAN during participant creation. |
src/components/AddParticipantModal/components/PageSetPayment.tsx |
Adds participant avatar randomization callback. |
src/components/AddEditTransactionModal/components/PageSetDetails.tsx |
Wires transaction name into emoji suggestions. |
src/app/layout.tsx |
Sets Mantine provider default color scheme to auto. |
src/app/group/[groupId]/components/TabTransactions.tsx |
Uses computed color scheme for transaction indicators. |
src/app/api/emoji-suggest/route.ts |
Adds Gemini-backed emoji suggestion endpoint. |
package.json |
Adds Google Generative AI dependency. |
package-lock.json |
Locks Google Generative AI dependency. |
Comments suppressed due to low confidence (4)
src/app/api/emoji-suggest/route.ts:20
- The response parser treats any segment with the Unicode Emoji property as a suggestion, but that property also includes digits and some symbols. If the model returns a numbered list despite the prompt, values like
1or2can be returned as “emojis” ahead of the actual emoji suggestions.
const emojis = [...new Intl.Segmenter().segment(text)]
.map((s) => s.segment)
.filter((s) => /\p{Emoji}/u.test(s) && s !== " ")
.slice(0, 5);
src/app/api/emoji-suggest/route.ts:15
- Gemini failures are not handled, so a missing API key, quota error, model error, or transient network failure will surface as a 500 from this route. Since the UI treats suggestions as optional, the API should return a controlled empty/error response instead of failing the request.
const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash-lite" });
const result = await model.generateContent(
`Given the transaction name: "${name}", suggest 5 relevant emojis. Reply with ONLY the 5 emojis separated by spaces, nothing else. No words, no punctuation, just emojis.`
);
src/app/api/emoji-suggest/route.ts:14
- There is no type or length validation for
namebefore it is interpolated into the Gemini prompt. A malformed or very large JSON value can produce server errors or unnecessarily large/expensive model requests; constrain this to a string with a reasonable maximum length before calling the model.
const { name } = await req.json();
if (!name?.trim()) return NextResponse.json({ emojis: [] });
const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash-lite" });
const result = await model.generateContent(
`Given the transaction name: "${name}", suggest 5 relevant emojis. Reply with ONLY the 5 emojis separated by spaces, nothing else. No words, no punctuation, just emojis.`
src/hooks/useEmojiSuggestions.ts:20
- This starts sending transaction names to the server/Gemini automatically as the user types. Expense names can contain sensitive personal information, so this should be opt-in or otherwise clearly disclosed/controlled before forwarding user-entered data to a third-party AI service.
const res = await fetch("/api/emoji-suggest", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: query }),
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| </Center> | ||
| <Center> | ||
| <EmojiActionButtion form={form} /> | ||
| <EmojiActionButtion form={form} suggestionQuery={form.values.name} /> |
…a abuse - Sanitize user input before prompt interpolation to prevent injection - Add IP-based rate limiting (20 req/min) with auto-expiry cleanup - Use Extended_Pictographic to filter emoji response, avoiding digit false positives - Wrap Gemini call in try/catch for graceful error handling - Fall back to curated expense emojis when suggestions unavailable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 16 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
src/app/api/emoji-suggest/route.ts:41
- This guard assumes
nameis a string before the laterString(name)normalization. A public caller can send a non-string JSON value (for example a number or object), causingname?.trimto throw outside thetryblock and return a 500.
const { name } = await req.json();
if (!name?.trim()) return NextResponse.json({ emojis: [] });
| </Center> | ||
| <Center> | ||
| <EmojiActionButtion form={form} /> | ||
| <EmojiActionButtion form={form} suggestionQuery={form.values.name} /> |
There was a problem hiding this comment.
Implemented in commit 3d1db6c.
The transaction details page now only passes suggestionQuery in create mode, so edit mode no longer triggers auto-apply from AI suggestions and existing avatars are preserved unless explicitly changed.
Screenshot: behavior-only fix (no visual UI change in this update).
| // Nature & weather | ||
| "🌈", "🌊", "🌋", "🌍", "🌙", "🌞", "🌟", "🌸", "🌺", "🌻", "🌼", "🍀", | ||
| "🍁", "🍂", "🍃", "🌿", "🌵", "🌴", "🌲", "🌳", "⛄", "❄️", "🔥", "💧", | ||
| "🌧️", "⛅", "🌤️", "🌪️", "🌈", "☀️", |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/guid3d/splt/sessions/859e9ecf-3dd3-4a22-8874-813067a7cd52 Co-authored-by: guid3d <29707419+guid3d@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. |
Agent-Logs-Url: https://github.com/guid3d/splt/sessions/07e19dce-8018-4000-afa1-58114e01852c Co-authored-by: guid3d <29707419+guid3d@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot build fail from vercel, fix it |
Agent-Logs-Url: https://github.com/guid3d/splt/sessions/c36d2e8a-03c3-4332-b9cc-4627ffd74b87 Co-authored-by: guid3d <29707419+guid3d@users.noreply.github.com>
Fixed in commit I investigated CI runs and reproduced locally: the emoji-suggest route imports Validation after the fix: |
|
@copilot somehow when emoji suggestion when typing in group name is wrong unlike how it was before |
Agent-Logs-Url: https://github.com/guid3d/splt/sessions/81a6641e-9dbe-4acd-8b73-df05dee12d4e Co-authored-by: guid3d <29707419+guid3d@users.noreply.github.com>
Fixed in commit I restored the previous auto-apply behavior for AI suggestions in group creation, so typing the group name now updates the avatar from the first fresh suggestion again (unless manually set in picker). |
Agent-Logs-Url: https://github.com/guid3d/splt/sessions/28314fb0-08ae-48c1-8032-fe5c38a23099 Co-authored-by: guid3d <29707419+guid3d@users.noreply.github.com>

Summary
/api/emoji-suggestroute calls Gemini 2.5 Flash Lite to suggest 5 emojis based on group/transaction name;useEmojiSuggestionshook debounces requests (400ms) and auto-applies first suggestion to avatar fieldEmojiActionButtongains cycle button (↻) to rotate through AI suggestions; shows loader while fetching; manual picker pick overrides AI suggestionsIconBrightnessAutoFilled), replacing binary light/dark flipTest plan
npm run build)🤖 Generated with Claude Code