-
Notifications
You must be signed in to change notification settings - Fork 154
Grouped Citations and enhanced styles of the citation pill #71
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: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds grouped-citation support: parser groups adjacent citations; renderer exposes "citation-group" nodes; CitationButton handles single or grouped citations with hosting-aware path; CitationModal accepts multiple sources; new citation-utils resolves names and formats display text; hosting context gains useOptionalHosting. Changes
Sequence DiagramsequenceDiagram
participant Parser as Remark Plugin
participant Renderer as Markdown Renderer
participant Button as CitationButton
participant Hosting as HostingContext / HostedCitationButton
participant Utils as citation-utils
participant Modal as CitationModal
Parser->>Parser: find citation matches
Parser->>Parser: group adjacent matches -> citation-group nodes
Parser->>Renderer: emit AST with citation / citation-group nodes
Renderer->>Button: render node(s)
alt Hosted environment (useOptionalHosting / useIsHosting true)
Button->>Hosting: detect hosting & delegate to HostedCitationButton
HostedCitationButton->>Utils: resolveCitationName(metadata, path)
Utils-->>HostedCitationButton: names
HostedCitationButton->>Utils: formatCitationDisplay(resolved)
Utils-->>HostedCitationButton: displayText
HostedCitationButton->>Modal: open modal with hosted sources + indices
else Non-hosted environment
Button->>Modal: open modal with local sources + indices
end
Modal->>Modal: filter/normalize sources & indices
Modal->>Modal: render CitationItem for each source
Modal-->>Button: show trigger with displayText
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
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: 2
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/web/src/components/chat/citation-button.tsx(1 hunks)apps/web/src/components/chat/citation-modal.tsx(2 hunks)apps/web/src/components/chat/citation-utils.ts(1 hunks)apps/web/src/components/chat/markdown.tsx(1 hunks)apps/web/src/components/chat/remark-citations.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.{ts,tsx}: Use the App Router directory structure in Next.js projects
Mark client components explicitly with 'use client' directive
Place static content and interfaces at file end
When usinguseRouter, import from@bprogress/next/appto show global progress bar
Use Zod for form validation in forms
Use react-hook-form for form handling as defined inapps/web/src/components/ui/form.tsx
Files:
apps/web/src/components/chat/remark-citations.tsapps/web/src/components/chat/markdown.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/components/chat/citation-utils.ts
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}
📄 CodeRabbit inference engine (.cursor/rules/shadcn.mdc)
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}: Import Shadcn UI components from the @agentset/ui alias (e.g.,import { Button, Card } from "@agentset/ui")
Use Shadcn UI components from the packages/ui/src/components/ui directory for UI elements
Style components using the 'new-york' style variant with 'neutral' base color and CSS variables for theming as configured in components.json
The Button component supports an isLoading prop to display a loading spinner
Files:
apps/web/src/components/chat/remark-citations.tsapps/web/src/components/chat/markdown.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/components/chat/citation-utils.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import from packages using configured aliases (e.g.,
@agentset/ui,@agentset/db/client) instead of relative paths to packages
Files:
apps/web/src/components/chat/remark-citations.tsapps/web/src/components/chat/markdown.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsxapps/web/src/components/chat/citation-utils.ts
apps/web/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.tsx: Wrap client components in Suspense with fallback
Minimize use of 'useEffect' and 'setState' hooks
Show loading states during form submission
Files:
apps/web/src/components/chat/markdown.tsxapps/web/src/components/chat/citation-modal.tsxapps/web/src/components/chat/citation-button.tsx
🧬 Code graph analysis (3)
apps/web/src/components/chat/markdown.tsx (1)
apps/web/src/components/chat/citation-button.tsx (1)
CitationButton(20-80)
apps/web/src/components/chat/citation-modal.tsx (5)
packages/utils/src/string.ts (1)
truncate(28-31)apps/web/src/contexts/hosting-context.tsx (2)
useIsHosting(29-32)useHosting(23-27)packages/ui/src/lib/utils.ts (1)
cn(5-7)apps/web/src/components/chat/citation-utils.ts (1)
resolveCitationName(6-31)packages/ui/src/components/ai-elements/code-block.tsx (2)
CodeBlock(72-131)CodeBlockCopyButton(139-179)
apps/web/src/components/chat/citation-button.tsx (4)
apps/web/src/types/ai.ts (1)
MyUIMessage(25-25)apps/web/src/contexts/hosting-context.tsx (2)
useIsHosting(29-32)useHosting(23-27)apps/web/src/components/chat/citation-modal.tsx (1)
CitationModal(45-100)apps/web/src/components/chat/citation-utils.ts (2)
ResolvedCitation(33-37)formatCitationDisplay(57-107)
🪛 Biome (2.1.2)
apps/web/src/components/chat/citation-modal.tsx
[error] 52-52: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
apps/web/src/components/chat/citation-button.tsx
[error] 27-27: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 38-38: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (8)
apps/web/src/components/chat/remark-citations.ts (2)
19-82: LGTM! Well-structured helper functions for citation node creation.The new
CitationGroupNodeinterface and helper functions cleanly separate concerns. The grouping logic correctly identifies adjacent citations by comparingstartIndexwithprevMatch.endIndex.
106-122: Grouping algorithm looks correct.The adjacent citation detection logic properly groups citations that are immediately consecutive (no characters between them). The algorithm preserves ordering and handles both single citations and groups appropriately.
apps/web/src/components/chat/markdown.tsx (1)
26-29: LGTM! Consistent rendering for grouped citations.The
citation-groupnode is correctly mapped to use the sameCitationButtoncomponent, which handles both single and grouped citations via thedata-citationanddata-citationsprops.apps/web/src/components/chat/citation-utils.ts (2)
6-31: LGTM! Robust path traversal with proper null safety.The function handles nested path traversal well and correctly converts different primitive types to strings.
95-106: Verify theremainingCountcalculation logic.The calculation
validCitations.length - Math.min(uniqueNames.length, maxNamesToShow)counts remaining citations after showing unique names. However, if there are 5 citations with 3 unique names and you show 2 names,remainingCountwould be5 - 2 = 3, which includes citations with shown names.This seems intentional based on the comment "Remaining count = total citations - unique names we're showing", but verify this matches the expected UX (e.g., "Name1, Name2 | +3" for 5 citations with 3 unique names).
apps/web/src/components/chat/citation-button.tsx (1)
88-132: LGTM! HostedCitationButton is well-structured.This component correctly calls all hooks at the top level before any conditional returns. The citation resolution and filtering logic is clear and properly memoized.
apps/web/src/components/chat/citation-modal.tsx (2)
17-36: Consider edge case in CitationDisplayText.The slice on line 25 uses
separatorIndex + 3to skip" | ", but the search pattern is" | +"(4 characters). This meanscountPartwill include the+prefix, which appears intentional for the display. Just confirming this is the expected behavior.
109-162: LGTM! CitationItem component is well-structured.All hooks are called unconditionally at the top level. The memoization of
citationNameandstringifiedMetadatais appropriate, and the error handling for JSON stringify is a nice touch.
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 (1)
apps/web/src/components/chat/citation-button.tsx (1)
106-113: Consider combining the duplicate filter operations.Both
validSourcesandvalidIndicesiterate over the same array with the same filter condition. This could be combined into a single pass.🔎 Proposed refactor to avoid double iteration
- // Filter valid sources for the modal - const validSources = resolvedCitations - .filter((c) => c.source !== undefined) - .map((c) => c.source!); - - const validIndices = resolvedCitations - .filter((c) => c.source !== undefined) - .map((c) => c.index); + // Filter valid sources for the modal + const { validSources, validIndices } = useMemo(() => { + const sources: Array<{ text: string; metadata?: Record<string, unknown> }> = []; + const indices: number[] = []; + for (const c of resolvedCitations) { + if (c.source !== undefined) { + sources.push(c.source); + indices.push(c.index); + } + } + return { validSources: sources, validIndices: indices }; + }, [resolvedCitations]);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/src/components/chat/citation-button.tsx(1 hunks)apps/web/src/components/chat/citation-modal.tsx(3 hunks)apps/web/src/contexts/hosting-context.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.{ts,tsx}: Use the App Router directory structure in Next.js projects
Mark client components explicitly with 'use client' directive
Place static content and interfaces at file end
When usinguseRouter, import from@bprogress/next/appto show global progress bar
Use Zod for form validation in forms
Use react-hook-form for form handling as defined inapps/web/src/components/ui/form.tsx
Files:
apps/web/src/components/chat/citation-button.tsxapps/web/src/contexts/hosting-context.tsxapps/web/src/components/chat/citation-modal.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/nextjs.mdc)
apps/web/**/*.tsx: Wrap client components in Suspense with fallback
Minimize use of 'useEffect' and 'setState' hooks
Show loading states during form submission
Files:
apps/web/src/components/chat/citation-button.tsxapps/web/src/contexts/hosting-context.tsxapps/web/src/components/chat/citation-modal.tsx
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}
📄 CodeRabbit inference engine (.cursor/rules/shadcn.mdc)
{apps/web/**/*.ts,apps/web/**/*.tsx,packages/ui/**/*.ts,packages/ui/**/*.tsx}: Import Shadcn UI components from the @agentset/ui alias (e.g.,import { Button, Card } from "@agentset/ui")
Use Shadcn UI components from the packages/ui/src/components/ui directory for UI elements
Style components using the 'new-york' style variant with 'neutral' base color and CSS variables for theming as configured in components.json
The Button component supports an isLoading prop to display a loading spinner
Files:
apps/web/src/components/chat/citation-button.tsxapps/web/src/contexts/hosting-context.tsxapps/web/src/components/chat/citation-modal.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import from packages using configured aliases (e.g.,
@agentset/ui,@agentset/db/client) instead of relative paths to packages
Files:
apps/web/src/components/chat/citation-button.tsxapps/web/src/contexts/hosting-context.tsxapps/web/src/components/chat/citation-modal.tsx
🧠 Learnings (1)
📚 Learning: 2025-11-26T20:30:45.439Z
Learnt from: CR
Repo: agentset-ai/agentset PR: 0
File: .cursor/rules/nextjs.mdc:0-0
Timestamp: 2025-11-26T20:30:45.439Z
Learning: Applies to apps/web/**/*.tsx : Minimize use of 'useEffect' and 'setState' hooks
Applied to files:
apps/web/src/components/chat/citation-modal.tsx
🧬 Code graph analysis (2)
apps/web/src/components/chat/citation-button.tsx (4)
apps/web/src/types/ai.ts (1)
MyUIMessage(25-25)apps/web/src/contexts/hosting-context.tsx (2)
useIsHosting(29-32)useHosting(23-27)apps/web/src/components/chat/citation-modal.tsx (1)
CitationModal(45-99)apps/web/src/components/chat/citation-utils.ts (3)
ResolvedCitation(33-37)resolveCitationName(6-31)formatCitationDisplay(57-107)
apps/web/src/components/chat/citation-modal.tsx (4)
packages/utils/src/string.ts (1)
truncate(28-31)apps/web/src/contexts/hosting-context.tsx (1)
useOptionalHosting(34-37)apps/web/src/components/chat/citation-utils.ts (1)
resolveCitationName(6-31)packages/ui/src/components/ai-elements/code-block.tsx (2)
CodeBlock(72-131)CodeBlockCopyButton(139-179)
🔇 Additional comments (7)
apps/web/src/contexts/hosting-context.tsx (1)
33-37: LGTM!The new
useOptionalHostinghook provides a clean, non-throwing access pattern to the hosting context. This properly addresses the Rules of Hooks violation that was previously flagged incitation-modal.tsxby enabling unconditional hook calls.apps/web/src/components/chat/citation-button.tsx (2)
20-42: Rules of Hooks violation has been resolved.The hooks (
useMemoat line 25 anduseIsHostingat line 36) are now correctly called before the early return at line 38. This ensures hooks are called unconditionally on every render.
88-131: LGTM!The
HostedCitationButtoncomponent is properly structured with hooks at the top level. Since it's only rendered whenisHostedis true (line 53), theuseHosting()call at line 93 is guaranteed to succeed. The citation resolution logic withresolveCitationNameandformatCitationDisplayis well-integrated.apps/web/src/components/chat/citation-modal.tsx (4)
51-51: Rules of Hooks violation has been resolved.Using
useOptionalHosting()instead of conditionally callinguseHosting()ensures the hook is called unconditionally on every render while still providing access to the hosting context when available.
17-36: LGTM!The
CitationDisplayTexthelper correctly parses and formats the display text, handling both single names (truncated to 50 chars) and grouped citations with the| +Npattern. The separator styling with reduced opacity provides good visual hierarchy.
108-161: Well-structured component.
CitationItemcorrectly usesuseMemofor expensive operations (name resolution and JSON stringification) rather thanuseEffect/setState. The error handling instringifiedMetadatais good defensive coding, and the conditional title tooltip for truncated names enhances UX. Based on learnings, the minimal use of state hooks here aligns with project guidelines.
85-94: VerifysourcesandsourceIndicesarrays have matching lengths.The component assumes these parallel arrays are of equal length. While the
CitationButtoncallers ensure this, a mismatch would causesourceIndices[idx]to beundefined, potentially breaking thekeyprop andsourceIndexprop.If you want to add defensive validation, consider:
if (sources.length !== sourceIndices.length) { console.warn("CitationModal: sources and sourceIndices length mismatch"); }
Examples
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.