import type { SuperagentMessage, SuperagentPromptSuggestion } from '../../types'; /** * Pure logic for the composer prompt-suggestion chips, mirroring the web builder's * `ChatInputSuggestionsAbove` / `useSuggestionsManager` behavior. Kept free of any * React Native imports so it can be unit-tested in the node vitest environment. */ /** The fabricated placeholder-intro bubble id used before a real agent turn arrives. */ const WELCOME_MESSAGE_ID = 'welcome'; /** * True once the conversation has at least one real assistant turn. The fabricated * "welcome" placeholder (id `welcome`) doesn't count — suggestions only make sense * after the agent has actually responded, matching the web `hasAssistantMessage` gate. */ export function hasRealAssistantMessage(messages: SuperagentMessage[]): boolean { return messages.some( (message) => message.role === 'assistant' && message.id !== WELCOME_MESSAGE_ID, ); } /** Titles of the currently-shown suggestions, sent as the `exclude` list on refresh. */ export function buildExcludeTitles(suggestions: SuperagentPromptSuggestion[]): string[] { return suggestions.map((suggestion) => suggestion.title).filter(Boolean); } /** Whether any suggestion carries connector types (drives the `has_connectors` BI prop). */ export function suggestionsHaveConnectors(suggestions: SuperagentPromptSuggestion[]): boolean { return suggestions.some((suggestion) => Boolean(suggestion.connector_types?.length)); } export type SuggestionsVisibilityInput = { suggestionCount: number; refreshing: boolean; isSending: boolean; dismissed: boolean; hiddenByUser: boolean; hasAssistantMessage: boolean; isOutOfCredits: boolean; }; export type SuggestionsVisibility = { /** Show the full chip row. */ visible: boolean; /** Show the collapsed "restore" affordance (the user hid the row but chips exist). */ showRestore: boolean; }; /** * Resolves what the composer should render, mirroring the web `isVisible` / `showLightbulb` * predicates. The chip row shows when there are suggestions (or a refresh is in flight), * the agent isn't mid-reply, a real assistant message exists, the user is in credit, and * neither the auto-dismiss (on send) nor the manual hide flag is set. When the user has * manually hidden a non-empty set, the collapsed restore button shows instead. */ export function computeSuggestionsVisibility({ suggestionCount, refreshing, isSending, dismissed, hiddenByUser, hasAssistantMessage, isOutOfCredits, }: SuggestionsVisibilityInput): SuggestionsVisibility { if (isOutOfCredits || isSending || dismissed || !hasAssistantMessage) { return { visible: false, showRestore: false }; } const hasChips = suggestionCount > 0; return { visible: (hasChips || refreshing) && !hiddenByUser, showRestore: hasChips && hiddenByUser, }; }