Skip to content

✨(sources) add source panel#480

Open
elvoisin wants to merge 1 commit into
mainfrom
evoisin/source-panel
Open

✨(sources) add source panel#480
elvoisin wants to merge 1 commit into
mainfrom
evoisin/source-panel

Conversation

@elvoisin
Copy link
Copy Markdown
Contributor

@elvoisin elvoisin commented May 19, 2026

add sources panel

Screenshot 2026-05-12 at 18 01 38

Summary by CodeRabbit

  • New Features

    • Introduced a dedicated sources panel that displays alongside the chat interface, allowing users to view source information in a dedicated space.
    • Added improved icons for chat actions (copy button, sources toggle).
    • Enhanced source item display with indexed numbering and type indicators.
  • Documentation

    • Updated changelog with sources panel addition.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Walkthrough

Chat sources are refactored from inline message rendering to a right-side panel using React portals. A preferences store manages panel visibility, context-based anchor propagation enables portal rendering, and layout adjustments accommodate the new sidebar. Message action buttons now use dedicated SVG icons.

Changes

Sources Panel Refactor

Layer / File(s) Summary
Sources Panel Context & Store Foundation
src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts, src/frontend/apps/conversations/src/features/sources-panel/SourcePanel.tsx, src/frontend/apps/conversations/src/features/sources-panel/index.ts
ChatPreferencesState adds isSourcesPanelOpen boolean and setSourcesPanelOpen setter, persisted via partialize. SourcePanel establishes SourcesPanelAnchorContext and exports useSourcePanelAnchor hook to consume the anchor from context.
SourceItem Props & Rendering Updates
src/frontend/apps/conversations/src/features/chat/components/SourceItem.tsx, src/frontend/apps/conversations/src/features/chat/components/SourceItemList.tsx
SourceItem now requires index: number prop and uses i18n for "Website" text. Favicon fallback replaced with renderType helper showing Icon + translated text when loading/error/no-favicon. Title row restructured to display index · renderType() with optional hostname separator; description rendered via Text component. SourceItemList passes 1-indexed items to SourceItem.
Chat Portal Integration & Sources State
src/frontend/apps/conversations/src/features/chat/components/Chat.tsx
Chat wires store's isSourcesPanelOpen and setSourcesPanelOpen, manages per-message source visibility via openSources callback, derives selectedSourceParts from opened message, builds sourcesPanelContent JSX with close button and SourceItemList, and portal-renders content into anchor element. Imports createPortal, SourceItemList, and useSourcePanelAnchor.
MainLayout Integration & Sizing
src/frontend/apps/conversations/src/layouts/MainLayout.tsx
Layout adds SourcePanel with local anchor state, derives leftPanelOffset and sourcesPanelOffset for fixed positioning (SOURCES_PANEL_WIDTH_PX = 360), adjusts chat column width calculations for both panels, and toggles anchor element visibility via aria-hidden and pointer-events based on isSourcesPanelOpen.
Message Item Action Icons & SourceItemList Removal
src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx, src/frontend/apps/conversations/src/pages/globals.css
MessageItem replaces generic Icon with dedicated SVG icons (CheckmarkIcon, ClipboardIcon, SourcesIcon), removes inline SourceItemList rendering, and adjusts sources button label. CSS adds .action-chat-button-icon and nested svg rules to style icons with neutral-secondary color.
Changelog Entry
CHANGELOG.md
Documents new source panel feature in Unreleased section.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • suitenumerique/conversations#119: Adjusts source display logic and SourceItem rendering in mobile contexts, directly affected by this PR's SourceItem index prop changes.
  • suitenumerique/conversations#435: Adds SourceItem test suite that needs updating for the new index prop and renderType rendering changes.
  • suitenumerique/conversations#65: Modifies useChatPreferencesStore for panel visibility state management, similar store-driven panel behavior pattern used here for sources panel.

Suggested labels

frontend

Suggested reviewers

  • maxenceh
  • qbey
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a source panel feature to the conversation interface. It directly aligns with the substantial refactoring across multiple components to implement portal-based source panel rendering.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch evoisin/source-panel
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch evoisin/source-panel

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a 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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx`:
- Around line 457-461: The toggle button currently shows a state label
("Hidden") instead of an action; update the JSX in MessageItem (the expression
using isSourceOpen, message.id and sourceParts) so when the panel is open
(isSourceOpen === message.id) it renders the imperative label t('Hide') instead
of t('Hidden'), and when closed it remains t('Show'); keep the count and
pluralization logic (sourceParts.length and t('source'/'sources')) unchanged.

In
`@src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts`:
- Around line 51-57: The partialize function in useChatPreferencesStore
currently omits isSourcesPanelOpen so that preference isn't persisted; update
the partialize return to include isSourcesPanelOpen alongside
themeModePreference, selectedModelHrid, forceWebSearch, isDarkModePreference,
and isPanelOpen so the sources panel open/closed state is stored and restored
across reloads.

In `@src/frontend/apps/conversations/src/layouts/MainLayout.tsx`:
- Around line 86-90: The closed-panel right offset uses a hardcoded '-300px'
while the panel width constant is SOURCES_PANEL_WIDTH_PX (360); update the
ternary in the styled/template string that uses isSourcesPanelOpen so the closed
position uses `-${SOURCES_PANEL_WIDTH_PX}px` instead of '-300px' (i.e., replace
the hardcoded value with the SOURCES_PANEL_WIDTH_PX-based expression) to ensure
the slide distance matches the panel width.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1855b4c3-9f37-4e25-8775-858f8cf95ec3

📥 Commits

Reviewing files that changed from the base of the PR and between 5e0e408 and 8fbb597.

⛔ Files ignored due to path filters (5)
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/book-filled.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/book.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/checkmark.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/clipboard.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/sources.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/features/chat/components/Chat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/SourceItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/SourceItemList.tsx
  • src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts
  • src/frontend/apps/conversations/src/features/sources-panel/SourcePanel.tsx
  • src/frontend/apps/conversations/src/features/sources-panel/index.ts
  • src/frontend/apps/conversations/src/layouts/MainLayout.tsx
  • src/frontend/apps/conversations/src/pages/globals.css

Comment on lines +457 to 461
{isSourceOpen !== message.id ? t('Show') : t('Hidden')}{' '}
{isSourceOpen !== message.id
? `${sourceParts.length} `
: ''}
{sourceParts.length !== 1 ? t('sources') : t('source')}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use an action label instead of state text for the toggle button.

On Line 457, Hidden reads like a status, not a button action. Prefer an imperative label (Hide) when the panel is open.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 458-458: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=suitenumerique_conversations&issues=AZ4_g4kaNqnL1Qk74aQy&open=AZ4_g4kaNqnL1Qk74aQy&pullRequest=480


[warning] 457-457: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=suitenumerique_conversations&issues=AZ4_g4kaNqnL1Qk74aQx&open=AZ4_g4kaNqnL1Qk74aQx&pullRequest=480

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx`
around lines 457 - 461, The toggle button currently shows a state label
("Hidden") instead of an action; update the JSX in MessageItem (the expression
using isSourceOpen, message.id and sourceParts) so when the panel is open
(isSourceOpen === message.id) it renders the imperative label t('Hide') instead
of t('Hidden'), and when closed it remains t('Show'); keep the count and
pluralization logic (sourceParts.length and t('source'/'sources')) unchanged.

Comment on lines +51 to +57
partialize: (state) => ({
themeModePreference: state.themeModePreference,
selectedModelHrid: state.selectedModelHrid,
forceWebSearch: state.forceWebSearch,
isDarkModePreference: state.isDarkModePreference,
isPanelOpen: state.isPanelOpen,
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Persist isSourcesPanelOpen in chat preferences.

On Line 51, partialize excludes isSourcesPanelOpen, so this preference resets after reload while other panel preferences are restored.

Proposed fix
       partialize: (state) => ({
         themeModePreference: state.themeModePreference,
         selectedModelHrid: state.selectedModelHrid,
         forceWebSearch: state.forceWebSearch,
         isDarkModePreference: state.isDarkModePreference,
         isPanelOpen: state.isPanelOpen,
+        isSourcesPanelOpen: state.isSourcesPanelOpen,
       }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
partialize: (state) => ({
themeModePreference: state.themeModePreference,
selectedModelHrid: state.selectedModelHrid,
forceWebSearch: state.forceWebSearch,
isDarkModePreference: state.isDarkModePreference,
isPanelOpen: state.isPanelOpen,
}),
partialize: (state) => ({
themeModePreference: state.themeModePreference,
selectedModelHrid: state.selectedModelHrid,
forceWebSearch: state.forceWebSearch,
isDarkModePreference: state.isDarkModePreference,
isPanelOpen: state.isPanelOpen,
isSourcesPanelOpen: state.isSourcesPanelOpen,
}),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/frontend/apps/conversations/src/features/chat/stores/useChatPreferencesStore.ts`
around lines 51 - 57, The partialize function in useChatPreferencesStore
currently omits isSourcesPanelOpen so that preference isn't persisted; update
the partialize return to include isSourcesPanelOpen alongside
themeModePreference, selectedModelHrid, forceWebSearch, isDarkModePreference,
and isPanelOpen so the sources panel open/closed state is stored and restored
across reloads.

Comment on lines +86 to +90
right: ${isSourcesPanelOpen ? '0px' : '-300px'};
bottom: 0;
z-index: 1001;
width: ${SOURCES_PANEL_WIDTH_PX}px;
`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the same offset constant for closed panel position.

On Line 86, the closed position uses -300px while the panel width is 360px. This creates an inconsistent slide distance.

Proposed fix
-                    right: ${isSourcesPanelOpen ? '0px' : '-300px'};
+                    right: ${isSourcesPanelOpen
+                      ? '0px'
+                      : `-${SOURCES_PANEL_WIDTH_PX}px`};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
right: ${isSourcesPanelOpen ? '0px' : '-300px'};
bottom: 0;
z-index: 1001;
width: ${SOURCES_PANEL_WIDTH_PX}px;
`
right: ${isSourcesPanelOpen
? '0px'
: `-${SOURCES_PANEL_WIDTH_PX}px`};
bottom: 0;
z-index: 1001;
width: ${SOURCES_PANEL_WIDTH_PX}px;
`
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 86-86: Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=suitenumerique_conversations&issues=AZ4_g4pHNqnL1Qk74aQ0&open=AZ4_g4pHNqnL1Qk74aQ0&pullRequest=480

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/apps/conversations/src/layouts/MainLayout.tsx` around lines 86 -
90, The closed-panel right offset uses a hardcoded '-300px' while the panel
width constant is SOURCES_PANEL_WIDTH_PX (360); update the ternary in the
styled/template string that uses isSourcesPanelOpen so the closed position uses
`-${SOURCES_PANEL_WIDTH_PX}px` instead of '-300px' (i.e., replace the hardcoded
value with the SOURCES_PANEL_WIDTH_PX-based expression) to ensure the slide
distance matches the panel width.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant