feat(web): enhance composer suggestion skeleton, improve document mention picker loading states & improved LLM prompt block for connector mentions

This commit is contained in:
Anish Sarkar 2026-05-27 00:43:50 +05:30
parent 17293125ef
commit dbf235cbda
4 changed files with 31 additions and 16 deletions

View file

@ -1401,14 +1401,15 @@ async def stream_new_chat(
continue continue
connector_lines.append( connector_lines.append(
f' - connector_id={connector_id}, connector_type="{connector_type}", ' f' - connector_id={connector_id}, connector_type="{connector_type}", '
f'account="{account_name or ""}"' f'account_name="{account_name or ""}"'
) )
if connector_lines: if connector_lines:
context_parts.append( context_parts.append(
"<mentioned_connectors>\n" "<mentioned_connectors>\n"
"The user selected these exact connector accounts with @. " "The user selected these exact connector accounts with @. "
"For read, write, or HITL tool calls involving these services, " "These entries are selection metadata, not retrieved connector content. "
"prefer the matching connector_id instead of guessing from available accounts:\n" "When a connector-backed tool needs an account, use the matching "
"connector_id from this list if the tool supports connector_id:\n"
+ "\n".join(connector_lines) + "\n".join(connector_lines)
+ "\n</mentioned_connectors>" + "\n</mentioned_connectors>"
) )

View file

@ -31,7 +31,7 @@ function ComposerSuggestionPopoverContent({
onCloseAutoFocus?.(event); onCloseAutoFocus?.(event);
}} }}
className={cn( className={cn(
"w-[232px] overflow-hidden rounded-md border border-popover-border bg-popover p-0 text-popover-foreground shadow-md sm:w-[264px]", "w-[232px] select-none overflow-hidden rounded-md border border-popover-border bg-popover p-0 text-popover-foreground shadow-md sm:w-[264px]",
"data-[state=open]:!animate-none data-[state=closed]:!animate-none data-[state=open]:!duration-0 data-[state=closed]:!duration-0", "data-[state=open]:!animate-none data-[state=closed]:!animate-none data-[state=open]:!duration-0 data-[state=closed]:!duration-0",
className className
)} )}
@ -145,18 +145,24 @@ function ComposerSuggestionMessage({
); );
} }
function ComposerSuggestionSkeleton() { function ComposerSuggestionSkeleton({
rows = 5,
mobileRows = 3,
}: {
rows?: number;
mobileRows?: number;
}) {
return ( return (
<div className="px-1.5 py-1"> <div className="px-1.5 py-1">
<div className="px-2 py-1"> <div className="px-2 py-1">
<Skeleton className="h-3.5 w-20" /> <Skeleton className="h-3.5 w-20" />
</div> </div>
{["a", "b", "c", "d", "e"].map((id, index) => ( {Array.from({ length: rows }, (_, index) => `skeleton-row-${index}`).map((id, index) => (
<div <div
key={id} key={id}
className={cn( className={cn(
"flex w-full items-center gap-1.5 rounded-md px-2 py-1 text-left", "flex w-full items-center gap-1.5 rounded-md px-2 py-1 text-left",
index >= 3 && "hidden sm:flex" index >= mobileRows && "hidden sm:flex"
)} )}
> >
<span className="shrink-0"> <span className="shrink-0">

View file

@ -438,7 +438,7 @@ export const DocumentMentionPicker = forwardRef<
() => new Set(initialSelectedDocuments.map((d) => getMentionDocKey(d))), () => new Set(initialSelectedDocuments.map((d) => getMentionDocKey(d))),
[initialSelectedDocuments] [initialSelectedDocuments]
); );
const showSurfsenseDocsRootRef = useRef((surfsenseDocs?.items?.length ?? 0) > 0); const showSurfsenseDocsRoot = surfsenseDocsList.length > 0;
const selectMention = useCallback( const selectMention = useCallback(
(mention: MentionedDocumentInfo) => { (mention: MentionedDocumentInfo) => {
@ -463,7 +463,7 @@ export const DocumentMentionPicker = forwardRef<
const rootNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>( const rootNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(
() => { () => {
const nodes: ComposerSuggestionNode<ResourceNodeValue>[] = [...recentRootNodes]; const nodes: ComposerSuggestionNode<ResourceNodeValue>[] = [...recentRootNodes];
if (showSurfsenseDocsRootRef.current) { if (showSurfsenseDocsRoot) {
nodes.push({ nodes.push({
id: "surfsense-docs", id: "surfsense-docs",
label: "SurfSense Docs", label: "SurfSense Docs",
@ -496,7 +496,7 @@ export const DocumentMentionPicker = forwardRef<
); );
return nodes; return nodes;
}, },
[activeConnectors.length, recentRootNodes] [activeConnectors.length, recentRootNodes, showSurfsenseDocsRoot]
); );
const searchNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => { const searchNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => {
@ -680,11 +680,18 @@ export const DocumentMentionPicker = forwardRef<
[canLoadMoreDocuments, hasMore, isLoadingMore, loadNextPage] [canLoadMoreDocuments, hasMore, isLoadingMore, loadNextPage]
); );
const isRootBrowseView = !hasSearch && view.kind === "root";
const isVisibleViewLoading = hasSearch
? isTitleSearchLoading || isSurfsenseDocsLoading || isConnectorsLoading
: view.kind === "surfsense-docs"
? isSurfsenseDocsLoading
: view.kind === "files-folders"
? isTitleSearchLoading
: view.kind === "connectors" || view.kind === "connector-type"
? isConnectorsLoading
: false;
const actualLoading = const actualLoading =
(isTitleSearchLoading || isSurfsenseDocsLoading || isConnectorsLoading) && isVisibleViewLoading && !isSingleCharSearch && visibleNodes.length === 0 && !isRootBrowseView;
!isSingleCharSearch &&
visibleNodes.length === 0 &&
(view.kind === "root" || hasSearch);
const title = const title =
hasSearch || view.kind === "root" hasSearch || view.kind === "root"
@ -703,9 +710,10 @@ export const DocumentMentionPicker = forwardRef<
onScroll={handleScroll} onScroll={handleScroll}
role="listbox" role="listbox"
tabIndex={-1} tabIndex={-1}
className={isRootBrowseView ? "max-h-none overflow-visible sm:max-h-none" : undefined}
> >
{actualLoading ? ( {actualLoading ? (
<ComposerSuggestionSkeleton /> <ComposerSuggestionSkeleton rows={8} mobileRows={8} />
) : ( ) : (
<ComposerSuggestionGroup> <ComposerSuggestionGroup>
{title ? ( {title ? (

View file

@ -127,7 +127,7 @@ export const PromptPicker = forwardRef<PromptPickerRef, PromptPickerProps>(funct
return ( return (
<ComposerSuggestionList ref={scrollContainerRef}> <ComposerSuggestionList ref={scrollContainerRef}>
{isLoading ? ( {isLoading ? (
<ComposerSuggestionSkeleton /> <ComposerSuggestionSkeleton rows={8} mobileRows={8} />
) : isError ? ( ) : isError ? (
<ComposerSuggestionMessage variant="destructive">Failed to load prompts</ComposerSuggestionMessage> <ComposerSuggestionMessage variant="destructive">Failed to load prompts</ComposerSuggestionMessage>
) : filtered.length === 0 ? ( ) : filtered.length === 0 ? (