Skip to content

✨(front) add co2 tooltip - #590

Merged
elvoisin merged 1 commit into
mainfrom
evoisin/feature-co2
Jul 17, 2026
Merged

✨(front) add co2 tooltip#590
elvoisin merged 1 commit into
mainfrom
evoisin/feature-co2

Conversation

@elvoisin

@elvoisin elvoisin commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added a carbon-impact (CO₂eq) energy indicator for assistant messages when a co2_impact annotation is available.
  • Bug Fixes
    • Improved chat finishing flow to keep displayed annotations synchronized after AI responses complete.
  • UI Updates
    • Made feedback buttons smaller (“nano”) and refined neutral button/tooltip styling.
  • Tests
    • Added coverage for CO₂ impact formatting/URL helpers and indicator rendering, including modal interactions and widget script injection.
  • Documentation
    • Updated the unreleased changelog entry for the new tooltip.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds CO2 impact extraction from message annotations, synchronizes assistant annotations into chat state, renders a responsive energy indicator in messages, and adds coverage for utilities and UI interactions. It also updates feedback button styling and the changelog.

Changes

CO2 Energy Indicator Feature

Layer / File(s) Summary
CO2 impact utilities
src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts, src/frontend/apps/conversations/src/features/chat/utils/impactCo2.ts, src/frontend/apps/conversations/src/features/chat/utils/__tests__/*
Extracts positive numeric co2_impact annotations and builds formatted ImpactCO2 URLs and widget query data, with unit tests.
Annotation synchronization
src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
Wraps onFinish to update the latest assistant message’s annotations before invoking the caller-provided callback.
Energy indicator component
src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx, src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageEnergyIndicator.test.tsx
Formats CO2 impact and displays responsive tooltip/modal content, external links, and the ImpactCO2 widget.
Message rendering integration
src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx, src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx
Conditionally renders the indicator for annotated messages, updates trace-feedback safety, and re-renders when CO2 impact changes.
Chat presentation updates
src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx, src/frontend/apps/conversations/src/pages/globals.css, CHANGELOG.md
Changes feedback buttons to nano sizing, adjusts tooltip and icon spacing, and records the CO2 tooltip addition.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AiSdk as "`@ai-sdk/react`"
  participant useChat
  participant ChatState as "Chat state"
  participant MessageItem
  participant MessageEnergyIndicator
  participant ImpactCO2 as "ImpactCO2 widget"

  AiSdk->>useChat: onFinish(message with annotations)
  useChat->>ChatState: update latest assistant annotations
  useChat->>AiSdk: invoke original callback
  ChatState->>MessageItem: render updated message
  MessageItem->>MessageEnergyIndicator: pass co2ImpactKg
  MessageEnergyIndicator->>ImpactCO2: inject widget script and data-search
Loading

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 captures the main frontend CO2 tooltip addition and is clearly related to the change set.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch evoisin/feature-co2

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.

@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch from 565f7e3 to f1d2e24 Compare July 6, 2026 13:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@CHANGELOG.md`:
- Around line 99-101: The unreleased changelog section contains a duplicated
bullet entry, so remove the repeated item and keep only one copy of the note.
Update the changelog entries in the affected section so the same release item
does not appear twice, preserving the other unique bullets around the duplicate.

In
`@src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx`:
- Line 12: Remove the unused Box import from MessageEnergyIndicator so the
component only imports what it actually uses; keep Text if it is referenced and
leave the rest of the component unchanged. Use the MessageEnergyIndicator
component as the locator when editing the import list.
🪄 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: 3858fea4-d093-4d55-ba00-f776c447bb57

📥 Commits

Reviewing files that changed from the base of the PR and between 08127a3 and 565f7e3.

⛔ Files ignored due to path filters (2)
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/arrow-up-right.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/leaves.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/custom-next.d.ts
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/Chat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/fixtures/fakeCo2Message.ts
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
💤 Files with no reviewable changes (1)
  • src/frontend/apps/conversations/src/custom-next.d.ts

Comment thread CHANGELOG.md Outdated

import ArrowUpRightIcon from '@/assets/icons/uikit-custom/arrow-up-right.svg';
import LeavesIcon from '@/assets/icons/uikit-custom/leaves.svg';
import { Box, Text } from '@/components';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the unused Box import.

Box isn't referenced in this component, so strict lint/noUnusedLocals checks will fail if they are enabled.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 12-12: Remove this unused import of 'Box'.

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

🤖 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/MessageEnergyIndicator.tsx`
at line 12, Remove the unused Box import from MessageEnergyIndicator so the
component only imports what it actually uses; keep Text if it is referenced and
leave the rest of the component unchanged. Use the MessageEnergyIndicator
component as the locator when editing the import list.

Source: Linters/SAST tools

@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch 2 times, most recently from f296ef8 to 0e0462e Compare July 6, 2026 14:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx (1)

526-552: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering the co2_impact <= 0 boundary.

The util's contract only surfaces co2ImpactKg when co2_impact > 0. Current tests only check "annotation present with positive value" vs. "annotation absent entirely" — a regression that renders the indicator for co2_impact: 0 or a negative value wouldn't be caught here.

🧪 Suggested additional test
+    it('does not render MessageEnergyIndicator when co2_impact is zero or negative', async () => {
+      await act(async () => {
+        renderWithProviders(
+          <MessageItem
+            {...defaultProps}
+            message={{
+              ...defaultProps.message,
+              annotations: [{ co2_impact: 0 }],
+            }}
+          />,
+        );
+      });
+
+      expect(
+        screen.queryByTestId('message-energy-indicator'),
+      ).not.toBeInTheDocument();
+    });

Based on learnings from getMessageCo2Impact.ts, which looks for an annotation object with co2_impact: number and returns that value only if it's > 0.

🤖 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/__tests__/MessageItem.test.tsx`
around lines 526 - 552, The energy indicator tests only cover a positive CO2
annotation and no annotation, so add a boundary case in MessageItem.test.tsx for
MessageItem/MessageEnergyIndicator where annotations include co2_impact values
of 0 and a negative number, and assert the indicator is not rendered. Use the
existing renderWithProviders and
screen.queryByTestId('message-energy-indicator') pattern so the suite matches
the getMessageCo2Impact contract that only surfaces co2ImpactKg when co2_impact
> 0.
src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx (1)

18-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using the current i18n locale for number formatting.

toLocaleString(undefined, ...) falls back to the browser's default locale, which may not match the app's active i18next language, causing inconsistent number formatting (decimal separators, etc.) versus the rest of the localized UI.

🌐 Optional improvement
-const formatCo2Impact = (kgCo2eq: number): string => {
-  return `${(kgCo2eq * 1000).toLocaleString(undefined, {
+const formatCo2Impact = (kgCo2eq: number, locale?: string): string => {
+  return `${(kgCo2eq * 1000).toLocaleString(locale, {
     maximumFractionDigits: 2,
   })} g CO₂eq`;
 };

And pass i18n.language from the component when calling formatCo2Impact.

🤖 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/MessageEnergyIndicator.tsx`
around lines 18 - 22, The number formatting in formatCo2Impact currently relies
on toLocaleString(undefined, ...) and may not match the app’s active i18next
language. Update MessageEnergyIndicator so the formatter uses the current i18n
locale instead of the browser default, and pass i18n.language from the component
when calling formatCo2Impact to keep numeric separators consistent with the rest
of the localized UI.
🤖 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/MessageEnergyIndicator.tsx`:
- Around line 62-86: The Prettier failure comes from inconsistent JSX
indentation in MessageEnergyIndicator’s leftActions/rightActions blocks.
Reformat the Button contents so the spacing matches the project style,
especially the Know more text and the OK button nesting. Use the
MessageEnergyIndicator component as the target and run the file through
Prettier-compatible formatting so CI passes.

---

Nitpick comments:
In
`@src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx`:
- Around line 526-552: The energy indicator tests only cover a positive CO2
annotation and no annotation, so add a boundary case in MessageItem.test.tsx for
MessageItem/MessageEnergyIndicator where annotations include co2_impact values
of 0 and a negative number, and assert the indicator is not rendered. Use the
existing renderWithProviders and
screen.queryByTestId('message-energy-indicator') pattern so the suite matches
the getMessageCo2Impact contract that only surfaces co2ImpactKg when co2_impact
> 0.

In
`@src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx`:
- Around line 18-22: The number formatting in formatCo2Impact currently relies
on toLocaleString(undefined, ...) and may not match the app’s active i18next
language. Update MessageEnergyIndicator so the formatter uses the current i18n
locale instead of the browser default, and pass i18n.language from the component
when calling formatCo2Impact to keep numeric separators consistent with the rest
of the localized UI.
🪄 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: 27b30117-a032-45ed-9aed-cff3ee5ec7eb

📥 Commits

Reviewing files that changed from the base of the PR and between 565f7e3 and f296ef8.

⛔ Files ignored due to path filters (2)
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/arrow-up-right.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/leaves.svg is excluded by !**/*.svg
📒 Files selected for processing (7)
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx

@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch from 0e0462e to 46bda7b Compare July 7, 2026 14:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx (1)

18-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Number formatting ignores app's active language.

toLocaleString(undefined, ...) falls back to the browser/runtime locale, not the app's i18n language selected via useTranslation. This can cause the CO2 value's decimal separator/grouping to be inconsistent with the rest of the localized UI.

♻️ Proposed fix
-const formatCo2Impact = (kgCo2eq: number): string => {
-  return `${(kgCo2eq * 1000).toLocaleString(undefined, {
-    maximumFractionDigits: 2,
-  })} g CO₂eq`;
-};
+const formatCo2Impact = (kgCo2eq: number, locale: string): string => {
+  return `${(kgCo2eq * 1000).toLocaleString(locale, {
+    maximumFractionDigits: 2,
+  })} g CO₂eq`;
+};

Then pass i18n.language from useTranslation() at the call site.

🤖 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/MessageEnergyIndicator.tsx`
around lines 18 - 22, The number formatting in formatCo2Impact is using the
runtime default locale, so it can differ from the app’s selected language.
Update MessageEnergyIndicator to get i18n from useTranslation and pass
i18n.language into the toLocaleString call at the formatCo2Impact call site so
the CO2 value matches the active app locale.
🤖 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/__tests__/MessageEnergyIndicator.test.tsx`:
- Around line 45-53: The desktop tooltip test in MessageEnergyIndicator is
asserting tooltip text before the Tooltip content is actually mounted. Update
the test to trigger the tooltip via hover or focus on the Carbon impact trigger
returned by getByLabelText('Carbon impact') before calling getByText for the CO₂
content, so the assertion targets the mounted tooltip content reliably.

---

Nitpick comments:
In
`@src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx`:
- Around line 18-22: The number formatting in formatCo2Impact is using the
runtime default locale, so it can differ from the app’s selected language.
Update MessageEnergyIndicator to get i18n from useTranslation and pass
i18n.language into the toLocaleString call at the formatCo2Impact call site so
the CO2 value matches the active app locale.
🪄 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: 3d1d47d8-6dad-4e0a-b521-4ec2fb6c6301

📥 Commits

Reviewing files that changed from the base of the PR and between f296ef8 and 46bda7b.

⛔ Files ignored due to path filters (2)
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/arrow-up-right.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/leaves.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageEnergyIndicator.test.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/utils/__tests__/getMessageCo2Impact.test.ts
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
✅ Files skipped from review due to trivial changes (3)
  • src/frontend/apps/conversations/src/features/chat/utils/tests/getMessageCo2Impact.test.ts
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/tests/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx

@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch from 46bda7b to bb9b6b5 Compare July 9, 2026 14:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (5)
src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts (1)

13-17: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Unnecessary .map() allocation before .find().

The .map() creates an intermediate array solely to cast each element to Co2Annotation. The cast can be applied inline in the .find() predicate, avoiding the extra allocation.

♻️ Proposed refactor
-  const impact = annotations
-    ?.map((annotation) => annotation as Co2Annotation)
-    .find(
-      (annotation) => typeof annotation.co2_impact === 'number',
-    )?.co2_impact;
+  const impact = annotations
+    ?.find(
+      (annotation) =>
+        typeof (annotation as Co2Annotation).co2_impact === 'number',
+    )
+    ?.co2_impact as number | undefined;
🤖 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/utils/getMessageCo2Impact.ts`
around lines 13 - 17, The `getMessageCo2Impact` logic is allocating an
unnecessary intermediate array by calling `.map()` before `.find()`. Update the
`impact` computation to apply the `Co2Annotation` cast inline within the
`.find()` callback instead of mapping first, so the existing `annotations`
iterable is searched directly without extra allocation.
src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx (2)

61-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hoist constant definitions outside the component.

IMPACT_CO2_SCRIPT_URL and IMPACT_CO2_TYPE are immutable constants but are defined inside the component body, causing re-allocation on every render. Move them above the component.

♻️ Proposed refactor
+const IMPACT_CO2_SCRIPT_URL = 'https://impactco2.fr/iframe.js';
+const IMPACT_CO2_TYPE = 'comparateur/etiquette-animee';
+
 const formatCo2Impact = (kgCo2eq: number): string => {
   return `${(kgCo2eq * 1000).toLocaleString(undefined, {
     maximumFractionDigits: 2,
   })} g CO₂eq`;
 };
@@
-  const IMPACT_CO2_SCRIPT_URL = 'https://impactco2.fr/iframe.js';
-  const IMPACT_CO2_TYPE = 'comparateur/etiquette-animee';
-
   const widgetContainerRef = useCallback(
🤖 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/MessageEnergyIndicator.tsx`
around lines 61 - 62, Hoist the immutable IMPACT_CO2_SCRIPT_URL and
IMPACT_CO2_TYPE definitions out of MessageEnergyIndicator so they are not
recreated on every render. Move these constants to module scope above the
component definition, and keep the component logic using the same symbols
without changing behavior.

72-74: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer .dataset over setAttribute for data attributes.

SonarCloud flags this: using the dataset property is the idiomatic DOM API for data-* attributes and is more concise.

♻️ Proposed refactor
       const script = document.createElement('script');
       script.src = IMPACT_CO2_SCRIPT_URL;
-      script.setAttribute('data-name', 'impact-co2');
-      script.setAttribute('data-type', IMPACT_CO2_TYPE);
-      script.setAttribute('data-search', dataSearch);
+      script.dataset.name = 'impact-co2';
+      script.dataset.type = IMPACT_CO2_TYPE;
+      script.dataset.search = dataSearch;
       container.appendChild(script);
🤖 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/MessageEnergyIndicator.tsx`
around lines 72 - 74, The MessageEnergyIndicator component is setting data-*
attributes with setAttribute; refactor the script setup to use the element’s
dataset property instead. Update the DOM manipulation near the impact-co2 script
initialization so the data-name, data-type, and data-search values are assigned
through dataset on the same script element, preserving the existing values and
behavior.

Source: Linters/SAST tools

src/frontend/apps/conversations/src/pages/globals.css (2)

367-370: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

!important on button gap may be avoidable.

Using gap: 7px !important forces this value globally for all --with-icon--right buttons and makes it hard to override per-component. If the base gap comes from a design system token, consider overriding via a higher-specificity selector or adjusting the token instead.

🤖 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/pages/globals.css` around lines 367 -
370, The `.c__button--with-icon--right` rule is forcing the button spacing with
`gap: 7px !important`, which makes component-level overrides difficult. Remove
the `!important` and adjust the styling through a more specific selector or the
relevant design token used by the button styles so `gap` can be overridden
normally.

140-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Scope the shared tooltip override

.c__tooltip is already defined in the shared Cunningham stylesheet, so this rule changes padding and hides the React Aria arrow for every tooltip using that class. If this is only meant for the energy indicator, scope it to a feature-specific selector instead of overriding the shared one. The !important also makes future overrides harder.

🤖 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/pages/globals.css` around lines 140 -
147, The `.c__tooltip` override in the conversations globals stylesheet is
affecting the shared Cunningham tooltip class, including padding and the React
Aria arrow for every tooltip using that selector. Update the styles to target a
feature-specific wrapper or component scope for the energy indicator instead of
the shared `.c__tooltip` class, and remove the `!important` usage so the shared
tooltip styling remains overridable.
🤖 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.

Nitpick comments:
In
`@src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx`:
- Around line 61-62: Hoist the immutable IMPACT_CO2_SCRIPT_URL and
IMPACT_CO2_TYPE definitions out of MessageEnergyIndicator so they are not
recreated on every render. Move these constants to module scope above the
component definition, and keep the component logic using the same symbols
without changing behavior.
- Around line 72-74: The MessageEnergyIndicator component is setting data-*
attributes with setAttribute; refactor the script setup to use the element’s
dataset property instead. Update the DOM manipulation near the impact-co2 script
initialization so the data-name, data-type, and data-search values are assigned
through dataset on the same script element, preserving the existing values and
behavior.

In
`@src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts`:
- Around line 13-17: The `getMessageCo2Impact` logic is allocating an
unnecessary intermediate array by calling `.map()` before `.find()`. Update the
`impact` computation to apply the `Co2Annotation` cast inline within the
`.find()` callback instead of mapping first, so the existing `annotations`
iterable is searched directly without extra allocation.

In `@src/frontend/apps/conversations/src/pages/globals.css`:
- Around line 367-370: The `.c__button--with-icon--right` rule is forcing the
button spacing with `gap: 7px !important`, which makes component-level overrides
difficult. Remove the `!important` and adjust the styling through a more
specific selector or the relevant design token used by the button styles so
`gap` can be overridden normally.
- Around line 140-147: The `.c__tooltip` override in the conversations globals
stylesheet is affecting the shared Cunningham tooltip class, including padding
and the React Aria arrow for every tooltip using that selector. Update the
styles to target a feature-specific wrapper or component scope for the energy
indicator instead of the shared `.c__tooltip` class, and remove the `!important`
usage so the shared tooltip styling remains overridable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dd83df19-22a5-4897-b627-c83708afbd24

📥 Commits

Reviewing files that changed from the base of the PR and between 46bda7b and bb9b6b5.

⛔ Files ignored due to path filters (2)
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/arrow-up-right.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/leaves.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageEnergyIndicator.test.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/utils/__tests__/getMessageCo2Impact.test.ts
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
  • src/frontend/apps/conversations/src/pages/globals.css
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/frontend/apps/conversations/src/features/chat/utils/tests/getMessageCo2Impact.test.ts
  • src/frontend/apps/conversations/src/features/chat/components/tests/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/components/tests/MessageEnergyIndicator.test.tsx
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx

@elvoisin elvoisin linked an issue Jul 9, 2026 that may be closed by this pull request
3 tasks
@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch 3 times, most recently from 1f330aa to 21b8848 Compare July 10, 2026 08:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts (1)

8-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant map before find.

annotations is already typed as Co2Annotation[] from the cast on line 6, so mapping each element to Co2Annotation again is a no-op. Call find directly.

♻️ Proposed refactor
   const impact = annotations
-    ?.map((annotation) => annotation as Co2Annotation)
-    .find(
+    ?.find(
       (annotation) => typeof annotation.co2_impact === 'number',
     )?.co2_impact;
🤖 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/utils/getMessageCo2Impact.ts`
around lines 8 - 12, Remove the redundant map cast before find in
getMessageCo2Impact, and call find directly on the already typed annotations
array while preserving the existing co2_impact number check and result handling.
src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx (1)

72-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use dataset instead of setAttribute to clear the SonarCloud failures.

SonarCloud flags lines 72–74 as failures. Switching to the dataset API keeps behavior identical and unblocks the check.

♻️ Proposed fix
-      script.setAttribute('data-name', 'impact-co2');
-      script.setAttribute('data-type', IMPACT_CO2_TYPE);
-      script.setAttribute('data-search', dataSearch);
+      script.dataset.name = 'impact-co2';
+      script.dataset.type = IMPACT_CO2_TYPE;
+      script.dataset.search = dataSearch;

As per static analysis hints (SonarCloud: "Prefer .dataset over setAttribute(…)").

🤖 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/MessageEnergyIndicator.tsx`
around lines 72 - 74, Replace the three setAttribute calls in the script setup
with assignments to script.dataset.name, script.dataset.type, and
script.dataset.search, preserving the existing values and behavior.

Source: Linters/SAST tools

🤖 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/MessageEnergyIndicator.tsx`:
- Around line 22-26: Update formatCo2Impact to accept an app locale parameter
and pass that locale to toLocaleString instead of undefined. At the impactLabel
call site, provide the locale derived from i18n.resolvedLanguage, preserving the
existing formatting options and translated widget language.

---

Nitpick comments:
In
`@src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx`:
- Around line 72-74: Replace the three setAttribute calls in the script setup
with assignments to script.dataset.name, script.dataset.type, and
script.dataset.search, preserving the existing values and behavior.

In
`@src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts`:
- Around line 8-12: Remove the redundant map cast before find in
getMessageCo2Impact, and call find directly on the already typed annotations
array while preserving the existing co2_impact number check and result handling.
🪄 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: f8f6c4d6-9fc8-4147-9c52-030358006dd5

📥 Commits

Reviewing files that changed from the base of the PR and between bb9b6b5 and 21b8848.

⛔ Files ignored due to path filters (2)
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/arrow-up-right.svg is excluded by !**/*.svg
  • src/frontend/apps/conversations/src/assets/icons/uikit-custom/leaves.svg is excluded by !**/*.svg
📒 Files selected for processing (12)
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageEnergyIndicator.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageEnergyIndicator.test.tsx
  • src/frontend/apps/conversations/src/features/chat/components/__tests__/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/utils/__tests__/getMessageCo2Impact.test.ts
  • src/frontend/apps/conversations/src/features/chat/utils/__tests__/impactCo2.test.ts
  • src/frontend/apps/conversations/src/features/chat/utils/getMessageCo2Impact.ts
  • src/frontend/apps/conversations/src/features/chat/utils/impactCo2.ts
  • src/frontend/apps/conversations/src/pages/globals.css
✅ Files skipped from review due to trivial changes (4)
  • src/frontend/apps/conversations/src/features/chat/utils/tests/impactCo2.test.ts
  • CHANGELOG.md
  • src/frontend/apps/conversations/src/pages/globals.css
  • src/frontend/apps/conversations/src/features/chat/components/FeedbackButtons.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/frontend/apps/conversations/src/features/chat/utils/tests/getMessageCo2Impact.test.ts
  • src/frontend/apps/conversations/src/features/chat/api/useChat.tsx
  • src/frontend/apps/conversations/src/features/chat/components/tests/MessageItem.test.tsx
  • src/frontend/apps/conversations/src/features/chat/components/MessageItem.tsx

Comment on lines +22 to +26
const formatCo2Impact = (kgCo2eq: number): string => {
return `${(kgCo2eq * 1000).toLocaleString(undefined, {
maximumFractionDigits: 2,
})} g CO₂eq`;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Format the CO₂ number using the active app language.

toLocaleString(undefined, …) uses the runtime/browser default locale rather than the user's selected i18n language, so the number in impactLabel (e.g. decimal/grouping separators) can diverge from the surrounding translated text and from the widget language you already derive from i18n.resolvedLanguage. Pass the app locale through.

🌐 Proposed fix
-const formatCo2Impact = (kgCo2eq: number): string => {
-  return `${(kgCo2eq * 1000).toLocaleString(undefined, {
-    maximumFractionDigits: 2,
-  })} g CO₂eq`;
-};
+const formatCo2Impact = (kgCo2eq: number, locale?: string): string => {
+  return `${(kgCo2eq * 1000).toLocaleString(locale, {
+    maximumFractionDigits: 2,
+  })} g CO₂eq`;
+};

And at the call site:

-  const impactLabel = t('This request: {{co2}}', {
-    co2: formatCo2Impact(co2ImpactKg),
-  });
+  const impactLabel = t('This request: {{co2}}', {
+    co2: formatCo2Impact(co2ImpactKg, i18n.language),
+  });
📝 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
const formatCo2Impact = (kgCo2eq: number): string => {
return `${(kgCo2eq * 1000).toLocaleString(undefined, {
maximumFractionDigits: 2,
})} g CO₂eq`;
};
const formatCo2Impact = (kgCo2eq: number, locale?: string): string => {
return `${(kgCo2eq * 1000).toLocaleString(locale, {
maximumFractionDigits: 2,
})} g CO₂eq`;
};
🤖 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/MessageEnergyIndicator.tsx`
around lines 22 - 26, Update formatCo2Impact to accept an app locale parameter
and pass that locale to toLocaleString instead of undefined. At the impactLabel
call site, provide the locale derived from i18n.resolvedLanguage, preserving the
existing formatting options and translated widget language.

@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch 3 times, most recently from ccec60c to 90cb29e Compare July 10, 2026 09:24
add co2 tooltip on assistant messages + modal
@elvoisin
elvoisin force-pushed the evoisin/feature-co2 branch from 90cb29e to 554ff4a Compare July 17, 2026 14:31
@sonarqubecloud

Copy link
Copy Markdown

@elvoisin
elvoisin merged commit 554ff4a into main Jul 17, 2026
28 of 31 checks passed
@elvoisin
elvoisin deleted the evoisin/feature-co2 branch July 17, 2026 15:02
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.

Add request carbon impact

1 participant