-
Notifications
You must be signed in to change notification settings - Fork 3
Localization, message sending, unread chats command, improved chat search and listing #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
📝 WalkthroughSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughReplaces the single find-chat command with multiple focused commands, adds English/Czech i18n and a language preference, introduces icon generation and avatar/network utilities, shared components/hooks, and localizes HUD/UI messages. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–75 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/search-chats.tsx (1)
7-27: Duplicate code withsrc/unread-chats.tsx.This is the same
getNetworkIconfunction duplicated fromsrc/unread-chats.tsx. See the comment on that file for the refactoring suggestion.
🧹 Nitpick comments (7)
scripts/generate-icons.js (1)
49-51: Simplify the filename handling.The code creates a
.pngfilename and immediately replaces it with.svg, which is unnecessarily convoluted.- const filename = `${name}.png`; - fs.writeFileSync(path.join(outputDir, filename.replace('.png', '.svg')), svg); - console.log(`Generated ${filename.replace('.png', '.svg')}`); + const filename = `${name}.svg`; + fs.writeFileSync(path.join(outputDir, filename), svg); + console.log(`Generated ${filename}`);src/list-accounts.tsx (3)
26-26: Semantic mismatch: using "unnamedChat" for accounts.The translation key
unnamedChatis semantically incorrect for an unnamed account. Consider adding a dedicatedunnamedAccountkey or renaming the existing key to something more generic likeunnamed.
33-44: Incomplete localization: metadata labels remain hardcoded.These metadata labels ("Account ID", "Network", "User ID", etc.) are still hardcoded in English while other UI strings use translations. For consistency, consider adding these to the translations object.
61-73: Incomplete localization: action titles remain hardcoded.Action titles like "Focus Beeper Desktop", "Toggle Details", and "Show Details (Old)" are hardcoded while other strings use translations. This creates inconsistency when the user switches languages.
src/unread-chats.tsx (1)
6-26: ExtractgetNetworkIconto a shared utility.This function is duplicated verbatim in
src/search-chats.tsx(lines 7-27). Extract it to a shared module (e.g.,src/utils.tsorsrc/icons.ts) to avoid maintaining the same code in multiple places.Create a new file
src/icons.ts:import { Icon, Image } from "@raycast/api"; export function getNetworkIcon(network: string): Image.ImageLike { const networkLower = network.toLowerCase().replace(/[/\s-]/g, ""); const iconMap: Record<string, string> = { slack: "slack.svg", whatsapp: "whatsapp.svg", telegram: "telegram.svg", discord: "discord.svg", instagram: "instagram.svg", facebook: "facebook.svg", facebookmessenger: "messenger.svg", messenger: "messenger.svg", signal: "signal.svg", imessage: "imessage.svg", twitter: "twitter.svg", email: "email.svg", googlemessages: "google-messages.svg", }; return iconMap[networkLower] || Icon.Message; }src/locales/index.ts (1)
34-39: Consider logging or returning success for invalid locale attempts.
setLocalesilently ignores unsupported locales. This could make debugging harder if a caller passes an invalid locale code. Consider returning a boolean or logging a warning.export function setLocale(locale: string): void { if (translations[locale]) { currentLocale = locale; currentTranslations = translations[locale]; + } else { + console.warn(`Locale "${locale}" not supported, keeping current locale "${currentLocale}"`); } }src/list-chats.tsx (1)
7-27: Consider extractinggetNetworkIconto a shared utility.Based on the AI summary, similar network icon logic is used in
search-chats.tsxandunread-chats.tsx. Extracting this to a shared utility (e.g.,src/utils.ts) would reduce duplication and ensure consistency.Example structure:
// src/utils.ts import { Icon, Image } from "@raycast/api"; export function getNetworkIcon(network: string): Image.ImageLike { const networkLower = network.toLowerCase().replace(/[/\s-]/g, ""); const iconMap: Record<string, string> = { slack: "slack.svg", // ... rest of mappings }; return iconMap[networkLower] || Icon.Message; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (13)
assets/discord.svgis excluded by!**/*.svgassets/email.svgis excluded by!**/*.svgassets/facebook.svgis excluded by!**/*.svgassets/google-messages.svgis excluded by!**/*.svgassets/imessage.svgis excluded by!**/*.svgassets/instagram.svgis excluded by!**/*.svgassets/messenger.svgis excluded by!**/*.svgassets/signal.svgis excluded by!**/*.svgassets/slack.svgis excluded by!**/*.svgassets/telegram.svgis excluded by!**/*.svgassets/twitter.svgis excluded by!**/*.svgassets/whatsapp.svgis excluded by!**/*.svgpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
README.md(1 hunks)package.json(3 hunks)scripts/generate-icons.js(1 hunks)src/api.ts(2 hunks)src/find-chat.tsx(0 hunks)src/list-accounts.tsx(3 hunks)src/list-chats.tsx(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/locales/index.ts(1 hunks)src/search-chats.tsx(1 hunks)src/send-message.tsx(1 hunks)src/unread-chats.tsx(1 hunks)
💤 Files with no reviewable changes (1)
- src/find-chat.tsx
🧰 Additional context used
🧬 Code graph analysis (9)
src/list-accounts.tsx (1)
src/locales/index.ts (1)
t(30-32)
src/locales/en.ts (1)
src/locales/index.ts (1)
Translations(45-45)
src/locales/cs.ts (2)
src/locales/en.ts (1)
Translations(84-84)src/locales/index.ts (1)
Translations(45-45)
src/send-message.tsx (2)
src/locales/index.ts (1)
t(30-32)src/api.ts (3)
useBeeperDesktop(67-71)getBeeperDesktop(50-65)createBeeperOAuth(31-48)
src/search-chats.tsx (2)
src/locales/index.ts (1)
t(30-32)src/api.ts (2)
useBeeperDesktop(67-71)focusApp(73-83)
src/list-chats.tsx (2)
src/locales/index.ts (1)
t(30-32)src/api.ts (2)
useBeeperDesktop(67-71)focusApp(73-83)
src/api.ts (1)
src/locales/index.ts (1)
t(30-32)
src/unread-chats.tsx (2)
src/locales/index.ts (1)
t(30-32)src/api.ts (3)
useBeeperDesktop(67-71)focusApp(73-83)createBeeperOAuth(31-48)
src/locales/index.ts (1)
src/locales/en.ts (1)
Translations(84-84)
🔇 Additional comments (20)
README.md (1)
23-68: LGTM! Clear and well-structured documentation.The command descriptions and NPM scripts table align with the package.json definitions and provide helpful guidance for users.
package.json (3)
22-45: LGTM! Well-structured command definitions.The new commands are properly defined with appropriate modes and descriptions that align with the documented features.
56-67: LGTM! Language preference configuration looks correct.The dropdown preference for language selection with English and Czech options is properly structured for Raycast.
80-80: simple-icons version is current and stable.Version ^15.22.0 is the latest available version on npm (released November 23, 2025) with no deprecation warnings. The package is actively maintained with regular releases.
src/locales/en.ts (1)
1-84: LGTM! Well-structured localization foundation.The translations object is logically organized with clear separation between commands and common strings. The use of functions for dynamic strings (e.g.,
unreadCount,totalCount) provides type safety while enabling parameterized translations.src/unread-chats.tsx (1)
28-86: Well-structured component with proper data flow.The implementation correctly:
- Filters and sorts chats by unread count during fetch
- Computes total unread count efficiently
- Handles empty state appropriately
- Uses proper accessories for visual indicators
src/api.ts (2)
67-71: Acceptable workaround for type overload issue.The
@ts-ignoreis documented with a clear explanation. Consider opening an issue upstream or with@raycast/utilsto get proper type support for optional dependency arrays.
73-82: Clean integration of localization with proper error handling.The function correctly uses localized messages and maintains good error handling with console logging for debugging.
src/locales/cs.ts (1)
1-84: Well-structured localization file with proper type safety.The file correctly imports the
Translationstype from./ento ensure structural consistency. The Czech translations appear complete and follow the same structure as the English locale.Consider having a native Czech speaker verify the translations for accuracy and natural phrasing.
src/locales/index.ts (1)
10-25: Robust locale resolution with sensible fallbacks.The function handles multiple locale formats (e.g.,
en-US,en_US) and provides a safe fallback to English. Theanycast is an acceptable workaround for the missing type onenvironment.locale.src/search-chats.tsx (2)
53-92: Conditional rendering logic is correct.The empty view handling works properly:
- Empty search text shows the "start typing" prompt
- Non-empty search with results shows the list
- Non-empty search without results shows the "no results" view
The
chats.map()on an empty array correctly produces no elements, allowing the no-resultsEmptyViewto display.
32-44: Efficient search implementation with proper dependency tracking.The hook correctly:
- Depends on
searchTextto trigger re-fetches- Returns early with an empty array when there's no search term
- Works well with the
throttleprop on the List componentsrc/send-message.tsx (5)
1-5: LGTM!Imports are well-organized and appropriate for the component's functionality.
7-10: LGTM!Clean interface definition for form values.
15-26: LGTM!The chat loading and sorting by recent activity is well-implemented.
59-95: LGTM!Good use of
storeValueon the dropdown for improved UX, and consistent translation usage throughout the form.
97-97: LGTM!Export pattern is consistent with other commands in the extension.
src/list-chats.tsx (3)
1-6: LGTM!Imports are appropriate for the component's functionality.
29-42: LGTM!Search-driven chat loading with proper dependency tracking and throttling.
91-102: LGTM!EmptyView condition and export pattern are correctly implemented.
| } catch (error) { | ||
| await showToast({ | ||
| style: Toast.Style.Failure, | ||
| title: translations.commands.sendMessage.errorTitle, | ||
| message: error instanceof Error ? error.message : "Unknown error occurred", | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use translated string for unknown error fallback.
The fallback error message is hardcoded in English, which breaks the localization pattern used throughout the file.
- message: error instanceof Error ? error.message : "Unknown error occurred",
+ message: error instanceof Error ? error.message : translations.common.unknownError,Ensure you add the unknownError key to your translation files if it doesn't already exist.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/send-message.tsx around lines 48 to 53, the catch block uses a hardcoded
English string "Unknown error occurred" for the fallback message which breaks
localization; replace that hardcoded string with
translations.commands.sendMessage.unknownError (or the appropriate translations
path used in this file) so the fallback uses the translated string, and ensure
you add the unknownError key to all translation files with the proper localized
values.
|
@coderabbitai generate docstrings |
✅ Actions performedInitiated docstring generation; will generate only if new commits exist. |
|
Note Docstrings generation - SUCCESS |
Docstrings generation was requested by @fery25. * #3 (comment) The following files were modified: * `src/api.ts` * `src/list-accounts.tsx` * `src/list-chats.tsx` * `src/locales/index.ts` * `src/search-chats.tsx` * `src/send-message.tsx` * `src/unread-chats.tsx`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
src/list-chats.tsx (2)
85-95: Apply the same unnamed-chat fallback in the Detail markdown header.The list item title already falls back to
translations.common.unnamedChat, but the Detail header useschat.titledirectly. For consistency and nicer UX when the title is missing, use the same fallback:- markdown={`# ${chat.title} + markdown={`# ${chat.title || translations.common.unnamedChat}
70-72: Fix accessories construction so empty objects aren’t passed through.
filter(Boolean)does not remove{}because empty objects are truthy, so you can still end up passing an empty accessory whenlastActivityis missing.Refactor to only append the date accessory when it exists:
- accessories={[{ text: chat.type }, chat.lastActivity ? { date: new Date(chat.lastActivity) } : {}].filter( - Boolean, - )} + accessories={[ + { text: chat.type }, + ...(chat.lastActivity ? [{ date: new Date(chat.lastActivity) }] : []), + ]}src/send-message.tsx (1)
57-62: Localize the unknown-error fallback in the failure toast.The fallback string
"Unknown error occurred"is hardcoded in English and breaks the otherwise consistent localization in this command. Prefer a translated key instead (e.g.,translations.common.unknownErroror atranslations.commands.sendMessage.unknownErrorentry).Example:
- message: error instanceof Error ? error.message : "Unknown error occurred", + message: error instanceof Error ? error.message : translations.common.unknownError,Make sure the chosen key exists in all locale files.
🧹 Nitpick comments (2)
src/search-chats.tsx (1)
7-33: Consider centralizinggetNetworkIconto avoid duplication.This helper (normalization + icon map) appears identical to the one in
src/list-chats.tsx. Pulling it into a shared module (e.g.,./network-icons) would reduce duplication and keep the icon mapping consistent across commands when you add/change networks.src/api.ts (1)
72-83: Optional: tighten typing to avoid@ts-ignoreonuseBeeperDesktop.The hook wrapper is fine functionally, but the
@ts-ignorearoundusePromiseis a small type-hygiene smell. If you want to clean this up later, you could add overloads foruseBeeperDesktop(with and withoutdeps) that mirrorusePromise’s signature so you don’t need to suppress the type checker.Not urgent, but worth considering when you next touch this file.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
src/api.ts(3 hunks)src/list-accounts.tsx(3 hunks)src/list-chats.tsx(1 hunks)src/locales/index.ts(1 hunks)src/search-chats.tsx(1 hunks)src/send-message.tsx(1 hunks)src/unread-chats.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/list-accounts.tsx
- src/locales/index.ts
- src/unread-chats.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
src/list-chats.tsx (2)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-83)focusApp(85-95)createBeeperOAuth(31-48)
src/api.ts (1)
src/locales/index.ts (1)
t(42-44)
src/search-chats.tsx (2)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-83)focusApp(85-95)createBeeperOAuth(31-48)
🔇 Additional comments (5)
src/search-chats.tsx (1)
35-107: Search behavior and localized UI look solid.The command correctly:
- Skips remote search when
searchTextis empty.- Streams results via
client.chats.search({ query: searchText }).- Uses localized placeholders/empty states and a distinct “no results” view.
- Shows good metadata (network, unread/pinned/muted) and focuses the chat via
focusApp.No issues from a correctness/UX standpoint.
src/api.ts (2)
50-70: BeeperDesktop client caching logic looks correct.
getBeeperDesktopcleanly reuses a single client instance and refreshes it when eitherbaseURLor the access token changes. This matches expected behavior with the OAuth flow and should avoid stale connections.
85-95: LocalizedfocusAppbehavior and HUD feedback look good.Switching to
app.open(params)and wiring success/error HUD messages throught()provides a nice localized UX, while still closing the main window on success. The error logging remains in English, which is fine for internal logs.src/send-message.tsx (1)
21-103: Send-message form behavior and UX look good overall.
- Chats are fetched once via
useBeeperDesktopand sorted bylastActivity(newest first).- Submission validates both
chatIdandmessage, with translated failure feedback.- On success, the command closes the main window and shows a localized HUD message.
- Form fields and actions are fully localized and use appropriate Raycast components.
Aside from the error-fallback string, the implementation looks solid.
src/list-chats.tsx (1)
35-112: ListChatsCommand search, localization, and actions look well-structured.The command:
- Uses localized search placeholder and empty-state copy.
- Streams chats from
client.chats.searchwith search-text–driven params.- Shows network-specific icons, localized unnamed-chat fallback, and last-activity/date accessory.
- Provides sensible actions (open in Beeper, show details, copy ID) with localized labels.
Functionally this all looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
src/locales/cs.ts (1)
61-64: Inconsistent indentation onunknownError.Line 63 has extra leading spaces compared to the surrounding properties, which breaks the visual alignment.
common: { unnamedChat: "Nepojmenovaný chat", - unknownError: "Došlo k neznámé chybě", + unknownError: "Došlo k neznámé chybě", openInBeeper: "Otevřít chat v Beeper",src/network-icons.ts (1)
12-26: Consider hoistingiconMapoutside the function.The
iconMapobject is recreated on every call. Moving it to module scope avoids repeated allocations, though the impact is minimal for this use case.+const NETWORK_ICON_MAP: Record<string, string> = { + slack: "slack.svg", + whatsapp: "whatsapp.svg", + telegram: "telegram.svg", + discord: "discord.svg", + instagram: "instagram.svg", + facebook: "facebook.svg", + facebookmessenger: "messenger.svg", + messenger: "messenger.svg", + signal: "signal.svg", + imessage: "imessage.svg", + twitter: "twitter.svg", + email: "email.svg", + googlemessages: "google-messages.svg", +}; + export function getNetworkIcon(network: string): Image.ImageLike { const networkLower = network.toLowerCase().replace(/[/\s-]/g, ""); - - const iconMap: Record<string, string> = { - slack: "slack.svg", - whatsapp: "whatsapp.svg", - telegram: "telegram.svg", - discord: "discord.svg", - instagram: "instagram.svg", - facebook: "facebook.svg", - facebookmessenger: "messenger.svg", - messenger: "messenger.svg", - signal: "signal.svg", - imessage: "imessage.svg", - twitter: "twitter.svg", - email: "email.svg", - googlemessages: "google-messages.svg", - }; - - return iconMap[networkLower] || Icon.Message; + return NETWORK_ICON_MAP[networkLower] || Icon.Message; }src/list-chats.tsx (2)
2-2: Remove unusedImageimport.
Imageis imported but never used in this file.-import { ActionPanel, Detail, List, Action, Icon, Image } from "@raycast/api"; +import { ActionPanel, Detail, List, Action, Icon } from "@raycast/api";
18-28: Streaming search collects all results before rendering.The
for awaitloop accumulates all chats into an array before returning. This means users won't see incremental results. For large result sets, consider a different pattern that updates state progressively, though this may be acceptable for typical chat counts.src/search-chats.tsx (2)
1-1: Remove unusedImageimport.
Imageis imported but never used in this file.-import { ActionPanel, Action, List, Icon, Image } from "@raycast/api"; +import { ActionPanel, Action, List, Icon } from "@raycast/api";
41-80: Two EmptyView components may cause confusion.The conditional rendering logic is correct, but having two separate
EmptyViewcomponents (lines 42-46 for empty search, lines 74-80 for no results) makes the JSX harder to follow. Consider consolidating into a single conditional block for clarity.{searchText === "" ? ( <List.EmptyView icon={Icon.MagnifyingGlass} title={translations.commands.searchChats.emptyTitle} description={translations.commands.searchChats.emptyDescription} /> + ) : !isLoading && chats.length === 0 ? ( + <List.EmptyView + icon={Icon.Message} + title={translations.commands.searchChats.noResultsTitle} + description={translations.commands.searchChats.noResultsDescription} + /> ) : ( chats.map((chat) => ( // ... existing List.Item code )) )} - {searchText !== "" && !isLoading && chats.length === 0 && ( - <List.EmptyView - icon={Icon.Message} - title={translations.commands.searchChats.noResultsTitle} - description={translations.commands.searchChats.noResultsDescription} - /> - )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
src/list-chats.tsx(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/network-icons.ts(1 hunks)src/search-chats.tsx(1 hunks)src/send-message.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/send-message.tsx
- src/locales/en.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/list-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-83)focusApp(85-95)createBeeperOAuth(31-48)src/network-icons.ts (1)
getNetworkIcon(9-29)
src/locales/cs.ts (2)
src/locales/en.ts (1)
Translations(85-85)src/locales/index.ts (1)
Translations(67-67)
src/search-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-83)focusApp(85-95)createBeeperOAuth(31-48)src/network-icons.ts (1)
getNetworkIcon(9-29)
🔇 Additional comments (5)
src/locales/cs.ts (1)
1-85: Translation structure looks good.The Czech translations correctly implement the
Translationstype and mirror the English structure. The dynamic count functions for unread and total counts are properly typed.src/list-chats.tsx (2)
43-46: Previous review comment addressed: accessories array handling.The spread pattern correctly handles the conditional
lastActivityaccessory, avoiding the previous issue with empty objects passing throughfilter(Boolean).
59-59: Previous review comment addressed: title fallback in Detail markdown.The fallback to
translations.common.unnamedChatis now correctly applied in the Detail markdown header.src/search-chats.tsx (1)
17-85: Implementation looks solid overall.The search command correctly handles empty state, loading state, and no-results state with proper localization. The use of
focusAppfor opening chats and the async iterator pattern for fetching results are consistent with the list-chats implementation.src/network-icons.ts (1)
9-10: File under review has been deleted in the current branch.The
src/network-icons.tsfile and all referenced SVG assets were removed in commitcf04bc3(current HEAD). In the previous commit (9714e2d), all 13 SVG files (slack.svg,whatsapp.svg,telegram.svg,discord.svg,instagram.svg,facebook.svg,messenger.svg,signal.svg,imessage.svg,twitter.svg,email.svg,google-messages.svg) existed and were correctly referenced by thegetNetworkIconfunction. Since the entire file and assets have been deleted, this review comment is no longer applicable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/search-chats.tsx (2)
17-86: Consider extracting shared chat rendering logic.There's substantial code duplication between
search-chats.tsxandlist-chats.tsx:
- The
useBeeperDesktopstreaming pattern (lines 20-32 here vs 18-28 in list-chats)- The
List.Itemrendering logic (lines 59-82 here vs 37-77 in list-chats)- Action definitions for Open in Beeper and Copy Chat ID
This duplication increases maintenance burden and the risk of inconsistencies.
Consider creating shared utilities:
Option 1: Extract a
ChatListItemcomponent that acceptschat,translations, and optionalaccessoriesconfiguration:// src/components/ChatListItem.tsx export function ChatListItem({ chat, translations, showDetails = true, accessories = [] }) { return ( <List.Item key={chat.id} icon={getNetworkIcon(chat.network)} title={chat.title || translations.common.unnamedChat} subtitle={chat.network} accessories={accessories} actions={/* ... shared actions ... */} /> ); }Option 2: Extract the streaming search logic into a custom hook:
// src/hooks/useChatSearch.ts export function useChatSearch(searchText: string, includeEmpty = false) { return useBeeperDesktop( async (client) => { if (!searchText && !includeEmpty) return []; const allChats = []; const params = searchText ? { query: searchText } : {}; for await (const chat of client.chats.search(params)) { allChats.push(chat); } return allChats; }, [searchText] ); }This would reduce duplication and make both commands easier to maintain.
66-66: Borrowing translation fromunreadChatscommand.Line 66 uses
translations.commands.unreadChats.unreadCount()within thesearchChatscommand. While this works functionally, it creates coupling between command translations.Consider either:
- Adding a shared
unreadCounthelper incommontranslations for reuse across commands- Adding
searchChats.unreadCountif this command should have independent translationsApply this diff for option 1:
common: { unnamedChat: "Unnamed chat", + unreadCount: (count: number) => `${count} unread`, // ... rest of common translations }Then update the usage:
- ? [{ text: translations.commands.unreadChats.unreadCount(chat.unreadCount) }] + ? [{ text: translations.common.unreadCount(chat.unreadCount) }]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/list-chats.tsx(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/network-icons.ts(1 hunks)src/search-chats.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/network-icons.ts
- src/locales/en.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/list-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (2)
useBeeperDesktop(79-83)focusApp(85-95)src/network-icons.ts (1)
getNetworkIcon(25-28)
src/locales/cs.ts (2)
src/locales/en.ts (1)
Translations(85-85)src/locales/index.ts (1)
Translations(67-67)
src/search-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-83)focusApp(85-95)createBeeperOAuth(31-48)src/network-icons.ts (1)
getNetworkIcon(25-28)
🔇 Additional comments (3)
src/locales/cs.ts (1)
1-85: LGTM! Structure and typing are correct.The Czech localization file is well-structured and correctly typed against the
Translationsinterface. The dynamic translation functions (unreadCount,totalCount) have proper type signatures.Note: I cannot verify the grammatical correctness or natural phrasing of the Czech translations. Consider having a native Czech speaker review the translation quality to ensure proper grammar, terminology, and user experience.
Would you like me to help identify a process for validating the translation quality, such as generating a checklist of strings to review or suggesting translation validation approaches?
src/list-chats.tsx (1)
1-89: LGTM! Well-implemented with localization and improved UX.The refactoring successfully integrates:
- Dynamic streaming search with proper state management
- Comprehensive localization via the
t()helper- Network-specific icons for better visual identification
- Enhanced actions including detailed view with translated field labels
- Proper conditional rendering for accessories and empty states
The past review comments regarding
filter(Boolean)and title fallback have been correctly addressed. The implementation is clean and follows React best practices.src/search-chats.tsx (1)
71-80: The review comment is based on incorrect information.After examining both files, I found that
search-chats.tsxdoes include the "Show Details" action (lines 26-45). The action displays a Detail component with markdown showing chat information, nearly identical tolist-chats.tsx.The actual difference is that
search-chats.tsxhas only the "Show Details" action, whilelist-chats.tsxincludes additional actions like "Refresh" and "Focus Beeper Desktop" in its ActionPanel.Additionally, the code snippet provided in the review (lines 71-80) does not correspond to
search-chats.tsx, which is only 61 lines total. The snippet appears to be fromlist-chats.tsxinstead.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/components/ChatListItem.tsx (2)
4-9: Consider adding proper types for props.Using
anyforchatandtranslationsreduces type safety and IDE assistance. Consider defining or importing proper types:+import { Translations } from "../locales/en"; + +interface Chat { + id: string; + network: string; + title?: string; + onOpen?: () => void; + detailsTarget?: React.ReactNode; +} + interface ChatListItemProps { - chat: any; - translations: any; + chat: Chat; + translations: Translations; accessories?: Array<{ text?: string; icon?: any; date?: Date }>; showDetails?: boolean; }
13-14: Remove redundantkeyprop.The
keyprop onList.Item(line 14) is redundant here since the key should be provided by the parent component when mapping overChatListItemcomponents. React keys are only meaningful when used in an array context at the call site.<List.Item - key={chat.id} icon={getNetworkIcon(chat.network)}src/search-chats.tsx (1)
1-1: Unused imports.
ActionPanel,Action, andIconare imported but not used directly in this file since the rendering logic moved toChatListItem. Consider removing unused imports.-import { ActionPanel, Action, List, Icon} from "@raycast/api"; +import { List, Icon } from "@raycast/api";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/components/ChatListItem.tsx(1 hunks)src/hooks/useChatSearch.ts(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/search-chats.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/locales/en.ts
🧰 Additional context used
🧬 Code graph analysis (4)
src/search-chats.tsx (4)
src/locales/index.ts (1)
t(42-44)src/hooks/useChatSearch.ts (1)
useChatSearch(3-16)src/components/ChatListItem.tsx (1)
ChatListItem(11-38)src/api.ts (2)
focusApp(85-95)createBeeperOAuth(31-48)
src/hooks/useChatSearch.ts (1)
src/api.ts (1)
useBeeperDesktop(79-83)
src/locales/cs.ts (2)
src/locales/en.ts (1)
Translations(86-86)src/locales/index.ts (1)
Translations(67-67)
src/components/ChatListItem.tsx (1)
src/network-icons.ts (1)
getNetworkIcon(25-28)
🔇 Additional comments (1)
src/locales/cs.ts (1)
1-86: LGTM! Well-structured Czech locale file.The file correctly imports the
Translationstype from./enensuring type safety and structural consistency with the English locale. The translation structure mirrors the English version with proper function signatures for dynamic strings (unreadCount,totalCount).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
src/api.ts (1)
72-83: Avoid@ts-ignoreinuseBeeperDesktopand narrow ondepsYou can keep the same runtime behavior and drop the
@ts-ignoreby branching on whetherdepsis provided, and also avoid relying on an implicitReactnamespace by importing the dependency list type:-import type { AppOpenParams } from "@beeper/desktop-api/resources/app"; +import type { AppOpenParams } from "@beeper/desktop-api/resources/app"; +import type { DependencyList } from "react"; @@ -/** - * Execute an asynchronous operation using the current BeeperDesktop client and return its managed result. - * - * @param fn - Function that receives the current BeeperDesktop client and returns a promise for the desired value - * @param deps - Optional React dependency list that controls when the operation is re-run - * @returns The value produced by `fn` when executed with the current BeeperDesktop client; loading and error state are managed by the hook - */ -export function useBeeperDesktop<T>(fn: (client: BeeperDesktop) => Promise<T>, deps?: React.DependencyList) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - usePromise type overload issue with optional deps - return usePromise(async () => fn(getBeeperDesktop()), deps); -} +/** + * Execute an asynchronous operation using the current BeeperDesktop client and return its managed result. + */ +export function useBeeperDesktop<T>(fn: (client: BeeperDesktop) => Promise<T>, deps?: DependencyList) { + const factory = () => fn(getBeeperDesktop()); + return deps ? usePromise(factory, deps) : usePromise(factory); +}This keeps the hook API the same while staying within the
usePromiseoverloads and avoiding suppressed type errors.src/utils/avatar.ts (1)
8-93: Avatar path validation is solid; consider a couple of hardening tweaksThe containment logic (
normalize(resolve(...))+isPathAllowed+ null‑byte check) is strong. Two optional refinements you might consider:
- Explicitly reject non‑
file://URLs up front (e.g.,if (!url.startsWith("file://")) return undefined;) so future changes to parsing can’t accidentally start accepting other schemes.- The
normalizedPath.includes("..")check may block perfectly valid filenames containing..; if you trustnormalize(resolve(...))+isPathAllowed, you could drop this guard or tighten it to check path segments explicitly.If symlink escapes under the allowed base are a concern, you could also resolve with
fs.realpathbefore the containment check, at the cost of I/O.src/components/ChatListItem.tsx (2)
5-25: De-duplicategetNetworkIconmapping across modulesThis icon map is effectively identical to the ones in
src/list-chats.tsxandsrc/unread-chats.tsx. Centralizing it (e.g., in a smallutils/networkIcons.tshelper) and reusing it across ChatListItem and the list commands would reduce drift when adding or renaming network icons.
27-42: TightenChatprops typing and avoid implicitReactglobal
detailsTarget?: React.ReactNodeassumes a globalReacttype namespace, and the accessories shape is manually duplicated from Raycast types. For better type safety and IDE support:-import { List, ActionPanel, Action, Icon, Image } from "@raycast/api"; -import { Translations } from "../locales/en"; -import { safeAvatarPath } from "../utils/avatar"; +import { List, ActionPanel, Action, Icon, Image } from "@raycast/api"; +import type { ReactNode } from "react"; +import { Translations } from "../locales/en"; +import { safeAvatarPath } from "../utils/avatar"; @@ interface Chat { @@ - detailsTarget?: React.ReactNode; + detailsTarget?: ReactNode; } interface ChatListItemProps { chat: Chat; translations: Translations; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - accessories?: Array<{ text?: string; icon?: any; date?: Date }>; + accessories?: List.Item.Props["accessories"]; showDetails?: boolean; }This removes reliance on a global
Reactnamespace and keeps accessories aligned with Raycast’sList.ItemAPI.src/search-chats.tsx (1)
14-23: Avoid runningsafeAvatarPathtwice for the same avatar
getAvatarUrlalready returns a sanitized filesystem path viasafeAvatarPath, andChatListItemwill callsafeAvatarPathagain onavatarUrl. It’s harmless but redundant; you could either:
- Have
getAvatarUrlreturn the rawimgURLand letChatListItemhandle sanitization, or- Change
ChatListItemto assumeavatarUrlis already sanitized and skip re-validating it.Either way, it keeps the responsibility for sanitization in one clear place.
Also applies to: 61-67
src/unread-chats.tsx (1)
57-115: Optionally surface errors fromuseBeeperDesktopinstead of just “no unread”The unread filtering, sorting, and accessories all look correct. One thing to consider: if
useBeeperDesktopfails (auth/API error), the component will currently just show the “no unread chats” empty view onceisLoadingis false. Exposingerrorfrom the hook and either showing an error EmptyView or a toast would make failures distinguishable from a genuinely empty unread state.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
src/api.ts(3 hunks)src/components/ChatListItem.tsx(1 hunks)src/hooks/useChatSearch.ts(1 hunks)src/list-accounts.tsx(3 hunks)src/list-chats.tsx(1 hunks)src/locales/index.ts(1 hunks)src/search-chats.tsx(1 hunks)src/send-message.tsx(1 hunks)src/unread-chats.tsx(1 hunks)src/utils/avatar.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/list-accounts.tsx
- src/hooks/useChatSearch.ts
- src/send-message.tsx
- src/locales/index.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/api.ts (1)
src/locales/index.ts (1)
t(42-44)
src/components/ChatListItem.tsx (2)
src/locales/en.ts (1)
Translations(86-86)src/utils/avatar.ts (1)
safeAvatarPath(62-93)
src/search-chats.tsx (5)
src/utils/avatar.ts (1)
safeAvatarPath(62-93)src/locales/index.ts (1)
t(42-44)src/hooks/useChatSearch.ts (1)
useChatSearch(3-16)src/components/ChatListItem.tsx (1)
ChatListItem(54-73)src/api.ts (1)
focusApp(85-95)
🔇 Additional comments (4)
src/api.ts (2)
50-70: Caching client and docstring match behaviorThe JSDoc accurately reflects the caching behavior keyed off base URL and access token, and the instance reuse logic looks correct and safe.
85-95: Localized HUD messages infocusAppUsing
t()for the success/error HUD messages and switching toapp.openwith defaultAppOpenParams = {}is a clean way to localize user feedback without changing behavior.src/search-chats.tsx (1)
41-77: Search flow and localized empty states look goodUsing
useChatSearch(searchText)with a separate “type to search” empty view forsearchText === ""and a localized “no results” view when the search returns nothing is clean. Mapping toChatListItemwithkey={chat.id}and localized accessories (unread count, pin, mute) looks correct.src/list-chats.tsx (1)
56-124: Searchable chat listing and localized Detail view look consistentThe search integration via
useBeeperDesktopwith[searchText]deps, localized search placeholder, unnamed title fallback, and the translated Detail markdown (id/account/network/type/unread/pin/mute/archive/lastActivity with yes/no/na) all look coherent. The accessories construction forchat.typeand optionallastActivityavoids the earlierfilter(Boolean)pitfall.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/search-chats.tsx (1)
9-22: Consider adding proper TypeScript types.The function correctly extracts avatar URLs for 1:1 chats and appropriately delegates sanitization to
ChatListItem. However, usinganytypes reduces type safety. If the chat and participant types are available from@beeper/desktop-api, consider using them.-// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getAvatarUrl(chat: any): string | undefined { +import type { Chat } from "@beeper/desktop-api/resources/chats"; + +function getAvatarUrl(chat: Chat): string | undefined { // Only show avatar for 1:1 chats, not groups if (chat.type !== "group" && chat.participants?.items) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf); + const otherParticipant = chat.participants.items.find((p) => !p.isSelf); return otherParticipant?.imgURL; } return undefined; }src/list-chats.tsx (2)
9-26: Consider adding proper TypeScript types.The icon resolution logic correctly handles 1:1 chats (with validated avatars) and groups (with network icons). The security validation via
safeAvatarPathis excellent. However, usinganytypes reduces type safety—consider importing proper types from@beeper/desktop-apiif available.-// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getChatIcon(chat: any): Image.ImageLike { +import type { Chat } from "@beeper/desktop-api/resources/chats"; + +function getChatIcon(chat: Chat): Image.ImageLike { // For 1:1 chats, try to get the other person's avatar if (chat.type !== "group" && chat.participants?.items) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf); + const otherParticipant = chat.participants.items.find((p) => !p.isSelf); if (otherParticipant?.imgURL) { const validatedPath = safeAvatarPath(otherParticipant.imgURL); if (validatedPath) { return { source: validatedPath, mask: Image.Mask.Circle }; } } } return getNetworkIcon(chat.network); }
38-48: Consider using theuseChatSearchhook to reduce duplication.The chat fetching logic (lines 40-45) duplicates the implementation in
src/hooks/useChatSearch.ts. Both iterate throughclient.chats.searchand build an array. Using the shared hook would improve maintainability and consistency across commands.- const { data: chats = [], isLoading } = useBeeperDesktop( - async (client) => { - const allChats = []; - const searchParams = searchText ? { query: searchText } : {}; - for await (const chat of client.chats.search(searchParams)) { - allChats.push(chat); - } - return allChats; - }, - [searchText], - ); + const { data: chats = [], isLoading } = useChatSearch(searchText, true);Note: The second parameter
trueenablesincludeEmptyto show all chats whensearchTextis empty.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
src/api.ts(3 hunks)src/components/ChatListItem.tsx(1 hunks)src/list-chats.tsx(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/search-chats.tsx(1 hunks)src/unread-chats.tsx(1 hunks)src/utils/avatar.ts(1 hunks)src/utils/networkIcons.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/components/ChatListItem.tsx
- src/unread-chats.tsx
- src/locales/en.ts
- src/locales/cs.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/list-chats.tsx (4)
src/utils/avatar.ts (1)
safeAvatarPath(62-98)src/utils/networkIcons.ts (1)
getNetworkIcon(30-40)src/locales/index.ts (1)
t(42-44)src/api.ts (2)
useBeeperDesktop(75-77)focusApp(79-89)
src/search-chats.tsx (4)
src/locales/index.ts (1)
t(42-44)src/hooks/useChatSearch.ts (1)
useChatSearch(3-16)src/components/ChatListItem.tsx (1)
ChatListItem(33-52)src/api.ts (1)
focusApp(79-89)
🔇 Additional comments (14)
src/api.ts (4)
2-2: LGTM!The import updates correctly reflect the API changes (AppFocusParams → AppOpenParams) and introduce localization support.
Also applies to: 5-5
50-54: LGTM!The docstring clearly documents the caching behavior and function purpose.
75-76: > Likely an incorrect or invalid review comment.
79-88: The code snippet in this review comment does not match the current codebase.The review describes a
focusAppfunction usingapp.open()with translated HUD messages viat(), but the actual code usesapp.focus()with hardcoded HUD messages. There are no translation imports or locale files in the repository. The function shown in the review snippet does not exist insrc/api.ts.Verify the code review is based on the correct version of the file before proceeding.
Likely an incorrect or invalid review comment.
src/utils/networkIcons.ts (1)
30-40: LGTM!The function correctly normalizes network names, looks up icons, and provides a sensible fallback. The normalization pattern
replace(/[/\s-]/g, "")handles common variations effectively.src/utils/avatar.ts (3)
8-34: LGTM!The platform-specific base directory handling is correct and follows OS conventions. The Linux implementation properly respects
XDG_DATA_HOMEwhile providing a standard fallback.
43-49: LGTM!The containment check correctly prevents prefix attacks by requiring either an exact match or a path separator after the base directory.
62-98: Excellent security implementation!The
safeAvatarPathfunction implements comprehensive security measures:
- Explicit
file://scheme validation- Safe URI decoding with error handling
- Null byte rejection
- Path normalization and resolution
- Strict containment validation
- Graceful error handling
This prevents path traversal, prefix attacks, and other file system exploits.
src/search-chats.tsx (3)
1-7: LGTM!The imports correctly reflect the refactored architecture using
useChatSearch,ChatListItem, and localization.
24-32: LGTM!The docstring provides a clear, comprehensive description of the component's behavior and UI states.
33-78: The review comment references code that does not exist in the repository.The code snippet and concerns in the review comment do not match the actual implementation in
src/search-chats.tsx. The review discusses:
const translations = t()andtranslations.common.unreadCount()function callsuseChatSearch()hookChatListItemcomponent- Complex accessor logic with unread count, pinned, and muted icons
However, the actual file (61 lines total) uses:
useBeeperDesktop()hook- Direct
List.Itemcomponents- Hardcoded strings ("Search chats...", "No chats found")
- Simple accessory configuration with chat type and last activity
The review comment appears to be reviewing code from a different version, branch, or file entirely. Lines 33-78 as described in the review do not exist in the current repository state.
Likely an incorrect or invalid review comment.
src/list-chats.tsx (3)
1-7: LGTM!The imports are well-organized and support the enhanced functionality (localization, avatar handling, network icons).
28-34: LGTM!The docstring clearly describes the component's functionality and behavior.
50-104: LGTM!The list rendering correctly integrates localization, handles empty states, and provides comprehensive actions. Previous review comments have been addressed:
- Line 63 uses the spread operator pattern for conditional accessories instead of
filter(Boolean).- Line 76 applies the translated fallback for unnamed chats in the Detail markdown header.
The component provides a good user experience with translated placeholders, empty states, and detailed chat information.
| const NETWORK_ICON_MAP: Record<string, string> = { | ||
| slack: "slack.svg", | ||
| whatsapp: "whatsapp.svg", | ||
| telegram: "telegram.svg", | ||
| discord: "discord.svg", | ||
| instagram: "instagram.svg", | ||
| facebook: "facebook.svg", | ||
| facebookmessenger: "messenger.svg", | ||
| messenger: "messenger.svg", | ||
| signal: "signal.svg", | ||
| imessage: "imessage.svg", | ||
| twitter: "twitter.svg", | ||
| email: "email.svg", | ||
| googlemessages: "google-messages.svg", | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -type d -name "assets" | head -20Repository: beeper/raycast
Length of output: 66
🏁 Script executed:
git ls-files | grep -E "\.(svg|assets)" | head -30Repository: beeper/raycast
Length of output: 40
🏁 Script executed:
ls -la ./assets/Repository: beeper/raycast
Length of output: 244
🏁 Script executed:
# Check for each SVG file referenced in NETWORK_ICON_MAP
for icon in slack.svg whatsapp.svg telegram.svg discord.svg instagram.svg facebook.svg messenger.svg signal.svg imessage.svg twitter.svg email.svg google-messages.svg; do
if [ -f "./assets/$icon" ]; then
echo "✓ $icon"
else
echo "✗ $icon"
fi
doneRepository: beeper/raycast
Length of output: 236
All referenced SVG files are missing from the assets directory.
The NETWORK_ICON_MAP references 12 SVG files (slack.svg, whatsapp.svg, telegram.svg, discord.svg, instagram.svg, facebook.svg, messenger.svg, signal.svg, imessage.svg, twitter.svg, email.svg, google-messages.svg), but none of these files exist in ./assets/. The assets directory currently contains only extension-icon.png. Either these SVG files need to be added to the repository, or the icon mapping entries should be removed until the corresponding assets are available.
🤖 Prompt for AI Agents
In src/utils/networkIcons.ts around lines 7 to 21, the NETWORK_ICON_MAP
references 12 SVG filenames that are not present in ./assets/, causing broken
asset imports at runtime; fix this by either (1) adding the corresponding SVG
files to the ./assets/ directory with the exact filenames used in the map
(slack.svg, whatsapp.svg, telegram.svg, discord.svg, instagram.svg,
facebook.svg, messenger.svg, signal.svg, imessage.svg, twitter.svg, email.svg,
google-messages.svg), or (2) updating the map to only reference existing asset
files or a single generic fallback icon (e.g., extension-icon.png) and remove
any entries that cannot be supplied, and ensure any build/bundler import paths
match the assets location.
… updated `useBeeperDesktop` signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/list-chats.tsx (1)
9-26: Duplicate: ExtractgetChatIconto a shared utility.This is another instance of the duplicated
getChatIconfunction. See the earlier comment onsrc/unread-chats.tsxfor the suggested refactoring to a shared utility module.
🧹 Nitpick comments (2)
src/hooks/useMessageSearch.ts (1)
3-46: LGTM with a minor naming suggestion.The hook implementation is solid:
- Early return for empty queries prevents unnecessary API calls
- 50-message limit and batch size of 5 provide reasonable performance bounds
- Per-chat error handling with
console.warnallows partial results rather than failing entirely- Message augmentation with chat data is correctly implemented with a null fallback
Consider renaming the parameter from
querytosearchTextfor consistency with the function signature:return useBeeperDesktop( - async (client, query) => { - if (!query) return []; + async (client, searchText) => { + if (!searchText) return []; const allMessages = []; // Search for messages matching the query // We limit to a reasonable number or let the iterator handle it, // but for a simple list view, fetching the first batch is usually enough. // The API returns a PagePromise, so we can iterate. - for await (const message of client.messages.search({ query })) { + for await (const message of client.messages.search({ query: searchText })) { allMessages.push(message); if (allMessages.length >= 50) break; }src/unread-chats.tsx (1)
36-52: LGTM with an optional performance suggestion.The filtering and sorting logic is correct:
- Filters chats with
unreadCount > 0- Sorts by unread count in descending order (highest first)
For users with many chats, consider adding a reasonable limit to prevent performance issues:
const allChats = []; for await (const chat of client.chats.search({})) { // Filter only chats with unread messages if (chat.unreadCount > 0) { allChats.push(chat); + // Limit to prevent performance issues with very large chat lists + if (allChats.length >= 100) break; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
package.json(4 hunks)src/api.ts(3 hunks)src/hooks/useChatSearch.ts(1 hunks)src/hooks/useMessageSearch.ts(1 hunks)src/list-accounts.tsx(3 hunks)src/list-chats.tsx(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/search-messages.tsx(1 hunks)src/send-message.tsx(1 hunks)src/unread-chats.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- src/send-message.tsx
- src/list-accounts.tsx
- src/locales/cs.ts
- src/locales/en.ts
- src/hooks/useChatSearch.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/hooks/useMessageSearch.ts (1)
src/api.ts (1)
useBeeperDesktop(79-84)
src/api.ts (1)
src/locales/index.ts (1)
t(42-44)
src/unread-chats.tsx (4)
src/utils/avatar.ts (1)
safeAvatarPath(62-98)src/utils/networkIcons.ts (1)
getNetworkIcon(30-40)src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-84)focusApp(86-96)createBeeperOAuth(31-48)
🔇 Additional comments (13)
src/hooks/useMessageSearch.ts (1)
1-2: LGTM!Imports are clean and minimal.
src/api.ts (4)
50-70: LGTM!The docblock accurately describes the caching behavior. The implementation correctly invalidates and recreates the client when the base URL or access token changes.
72-84: LGTM!The generic enhancement is well-designed:
- Maintains backward compatibility with the previous signature
- Type-safe with proper generic constraints
- The docblock clearly explains the new dependency array behavior
86-96: Do not useAppOpenParamsor change the method toapp.open().The import statement specifies
AppFocusParamsfrom@beeper/desktop-api/resources/app, andAppOpenParamsdoes not exist in the codebase. The current implementation correctly usesapp.focus(params)withAppFocusParams. Changing toapp.open(params)withAppOpenParamswill cause a type error and break the function.If the intent is to open the app instead of focusing it, first verify that
app.open()is available in the@beeper/desktop-apiand update the import accordingly. The localization additions are appropriate; integrate them with the correct method and parameter type.Likely an incorrect or invalid review comment.
2-2: No changes needed. Line 2 currently importsAppFocusParams(notAppOpenParams), and the code uses the compatibleapp.focus()method with this parameter type. The reference to line 89 andapp.open()does not match the current codebase (file is 79 lines total). If reviewing an actual change toAppOpenParams, verify the corresponding API method calls are updated to useapp.open()instead ofapp.focus().package.json (2)
23-51: LGTM!The new command structure is well-organized:
- Five focused commands replace the single
find-chatcommand- Consistent naming and structure
- Clear titles and descriptions
- Appropriate use of
mode: "view"for all commands
62-79: LGTM!The language preference is well-structured:
- Dropdown with clear English and Czech options
- Proper Unicode handling for "Čeština"
- Sensible default to English
- Correctly marked as optional
src/unread-chats.tsx (1)
54-104: LGTM!The UI implementation is solid:
- Correct calculation of
totalUnreadfor the navigation title- Proper conditional rendering of empty states (error vs. no unread chats)
- Correct use of spread operator for conditional accessories (avoiding the empty object issue)
- Appropriate actions for opening chats and copying IDs
src/search-messages.tsx (2)
31-55: LGTM!The search UX is well-implemented:
- Throttled search prevents excessive API calls
- Clear empty state before search ("Enter a search query...")
- Distinct "no results" state after unsuccessful search
- Proper use of the
useMessageSearchhook
56-79: LGTM with proper null handling!The message item rendering is well-implemented:
- Proper optional chaining for
chat?.title- Appropriate fallbacks for missing data (
unknownMessage,unnamedChat)- Correct timestamp formatting
messageSortKeyconverted toStringfor API compatibilityThe action on line 72 passes
messageSortKeytofocusApp. This should be verified as part of theAppOpenParamscompatibility check flagged in theapi.tsreview.src/list-chats.tsx (3)
28-48: LGTM!The search-driven data fetching is well-implemented:
- Local state for search text with proper dependency tracking
- Conditional search parameters (all chats when query is empty, filtered when provided)
- Clear docblock explaining the component's behavior
50-63: LGTM!The List component is properly configured with throttled search and localized placeholders. The accessories array (line 63) correctly uses the spread operator pattern to conditionally include the lastActivity date, avoiding the empty object issue mentioned in past reviews.
64-103: LGTM!The actions and Detail view are well-implemented:
- Properly structured ActionPanel with three clear actions
- Detail markdown includes the title fallback (line 76) as suggested in past reviews
- All detail fields use translated labels with appropriate formatting
- Boolean values correctly show yes/no translations
- Empty view is properly conditioned and localized
All issues mentioned in past review comments have been addressed.
| "@types/react": "19.0.10", | ||
| "eslint": "^9.22.0", | ||
| "prettier": "^3.5.3", | ||
| "simple-icons": "^15.22.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
simple-icons npm latest version 2025
💡 Result:
As of December 4, 2025 the latest simple-icons npm version is 9.16.1. [1][2]
Sources:
[1] npm package page — simple-icons. [2] npm.io package summary for simple-icons.
🌐 Web query:
simple-icons npm security vulnerabilities CVE
💡 Result:
Short answer: There are no public CVEs for the simple-icons npm package itself. The only CVE I found referencing "Simple Icons" is for the WordPress plugin "Popular Brand Icons – Simple Icons" (CVE-2021-24503, stored XSS); that CVE affects the WordPress plugin (versions < 2.7.8), not the npm simple-icons library. [1][2]
Sources:
- NVD entry for CVE-2021-24503 (Popular Brand Icons – Simple Icons WordPress plugin). [1]
- Rapid7 / other vulnerability DB summary for the same CVE and package distinctions. [2]
If you want, I can run a package-specific scan (OSS advisory feeds: GitHub Advisory, Snyk, npm audit) against a particular simple-icons version you use.
Correct the simple-icons version to an available release.
The specified version ^15.22.0 does not exist. The latest available version of simple-icons is 9.16.1. The version constraint will cause installation to fail. Update to a valid version (e.g., ^9.16.0 or ^9.0.0). No security vulnerabilities exist for the package itself.
🤖 Prompt for AI Agents
In package.json around line 92, the dependency "simple-icons": "^15.22.0"
references a non-existent release and will break installs; change that version
to a valid release such as "^9.16.0" (or "^9.0.0"), save the file, then run your
package manager (npm install or yarn) to update node_modules and the lockfile
(package-lock.json or yarn.lock) so the correct version is recorded.
src/unread-chats.tsx
Outdated
| /** | ||
| * Returns chat icon - contact avatar for DMs, network icon for groups. | ||
| */ | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| function getChatIcon(chat: any): Image.ImageLike { | ||
| // For 1:1 chats, try to get the other person's avatar | ||
| if (chat.type !== "group" && chat.participants?.items) { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf); | ||
| if (otherParticipant?.imgURL) { | ||
| const validatedPath = safeAvatarPath(otherParticipant.imgURL); | ||
| if (validatedPath) { | ||
| return { source: validatedPath, mask: Image.Mask.Circle }; | ||
| } | ||
| } | ||
| } | ||
| return getNetworkIcon(chat.network); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Extract getChatIcon to a shared utility module.
This function is duplicated across at least three files (src/unread-chats.tsx, src/search-messages.tsx, and src/list-chats.tsx). Extract it to a shared utility (e.g., src/utils/chatIcon.ts) to eliminate duplication and ensure consistency.
Create a new file src/utils/chatIcon.ts:
import { Image, Icon } from "@raycast/api";
import { safeAvatarPath } from "./avatar";
import { getNetworkIcon } from "./networkIcons";
/**
* Returns chat icon - contact avatar for DMs, network icon for groups.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getChatIcon(chat: any): Image.ImageLike {
// For 1:1 chats, try to get the other person's avatar
if (chat.type !== "group" && chat.participants?.items) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf);
if (otherParticipant?.imgURL) {
const validatedPath = safeAvatarPath(otherParticipant.imgURL);
if (validatedPath) {
return { source: validatedPath, mask: Image.Mask.Circle };
}
}
}
return getNetworkIcon(chat.network);
}Then import and use it in all three files:
+import { getChatIcon } from "./utils/chatIcon";
-
-/**
- * Returns chat icon - contact avatar for DMs, network icon for groups.
- */
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function getChatIcon(chat: any): Image.ImageLike {
- // ...implementation...
-}🤖 Prompt for AI Agents
In src/unread-chats.tsx around lines 8 to 25, the getChatIcon function is
duplicated across multiple files; extract it into a shared utility module
src/utils/chatIcon.ts, moving the implementation there and exporting it; create
the new file importing required symbols (Image from @raycast/api, safeAvatarPath
from ./avatar, getNetworkIcon from ./networkIcons) and export getChatIcon
exactly as in the snippet, then update src/unread-chats.tsx,
src/search-messages.tsx, and src/list-chats.tsx to remove the local definition
and import { getChatIcon } from "src/utils/chatIcon" (or relative path as
appropriate), ensure ESLint directives are preserved or removed as necessary and
run type checks to fix any import path or type issues.
…-related components to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/list-chats.tsx (1)
18-28: Consider adding pagination or a result limit for performance.The current implementation fetches all chats when
searchTextis empty or provided (lines 21-24). For users with many chats, this could result in slow load times and high memory usage.Consider adding a limit to the iteration:
const { data: chats = [], isLoading } = useBeeperDesktop( async (client, query) => { const allChats = []; const searchParams = query ? { query } : {}; + const MAX_CHATS = 200; for await (const chat of client.chats.search(searchParams)) { allChats.push(chat); + if (allChats.length >= MAX_CHATS) break; } return allChats; }, [searchText], );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/components/ChatListItem.tsx(1 hunks)src/list-chats.tsx(1 hunks)src/search-messages.tsx(1 hunks)src/unread-chats.tsx(1 hunks)src/utils/chatIcon.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/unread-chats.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
src/utils/chatIcon.ts (2)
src/utils/avatar.ts (1)
safeAvatarPath(62-98)src/utils/networkIcons.ts (1)
getNetworkIcon(30-40)
src/list-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (2)
useBeeperDesktop(79-84)focusApp(86-96)src/utils/chatIcon.ts (1)
getChatIcon(9-24)
src/search-messages.tsx (4)
src/locales/index.ts (1)
t(42-44)src/hooks/useMessageSearch.ts (1)
useMessageSearch(3-46)src/utils/chatIcon.ts (1)
getChatIcon(9-24)src/api.ts (2)
focusApp(86-96)createBeeperOAuth(31-48)
src/components/ChatListItem.tsx (2)
src/locales/en.ts (1)
Translations(99-99)src/utils/chatIcon.ts (1)
getChatIcon(9-24)
🔇 Additional comments (7)
src/utils/chatIcon.ts (1)
9-24: LGTM!The function correctly handles null chats, validates avatar paths with
safeAvatarPath, and provides appropriate fallbacks (Icon.Bubble for null, network icon for groups or when avatar is unavailable). The defensive checks and circular mask for avatars align well with the UI requirements.src/components/ChatListItem.tsx (1)
22-41: Component structure is well-designed.The component properly uses optional chaining for
chat.onOpen?.(), provides appropriate fallbacks for missing titles, and conditionally renders the Show Details action. The action panel structure follows Raycast best practices.Note: Address the Chat interface type mismatch flagged in the previous comment to ensure
getChatIconworks correctly.src/list-chats.tsx (2)
8-14: LGTM!The docstring clearly describes the component's functionality and aligns with the implementation below.
37-82: LGTM!The list rendering correctly handles:
- Chat icons via
getChatIconwith network/avatar fallbacks- Title fallbacks using translations
- Conditional accessories using the spread operator (previous filter(Boolean) issue resolved)
- Localized markdown in the Details view (previous title fallback issue resolved)
- Proper empty state with translated messages
src/search-messages.tsx (3)
9-12: LGTM!The state management and hook usage follow React best practices. The
useMessageSearchhook is properly invoked withsearchTextas a dependency.
21-33: LGTM!The dual empty states (initial prompt and no results) provide clear user guidance and follow Raycast UX patterns effectively.
34-56: Message list rendering is well-structured.The list items correctly:
- Use the shared
getChatIconutility (previous duplication concern resolved)- Provide comprehensive fallbacks for missing data (senderName, text, chat title)
- Display timestamps for context
- Offer appropriate actions (open and copy)
Note: Address the sortKey handling flagged in the previous comment.
…ly pass `messageSortKey` to `focusApp`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
src/components/ChatListItem.tsx (2)
6-17: Consider removing unusedavatarUrlproperty from the interface.The
avatarUrlproperty (line 14) is defined in theChatinterface but is never used within this component. ThegetChatIconfunction handles avatar logic internally by accessingchat.participants. IfavatarUrlis intended for future use or is passed through for other purposes, consider documenting this; otherwise, removing it would reduce interface clutter.
28-29: Remove redundantkeyprop fromList.Item.The
key={chat.id}on line 29 is unnecessary here. React keys are needed on the outermost element returned frommap()calls, which is the parent's responsibility (and correctly done insearch-chats.tsxat line 60). Thekeyprop inside this component has no effect on reconciliation.<List.Item - key={chat.id} icon={getChatIcon(chat)}src/search-chats.tsx (1)
9-22:getAvatarUrlresult appears unused.The
getAvatarUrlfunction extracts an avatar URL, but the returned value assigned toavatarUrl(line 63) is never consumed byChatListItem. ThegetChatIconfunction insideChatListItemindependently derives the avatar fromchat.participants. Consider removing this helper and theavatarUrlassignment to eliminate dead code.-/** - * Returns raw avatar URL for 1:1 chats, undefined for groups. - * Note: The URL is not sanitized here - ChatListItem handles sanitization via safeAvatarPath. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getAvatarUrl(chat: any): string | undefined { - // Only show avatar for 1:1 chats, not groups - if (chat.type !== "group" && chat.participants?.items && Array.isArray(chat.participants.items)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf); - return otherParticipant?.imgURL; - } - return undefined; -}And in the chat object spread:
<ChatListItem key={chat.id} chat={{ ...chat, - avatarUrl: getAvatarUrl(chat), onOpen: () => focusApp({ chatID: chat.id }), }}src/unread-chats.tsx (3)
68-94: Consider usingChatListItemfor consistency.This file renders
List.Iteminline with similar logic to whatChatListItemalready encapsulates (icon viagetChatIcon, title fallback to unnamed chat, network subtitle, actions). UsingChatListItemwould reduce duplication and ensure consistent behavior across chat list views.+import { ChatListItem } from "./components/ChatListItem"; + // In the render: - chats.map((chat) => ( - <List.Item - key={chat.id} - icon={getChatIcon(chat)} - title={chat.title || translations.common.unnamedChat} - subtitle={chat.network} - accessories={[ - { - text: translations.commands.unreadChats.unreadCount(chat.unreadCount), - icon: Icon.Bubble, - }, - ...(chat.isPinned ? [{ icon: Icon.Pin }] : []), - ...(chat.isMuted ? [{ icon: Icon.SpeakerOff }] : []), - ...(chat.lastActivity ? [{ date: new Date(chat.lastActivity) }] : []), - ]} - actions={...} - /> - )) + chats.map((chat) => ( + <ChatListItem + key={chat.id} + chat={{ + ...chat, + onOpen: () => focusApp({ chatID: chat.id }), + }} + translations={translations} + accessories={[ + { text: translations.commands.unreadChats.unreadCount(chat.unreadCount), icon: Icon.Bubble }, + ...(chat.isPinned ? [{ icon: Icon.Pin }] : []), + ...(chat.isMuted ? [{ icon: Icon.SpeakerOff }] : []), + ...(chat.lastActivity ? [{ date: new Date(chat.lastActivity) }] : []), + ]} + /> + ))Note:
ChatListItemwould need to support thelastActivityaccessory pattern, or you could keep accessories as-is since they're passed through.
22-45: Consider adding a refresh mechanism.The
useBeeperDesktophook fetches once on mount with no dependencies. Users expecting to see newly arrived unread messages would need to close and reopen the command. Consider exposing therevalidatefunction fromusePromise(whichuseBeeperDesktopwraps) and adding a refresh action, or document this limitation.
1-1: Remove unusedImageimport.
Imageis imported but not used in this file. ThegetChatIconfunction is imported from./utils/chatIconand handles theImage.ImageLiketype internally.-import { ActionPanel, Action, List, Icon, Image } from "@raycast/api"; +import { ActionPanel, Action, List, Icon } from "@raycast/api";src/list-chats.tsx (1)
18-18: Consider defining a typed interface forchats.Using
any[]loses type safety. If theBeeperDesktopAPI provides type definitions, consider using those, or define a localChatinterface with the properties you're using (id,title,network,type,lastActivity,accountID,unreadCount,isPinned,isMuted,isArchived,participants).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
src/components/ChatListItem.tsx(1 hunks)src/hooks/useChatSearch.ts(1 hunks)src/list-chats.tsx(1 hunks)src/locales/cs.ts(1 hunks)src/locales/en.ts(1 hunks)src/search-chats.tsx(1 hunks)src/unread-chats.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/locales/en.ts
🧰 Additional context used
🧬 Code graph analysis (6)
src/locales/cs.ts (2)
src/locales/en.ts (1)
Translations(104-104)src/locales/index.ts (1)
Translations(67-67)
src/search-chats.tsx (4)
src/locales/index.ts (1)
t(42-44)src/hooks/useChatSearch.ts (1)
useChatSearch(3-36)src/components/ChatListItem.tsx (1)
ChatListItem(26-45)src/api.ts (1)
focusApp(86-96)
src/hooks/useChatSearch.ts (1)
src/api.ts (1)
useBeeperDesktop(79-84)
src/components/ChatListItem.tsx (3)
src/locales/en.ts (1)
Translations(104-104)src/locales/index.ts (1)
Translations(67-67)src/utils/chatIcon.ts (1)
getChatIcon(9-24)
src/list-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-84)getBeeperDesktop(55-70)focusApp(86-96)src/utils/chatIcon.ts (1)
getChatIcon(9-24)
src/unread-chats.tsx (3)
src/locales/index.ts (1)
t(42-44)src/api.ts (3)
useBeeperDesktop(79-84)focusApp(86-96)createBeeperOAuth(31-48)src/utils/chatIcon.ts (1)
getChatIcon(9-24)
🔇 Additional comments (10)
src/components/ChatListItem.tsx (1)
26-45: LGTM!The component is well-structured with proper conditional rendering for the details action, correct use of optional chaining for
onOpen, and appropriate default values for optional props.src/search-chats.tsx (2)
38-78: LGTM!The search implementation correctly handles empty state, loading, and no-results scenarios. The
throttleprop on the List is a good practice for search performance.
67-71: This review comment is based on incorrect code. The filesrc/search-chats.tsxdoes not contain anytranslations.common.unreadCount()calls or references to atranslationsobject. The code snippet cited (lines 67-71) does not match the actual file content.The actual file uses Raycast's built-in
List.Itemcomponent and does referencechat.unreadCountin a template literal on line 36 (**Unread Count:** ${chat.unreadCount}), but this is simple string interpolation without a translations function call. No defensive handling is needed for this usage as string interpolation safely handles undefined values.Likely an incorrect or invalid review comment.
src/hooks/useChatSearch.ts (1)
3-35: LGTM!The pagination logic with safety limits (MAX_PAGES, early exit at 200 results) is well-designed. The dependency array correctly includes both
searchTextandincludeEmpty, addressing the previous review feedback.src/locales/cs.ts (1)
1-104: LGTM!The Czech translation file correctly implements the
Translationstype, ensuring structural consistency with the English locale. The dynamic helper functions (unreadCount,totalCount) maintain the same signatures as their English counterparts.Consider having a native Czech speaker review the translations for accuracy and natural phrasing, as noted in the PR description that AI assistance was used.
src/unread-chats.tsx (1)
16-98: LGTM on the overall structure!The component correctly handles loading, error, and empty states with appropriate localized messages. The pagination with a safety limit and the server-side
unreadOnlyfilter is a good approach. Sorting byunreadCountdescending provides a sensible default ordering.src/list-chats.tsx (4)
23-40: LGTM!The search implementation correctly resets pagination state on query changes and uses the
isLoadingstate to provide feedback during the transition. The throttle prop on the List component helps reduce excessive API calls.
76-112: LGTM!Previous review feedback has been addressed:
- Accessories array now uses spread operator to conditionally include the date (line 82)
- Title fallback is applied in both the List.Item (line 80) and Detail markdown (line 95)
The action panel provides a complete set of useful actions for each chat item.
114-132: LGTM!The conditional rendering logic correctly handles the various states:
- Shows load-more only when there are existing results and more available
- Displays loading feedback in the button title during pagination
- Empty view provides appropriate guidance when no results exist
61-62: This review comment references code that does not exist in the current file.The code snippet showing
translations.commands.listChats.loadMoreErrorandtranslations.commands.listChats.loadMoreErrorMessageat lines 61-62 does not match the actual content of src/list-chats.tsx. Lines 61-62 containonAction={() => focusApp()}, and there is no loadMore function, translations object, or toast error handling in this file. Additionally, no translation keys need to be verified as none are referenced in the current code.Likely an incorrect or invalid review comment.
Changes and New Features
Added translations for multiple languages (English, Czech). The extension UI now adapts to the selected language.
Users can now send messages directly from Raycast using the new command.
Added a new command to display all chats with unread messages.
Chats are only displayed in search results when a keyword is entered, making search more relevant and efficient.
Listing of chats has been fixed and improved to ensure correct display and functionality.
Written with the assistance of AI. It will definitely need improvement and review :)