@memori.ai/memori-react 8.31.0 → 8.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.css +351 -0
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.d.ts +34 -0
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.js +105 -0
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.js.map +1 -0
- package/dist/components/Header/ChatConsumptionDropdown.d.ts +8 -0
- package/dist/components/Header/ChatConsumptionDropdown.js +103 -0
- package/dist/components/Header/ChatConsumptionDropdown.js.map +1 -0
- package/dist/components/Header/Header.css +128 -17
- package/dist/components/Header/Header.js +2 -72
- package/dist/components/Header/Header.js.map +1 -1
- package/dist/components/MicrophoneButton/MicrophoneButton.css +14 -3
- package/dist/components/MicrophoneButton/MicrophoneButton.js +1 -1
- package/dist/components/MicrophoneButton/MicrophoneButton.js.map +1 -1
- package/dist/components/PositionPopover/PositionPopover.js +8 -2
- package/dist/components/PositionPopover/PositionPopover.js.map +1 -1
- package/dist/components/icons/GasStation.d.ts +6 -0
- package/dist/components/icons/GasStation.js +6 -0
- package/dist/components/icons/GasStation.js.map +1 -0
- package/dist/components/ui/Tooltip.js +6 -3
- package/dist/components/ui/Tooltip.js.map +1 -1
- package/dist/locales/de.json +3 -0
- package/dist/locales/en.json +3 -0
- package/dist/locales/es.json +3 -0
- package/dist/locales/fr.json +3 -0
- package/dist/locales/it.json +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.css +351 -0
- package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.d.ts +34 -0
- package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.js +102 -0
- package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.js.map +1 -0
- package/esm/components/Header/ChatConsumptionDropdown.d.ts +8 -0
- package/esm/components/Header/ChatConsumptionDropdown.js +100 -0
- package/esm/components/Header/ChatConsumptionDropdown.js.map +1 -0
- package/esm/components/Header/Header.css +128 -17
- package/esm/components/Header/Header.js +3 -73
- package/esm/components/Header/Header.js.map +1 -1
- package/esm/components/MicrophoneButton/MicrophoneButton.css +14 -3
- package/esm/components/MicrophoneButton/MicrophoneButton.js +1 -1
- package/esm/components/MicrophoneButton/MicrophoneButton.js.map +1 -1
- package/esm/components/PositionPopover/PositionPopover.js +9 -3
- package/esm/components/PositionPopover/PositionPopover.js.map +1 -1
- package/esm/components/icons/GasStation.d.ts +6 -0
- package/esm/components/icons/GasStation.js +4 -0
- package/esm/components/icons/GasStation.js.map +1 -0
- package/esm/components/ui/Tooltip.js +6 -3
- package/esm/components/ui/Tooltip.js.map +1 -1
- package/esm/locales/de.json +3 -0
- package/esm/locales/en.json +3 -0
- package/esm/locales/es.json +3 -0
- package/esm/locales/fr.json +3 -0
- package/esm/locales/it.json +3 -0
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +2 -2
- package/src/components/Header/ChatConsumptionDropdown.test.tsx +86 -0
- package/src/components/Header/ChatConsumptionDropdown.tsx +266 -0
- package/src/components/Header/Header.css +128 -17
- package/src/components/Header/Header.stories.tsx +48 -41
- package/src/components/Header/Header.tsx +7 -147
- package/src/components/MicrophoneButton/MicrophoneButton.css +14 -3
- package/src/components/MicrophoneButton/MicrophoneButton.tsx +12 -3
- package/src/components/icons/GasStation.tsx +36 -0
- package/src/components/ui/Tooltip.tsx +12 -3
- package/src/locales/de.json +3 -0
- package/src/locales/en.json +3 -0
- package/src/locales/es.json +3 -0
- package/src/locales/fr.json +3 -0
- package/src/locales/it.json +3 -0
- package/src/version.ts +1 -1
package/esm/locales/fr.json
CHANGED
|
@@ -418,6 +418,9 @@
|
|
|
418
418
|
"co2": "CO2",
|
|
419
419
|
"water": "Eau",
|
|
420
420
|
"usageBadgesHint": "Cliquez sur un de ces boutons pour afficher plus d'informations",
|
|
421
|
+
"totalChatConsumptionTitle": "Consommation Totale du Chat",
|
|
422
|
+
"modelUsage": "Utilisation du modèle",
|
|
423
|
+
"environmentalImpact": "Impact environnemental",
|
|
421
424
|
"impactComparisonUnavailable": "Comparaison indicative non disponible.",
|
|
422
425
|
"impactComparisonEnergy": "Comparaison indicative : environ comme laisser allumee une ampoule LED de 10 W pendant {{duration}}.",
|
|
423
426
|
"impactComparisonCo2": "Comparaison indicative : environ {{distance}} parcourus en voiture essence moyenne.",
|
package/esm/locales/it.json
CHANGED
|
@@ -450,6 +450,9 @@
|
|
|
450
450
|
"co2": "CO2",
|
|
451
451
|
"water": "Acqua",
|
|
452
452
|
"usageBadgesHint": "Clicca uno di questi pulsanti per mostrare maggiori informazioni",
|
|
453
|
+
"totalChatConsumptionTitle": "Consumo Totale Chat",
|
|
454
|
+
"modelUsage": "Utilizzo del modello",
|
|
455
|
+
"environmentalImpact": "Impatto ambientale",
|
|
453
456
|
"impactComparisonUnavailable": "Confronto indicativo non disponibile.",
|
|
454
457
|
"impactComparisonEnergy": "Confronto indicativo: circa quanto tenere accesa una lampadina LED da 10 W per {{duration}}.",
|
|
455
458
|
"impactComparisonCo2": "Confronto indicativo: circa {{distance}} percorsi in auto a benzina media.",
|
package/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "8.
|
|
1
|
+
export declare const version = "8.33.0";
|
package/esm/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '8.
|
|
1
|
+
export const version = '8.33.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "8.
|
|
2
|
+
"version": "8.33.0",
|
|
3
3
|
"name": "@memori.ai/memori-react",
|
|
4
4
|
"author": "Memori Srl",
|
|
5
5
|
"main": "dist/index.js",
|
|
@@ -297,7 +297,7 @@
|
|
|
297
297
|
"@react-three/drei": "8.20.2",
|
|
298
298
|
"@react-three/fiber": "7.0.25",
|
|
299
299
|
"classnames": "2.5.1",
|
|
300
|
-
"dompurify": "^3.
|
|
300
|
+
"dompurify": "^3.4.0",
|
|
301
301
|
"ellipsed": "1.6.0",
|
|
302
302
|
"i18next": "22.0.6",
|
|
303
303
|
"katex": "^0.16.11",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import { Message } from '@memori.ai/memori-api-client/dist/types';
|
|
4
|
+
import ChatConsumptionDropdown from './ChatConsumptionDropdown';
|
|
5
|
+
|
|
6
|
+
type TestMessage = Message & {
|
|
7
|
+
llmUsage?: {
|
|
8
|
+
provider?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
totalInputTokens?: number;
|
|
11
|
+
outputTokens?: number;
|
|
12
|
+
energyImpact?: {
|
|
13
|
+
energy?: number | { parsedValue?: number };
|
|
14
|
+
gwp?: number | { parsedValue?: number; source?: string };
|
|
15
|
+
wcf?: number | { parsedValue?: number; source?: string };
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe('ChatConsumptionDropdown', () => {
|
|
21
|
+
it('renders aggregated token and environmental usage', () => {
|
|
22
|
+
const history = [
|
|
23
|
+
{
|
|
24
|
+
text: 'First response',
|
|
25
|
+
timestamp: '2021-03-01T12:00:00.000Z',
|
|
26
|
+
llmUsage: {
|
|
27
|
+
provider: 'OpenAI',
|
|
28
|
+
model: 'gpt-5',
|
|
29
|
+
totalInputTokens: 1000,
|
|
30
|
+
outputTokens: 200,
|
|
31
|
+
energyImpact: {
|
|
32
|
+
energy: { parsedValue: 0.0012 },
|
|
33
|
+
gwp: { parsedValue: 0.00045 },
|
|
34
|
+
wcf: { parsedValue: 0.0021 },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
text: 'Second response',
|
|
40
|
+
timestamp: '2021-03-01T12:01:00.000Z',
|
|
41
|
+
llmUsage: {
|
|
42
|
+
provider: 'Anthropic',
|
|
43
|
+
model: 'claude-3',
|
|
44
|
+
totalInputTokens: 250,
|
|
45
|
+
outputTokens: 50,
|
|
46
|
+
energyImpact: {
|
|
47
|
+
energy: 0.0008,
|
|
48
|
+
gwp: { source: '0.00035' },
|
|
49
|
+
wcf: { source: '0.0014' },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
] as TestMessage[];
|
|
54
|
+
|
|
55
|
+
render(<ChatConsumptionDropdown history={history} />);
|
|
56
|
+
|
|
57
|
+
fireEvent.click(screen.getByTitle('write_and_speak.showMessageConsumptionLabel'));
|
|
58
|
+
|
|
59
|
+
expect(screen.getByText('chatLogs.totalChatConsumptionTitle')).toBeTruthy();
|
|
60
|
+
expect(screen.getByText('chatLogs.modelUsage')).toBeTruthy();
|
|
61
|
+
expect(screen.getByText('chatLogs.environmentalImpact')).toBeTruthy();
|
|
62
|
+
expect(screen.getByText('1,250')).toBeTruthy();
|
|
63
|
+
expect(screen.getByText('250')).toBeTruthy();
|
|
64
|
+
expect(screen.getByText('OpenAI · gpt-5')).toBeTruthy();
|
|
65
|
+
expect(screen.getByText('Anthropic · claude-3')).toBeTruthy();
|
|
66
|
+
expect(screen.getByText('2 Wh')).toBeTruthy();
|
|
67
|
+
expect(screen.getByText('800 mg')).toBeTruthy();
|
|
68
|
+
expect(screen.getByText('3.5 mL')).toBeTruthy();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('does not render when the chat has no llm usage data', () => {
|
|
72
|
+
const history = [
|
|
73
|
+
{
|
|
74
|
+
text: 'Plain message',
|
|
75
|
+
timestamp: '2021-03-01T12:00:00.000Z',
|
|
76
|
+
},
|
|
77
|
+
] as TestMessage[];
|
|
78
|
+
|
|
79
|
+
const { container } = render(<ChatConsumptionDropdown history={history} />);
|
|
80
|
+
|
|
81
|
+
expect(container.firstChild).toBeNull();
|
|
82
|
+
expect(
|
|
83
|
+
screen.queryByTitle('write_and_speak.showMessageConsumptionLabel')
|
|
84
|
+
).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import { Message } from '@memori.ai/memori-api-client/dist/types';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import Button from '../ui/Button';
|
|
6
|
+
import Dropdown from '../ui/Dropdown';
|
|
7
|
+
import GasStation from '../icons/GasStation';
|
|
8
|
+
import { BADGE_EMOJI } from '../../helpers/llmUsage';
|
|
9
|
+
|
|
10
|
+
type ImpactMetricType = 'energy' | 'co2' | 'water';
|
|
11
|
+
|
|
12
|
+
type LlmUsageEnergyImpact = {
|
|
13
|
+
energy?: number | { source?: string; parsedValue?: number };
|
|
14
|
+
gwp?: number | { source?: string; parsedValue?: number };
|
|
15
|
+
wcf?: number | { source?: string; parsedValue?: number };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type MessageLlmUsage = {
|
|
19
|
+
provider?: string;
|
|
20
|
+
model?: string;
|
|
21
|
+
totalInputTokens?: number;
|
|
22
|
+
outputTokens?: number;
|
|
23
|
+
energyImpact?: LlmUsageEnergyImpact;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface ChatConsumptionDropdownProps {
|
|
27
|
+
history: Message[];
|
|
28
|
+
hasSpacedButtons?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getMetricValue = (
|
|
32
|
+
metric?: number | { source?: string; parsedValue?: number }
|
|
33
|
+
): number | undefined => {
|
|
34
|
+
if (typeof metric === 'number' && Number.isFinite(metric)) return metric;
|
|
35
|
+
if (!metric || typeof metric !== 'object') return undefined;
|
|
36
|
+
if (
|
|
37
|
+
typeof metric.parsedValue === 'number' &&
|
|
38
|
+
Number.isFinite(metric.parsedValue)
|
|
39
|
+
) {
|
|
40
|
+
return metric.parsedValue;
|
|
41
|
+
}
|
|
42
|
+
if (typeof metric.source === 'string') {
|
|
43
|
+
const parsed = Number(metric.source);
|
|
44
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const formatMetricValue = (value: number, locale: string): string =>
|
|
50
|
+
new Intl.NumberFormat(locale, {
|
|
51
|
+
minimumFractionDigits: 0,
|
|
52
|
+
maximumFractionDigits: Math.abs(value) >= 1 ? 3 : 4,
|
|
53
|
+
}).format(value);
|
|
54
|
+
|
|
55
|
+
const formatCountValue = (value: number, locale: string): string =>
|
|
56
|
+
new Intl.NumberFormat(locale, {
|
|
57
|
+
maximumFractionDigits: 0,
|
|
58
|
+
}).format(value);
|
|
59
|
+
|
|
60
|
+
const formatImpactInReadableUnit = (
|
|
61
|
+
value: number,
|
|
62
|
+
metricType: ImpactMetricType,
|
|
63
|
+
locale: string
|
|
64
|
+
): string => {
|
|
65
|
+
const absValue = Math.abs(value);
|
|
66
|
+
|
|
67
|
+
if (metricType === 'energy') {
|
|
68
|
+
if (absValue >= 1) return `${formatMetricValue(value, locale)} kWh`;
|
|
69
|
+
const wh = value * 1000;
|
|
70
|
+
if (Math.abs(wh) >= 1) return `${formatMetricValue(wh, locale)} Wh`;
|
|
71
|
+
return `${formatMetricValue(wh * 1000, locale)} mWh`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (metricType === 'co2') {
|
|
75
|
+
if (absValue >= 1) return `${formatMetricValue(value, locale)} kg`;
|
|
76
|
+
const g = value * 1000;
|
|
77
|
+
if (Math.abs(g) >= 1) return `${formatMetricValue(g, locale)} g`;
|
|
78
|
+
return `${formatMetricValue(g * 1000, locale)} mg`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (absValue >= 1) return `${formatMetricValue(value, locale)} L`;
|
|
82
|
+
const ml = value * 1000;
|
|
83
|
+
if (Math.abs(ml) >= 1) return `${formatMetricValue(ml, locale)} mL`;
|
|
84
|
+
return `${formatMetricValue(ml * 1000, locale)} μL`;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const ChatConsumptionDropdown: React.FC<ChatConsumptionDropdownProps> = ({
|
|
88
|
+
history,
|
|
89
|
+
hasSpacedButtons = false,
|
|
90
|
+
}) => {
|
|
91
|
+
const { t, i18n } = useTranslation();
|
|
92
|
+
|
|
93
|
+
const currentLocale = i18n.language || navigator.language || 'en';
|
|
94
|
+
const chatLog = useMemo(() => ({ lines: history }), [history]);
|
|
95
|
+
|
|
96
|
+
const chatConsumptionTotals = useMemo(() => {
|
|
97
|
+
const totals = {
|
|
98
|
+
totalInputTokens: 0,
|
|
99
|
+
totalOutputTokens: 0,
|
|
100
|
+
energy: 0,
|
|
101
|
+
gwp: 0,
|
|
102
|
+
wcf: 0,
|
|
103
|
+
models: new Set<string>(),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
(chatLog?.lines ?? []).forEach(line => {
|
|
107
|
+
const llmUsage = (line as Message & {
|
|
108
|
+
llmUsage?: MessageLlmUsage;
|
|
109
|
+
}).llmUsage;
|
|
110
|
+
|
|
111
|
+
if (!llmUsage) return;
|
|
112
|
+
|
|
113
|
+
totals.totalInputTokens += llmUsage.totalInputTokens ?? 0;
|
|
114
|
+
totals.totalOutputTokens += llmUsage.outputTokens ?? 0;
|
|
115
|
+
|
|
116
|
+
if (llmUsage.provider || llmUsage.model) {
|
|
117
|
+
totals.models.add(
|
|
118
|
+
[llmUsage.provider, llmUsage.model].filter(Boolean).join(' · ')
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!llmUsage.energyImpact) return;
|
|
123
|
+
|
|
124
|
+
const impact = llmUsage.energyImpact;
|
|
125
|
+
totals.energy += getMetricValue(impact.energy) ?? 0;
|
|
126
|
+
totals.gwp += getMetricValue(impact.gwp) ?? 0;
|
|
127
|
+
totals.wcf += getMetricValue(impact.wcf) ?? 0;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return totals;
|
|
131
|
+
}, [chatLog]);
|
|
132
|
+
|
|
133
|
+
const llmUsageModels = useMemo(
|
|
134
|
+
() => Array.from(chatConsumptionTotals.models),
|
|
135
|
+
[chatConsumptionTotals.models]
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const hasConsumptionData = useMemo(
|
|
139
|
+
() =>
|
|
140
|
+
(chatLog?.lines ?? []).some(
|
|
141
|
+
line =>
|
|
142
|
+
!!(line as Message & { llmUsage?: MessageLlmUsage }).llmUsage
|
|
143
|
+
),
|
|
144
|
+
[chatLog]
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
if (!hasConsumptionData) return null;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<Dropdown
|
|
151
|
+
placement="bottom-right"
|
|
152
|
+
trigger={
|
|
153
|
+
<Button
|
|
154
|
+
primary
|
|
155
|
+
shape="circle"
|
|
156
|
+
className={cx(
|
|
157
|
+
'memori-header--button',
|
|
158
|
+
'memori-header--button--sustainability',
|
|
159
|
+
hasSpacedButtons && 'memori-header--button-spaced'
|
|
160
|
+
)}
|
|
161
|
+
title={t('write_and_speak.showMessageConsumptionLabel') || 'LLM consumption'}
|
|
162
|
+
icon={<GasStation className="memori-header--button--sustainability-icon" />}
|
|
163
|
+
/>
|
|
164
|
+
}
|
|
165
|
+
>
|
|
166
|
+
<div className="memori-dropdown--sustainability">
|
|
167
|
+
<h4 className="memori-dropdown--sustainability-title">
|
|
168
|
+
{t('chatLogs.totalChatConsumptionTitle') || 'Consumo Totale Chat'}
|
|
169
|
+
</h4>
|
|
170
|
+
<div className="memori-dropdown--sustainability-section">
|
|
171
|
+
<h5 className="memori-dropdown--sustainability-section-title">
|
|
172
|
+
{t('chatLogs.modelUsage') || 'Model usage'}
|
|
173
|
+
</h5>
|
|
174
|
+
<div className="memori-dropdown--sustainability-summary">
|
|
175
|
+
<div className="memori-dropdown--sustainability-stat">
|
|
176
|
+
<span className="memori-dropdown--sustainability-stat-label">
|
|
177
|
+
{t('chatLogs.input') || 'Input'}
|
|
178
|
+
</span>
|
|
179
|
+
<strong className="memori-dropdown--sustainability-stat-value">
|
|
180
|
+
{formatCountValue(chatConsumptionTotals.totalInputTokens, currentLocale)}
|
|
181
|
+
</strong>
|
|
182
|
+
<span className="memori-dropdown--sustainability-stat-meta">
|
|
183
|
+
{t('chatLogs.tokens') || 'Tokens'}
|
|
184
|
+
</span>
|
|
185
|
+
</div>
|
|
186
|
+
<div className="memori-dropdown--sustainability-stat">
|
|
187
|
+
<span className="memori-dropdown--sustainability-stat-label">
|
|
188
|
+
{t('chatLogs.output') || 'Output'}
|
|
189
|
+
</span>
|
|
190
|
+
<strong className="memori-dropdown--sustainability-stat-value">
|
|
191
|
+
{formatCountValue(chatConsumptionTotals.totalOutputTokens, currentLocale)}
|
|
192
|
+
</strong>
|
|
193
|
+
<span className="memori-dropdown--sustainability-stat-meta">
|
|
194
|
+
{t('chatLogs.tokens') || 'Tokens'}
|
|
195
|
+
</span>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
{llmUsageModels.length > 0 && (
|
|
199
|
+
<div className="memori-dropdown--sustainability-row memori-dropdown--sustainability-row--stacked">
|
|
200
|
+
<span className="memori-dropdown--sustainability-label">
|
|
201
|
+
{t('chatLogs.provider') || 'Provider'} /{' '}
|
|
202
|
+
{t('chatLogs.model') || 'Model'}
|
|
203
|
+
</span>
|
|
204
|
+
<div className="memori-dropdown--sustainability-tags">
|
|
205
|
+
{llmUsageModels.map(modelLabel => (
|
|
206
|
+
<span
|
|
207
|
+
key={modelLabel}
|
|
208
|
+
className="memori-dropdown--sustainability-tag"
|
|
209
|
+
>
|
|
210
|
+
{modelLabel}
|
|
211
|
+
</span>
|
|
212
|
+
))}
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
<div className="memori-dropdown--sustainability-metrics">
|
|
218
|
+
<h5 className="memori-dropdown--sustainability-section-title">
|
|
219
|
+
{t('chatLogs.environmentalImpact') || 'Environmental impact'}
|
|
220
|
+
</h5>
|
|
221
|
+
<div className="memori-dropdown--sustainability-row">
|
|
222
|
+
<span className="memori-dropdown--sustainability-label">
|
|
223
|
+
<span aria-hidden="true">{BADGE_EMOJI.energy}</span>{' '}
|
|
224
|
+
{t('chatLogs.energy') || 'Energy'}
|
|
225
|
+
</span>
|
|
226
|
+
<strong className="memori-dropdown--sustainability-value">
|
|
227
|
+
{formatImpactInReadableUnit(
|
|
228
|
+
chatConsumptionTotals.energy,
|
|
229
|
+
'energy',
|
|
230
|
+
currentLocale
|
|
231
|
+
)}
|
|
232
|
+
</strong>
|
|
233
|
+
</div>
|
|
234
|
+
<div className="memori-dropdown--sustainability-row">
|
|
235
|
+
<span className="memori-dropdown--sustainability-label">
|
|
236
|
+
<span aria-hidden="true">{BADGE_EMOJI.co2}</span>{' '}
|
|
237
|
+
{t('chatLogs.co2') || 'CO2'}
|
|
238
|
+
</span>
|
|
239
|
+
<strong className="memori-dropdown--sustainability-value">
|
|
240
|
+
{formatImpactInReadableUnit(
|
|
241
|
+
chatConsumptionTotals.gwp,
|
|
242
|
+
'co2',
|
|
243
|
+
currentLocale
|
|
244
|
+
)}
|
|
245
|
+
</strong>
|
|
246
|
+
</div>
|
|
247
|
+
<div className="memori-dropdown--sustainability-row">
|
|
248
|
+
<span className="memori-dropdown--sustainability-label">
|
|
249
|
+
<span aria-hidden="true">{BADGE_EMOJI.water}</span>{' '}
|
|
250
|
+
{t('chatLogs.water') || 'Water'}
|
|
251
|
+
</span>
|
|
252
|
+
<strong className="memori-dropdown--sustainability-value">
|
|
253
|
+
{formatImpactInReadableUnit(
|
|
254
|
+
chatConsumptionTotals.wcf,
|
|
255
|
+
'water',
|
|
256
|
+
currentLocale
|
|
257
|
+
)}
|
|
258
|
+
</strong>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</Dropdown>
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
export default ChatConsumptionDropdown;
|
|
@@ -97,12 +97,19 @@
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
.memori-header--button--sustainability-icon {
|
|
101
|
+
width: 1em;
|
|
102
|
+
color: currentColor;
|
|
103
|
+
}
|
|
100
104
|
.memori-header .memori-header--button--position {
|
|
101
105
|
margin-right: 0;
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
.memori-header--button--sustainability {
|
|
109
|
+
max-height: 37px;
|
|
110
|
+
}
|
|
104
111
|
|
|
105
|
-
.memori-dropdown--avatar-input{
|
|
112
|
+
.memori-dropdown--avatar-input {
|
|
106
113
|
position: absolute;
|
|
107
114
|
top: 20px;
|
|
108
115
|
left: 15px;
|
|
@@ -112,60 +119,164 @@
|
|
|
112
119
|
opacity: 0;
|
|
113
120
|
}
|
|
114
121
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
.memori-dropdown--avatar
|
|
122
|
+
.memori-dropdown--avatar:hover,
|
|
123
|
+
.memori-dropdown--avatar-initial:hover,
|
|
124
|
+
.memori-dropdown--avatar-input:hover {
|
|
118
125
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
|
|
119
126
|
cursor: pointer;
|
|
120
127
|
pointer-events: cursor;
|
|
121
128
|
transform: scale(1.05);
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
.memori-dropdown--avatar-initial:hover + .memori-dropdown--avatar-input
|
|
131
|
+
.memori-dropdown--avatar-initial:hover + .memori-dropdown--avatar-input,
|
|
132
|
+
.memori-dropdown--avatar:hover + .memori-dropdown--avatar-input {
|
|
125
133
|
display: block;
|
|
126
134
|
}
|
|
127
135
|
|
|
128
136
|
.memori-dropdown--sustainability {
|
|
129
|
-
min-width:
|
|
130
|
-
padding: 0.
|
|
131
|
-
border
|
|
132
|
-
|
|
137
|
+
min-width: 300px;
|
|
138
|
+
padding: 0.85rem;
|
|
139
|
+
border: 1px solid color-mix(in srgb, var(--memori-button-border-color, #d9d9d9) 75%, white);
|
|
140
|
+
border-radius: 12px;
|
|
141
|
+
background: linear-gradient(180deg, rgba(252, 253, 255, 0.98) 0%, rgba(245, 248, 252, 0.98) 100%);
|
|
142
|
+
box-shadow: 0 14px 34px rgba(15, 23, 42, 0.1);
|
|
133
143
|
}
|
|
134
144
|
|
|
135
145
|
.memori-dropdown--sustainability-title {
|
|
136
|
-
|
|
146
|
+
padding-bottom: 12px;
|
|
147
|
+
border-bottom: 1px solid rgba(15, 23, 42, 0.08);
|
|
148
|
+
margin: 0.2rem 0.2rem 1rem;
|
|
149
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 92%, #36506b);
|
|
137
150
|
font-size: 0.95rem;
|
|
138
151
|
font-weight: 700;
|
|
139
152
|
line-height: 1.2;
|
|
140
|
-
text-align:
|
|
153
|
+
text-align: left;
|
|
141
154
|
}
|
|
142
155
|
|
|
156
|
+
.memori-dropdown--sustainability-section,
|
|
143
157
|
.memori-dropdown--sustainability-metrics {
|
|
144
158
|
display: flex;
|
|
145
159
|
flex-direction: column;
|
|
146
160
|
gap: 0.35rem;
|
|
147
161
|
}
|
|
148
162
|
|
|
163
|
+
.memori-dropdown--sustainability-section-title {
|
|
164
|
+
margin: 0 0 0.1rem;
|
|
165
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 62%, #64748b);
|
|
166
|
+
font-size: 0.71rem;
|
|
167
|
+
font-weight: 700;
|
|
168
|
+
letter-spacing: 0.06em;
|
|
169
|
+
line-height: 1.2;
|
|
170
|
+
text-transform: uppercase;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.memori-dropdown--sustainability-section {
|
|
174
|
+
padding-bottom: 0.65rem;
|
|
175
|
+
margin-bottom: 0.65rem;
|
|
176
|
+
/* border-bottom: 1px solid rgba(15, 23, 42, 0.08); */
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.memori-dropdown--sustainability-summary {
|
|
180
|
+
display: grid;
|
|
181
|
+
gap: 0.5rem;
|
|
182
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.memori-dropdown--sustainability-stat {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
padding: 0.55rem 0.65rem;
|
|
189
|
+
border: 1px solid rgba(15, 23, 42, 0.07);
|
|
190
|
+
border-radius: 10px;
|
|
191
|
+
background: rgba(255, 255, 255, 0.78);
|
|
192
|
+
gap: 0.15rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.memori-dropdown--sustainability-stat-label,
|
|
196
|
+
.memori-dropdown--sustainability-stat-meta {
|
|
197
|
+
font-size: 0.72rem;
|
|
198
|
+
line-height: 1.2;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.memori-dropdown--sustainability-stat-label {
|
|
202
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 68%, #6b7280);
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
letter-spacing: 0.02em;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.memori-dropdown--sustainability-stat-value {
|
|
208
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 94%, #1d4ed8);
|
|
209
|
+
font-size: 1rem;
|
|
210
|
+
font-weight: 700;
|
|
211
|
+
line-height: 1.1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.memori-dropdown--sustainability-stat-meta {
|
|
215
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 55%, #64748b);
|
|
216
|
+
}
|
|
217
|
+
|
|
149
218
|
.memori-dropdown--sustainability-row {
|
|
150
219
|
display: flex;
|
|
151
220
|
align-items: center;
|
|
152
221
|
justify-content: space-between;
|
|
153
|
-
padding: 0.
|
|
154
|
-
border
|
|
155
|
-
|
|
222
|
+
padding: 0.5rem 0.6rem;
|
|
223
|
+
border: 1px solid rgba(15, 23, 42, 0.05);
|
|
224
|
+
border-radius: 10px;
|
|
225
|
+
background: rgba(255, 255, 255, 0.72);
|
|
156
226
|
gap: 1rem;
|
|
157
227
|
}
|
|
158
228
|
|
|
229
|
+
.memori-dropdown--sustainability-row--stacked {
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
align-items: flex-start;
|
|
232
|
+
gap: 0.45rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
159
235
|
.memori-dropdown--sustainability-label {
|
|
160
236
|
display: inline-flex;
|
|
161
237
|
align-items: center;
|
|
238
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 76%, #64748b);
|
|
162
239
|
font-size: 0.82rem;
|
|
163
|
-
font-weight:
|
|
240
|
+
font-weight: 600;
|
|
164
241
|
gap: 0.35rem;
|
|
165
|
-
opacity: 0.9;
|
|
166
242
|
}
|
|
167
243
|
|
|
168
244
|
.memori-dropdown--sustainability-value {
|
|
169
|
-
|
|
245
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 96%, black);
|
|
246
|
+
font-size: 0.88rem;
|
|
247
|
+
font-weight: 700;
|
|
170
248
|
white-space: nowrap;
|
|
171
249
|
}
|
|
250
|
+
|
|
251
|
+
.memori-dropdown--sustainability-value--multiline {
|
|
252
|
+
width: 100%;
|
|
253
|
+
overflow-wrap: anywhere;
|
|
254
|
+
white-space: normal;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.memori-dropdown--sustainability-tags {
|
|
258
|
+
display: flex;
|
|
259
|
+
width: 100%;
|
|
260
|
+
flex-wrap: wrap;
|
|
261
|
+
gap: 0.35rem;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.memori-dropdown--sustainability-tag {
|
|
265
|
+
display: inline-flex;
|
|
266
|
+
min-height: 1.75rem;
|
|
267
|
+
align-items: center;
|
|
268
|
+
padding: 0.2rem 0.55rem;
|
|
269
|
+
/* border: 1px solid rgba(15, 23, 42, 0.08); */
|
|
270
|
+
border-radius: 999px;
|
|
271
|
+
background: rgba(255, 255, 255, 0.88);
|
|
272
|
+
color: color-mix(in srgb, var(--memori-text-color, #111827) 85%, #475569);
|
|
273
|
+
font-size: 0.76rem;
|
|
274
|
+
font-weight: 600;
|
|
275
|
+
line-height: 1.1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@media (max-width: 480px) {
|
|
279
|
+
.memori-dropdown--sustainability {
|
|
280
|
+
min-width: min(300px, calc(100vw - 2rem));
|
|
281
|
+
}
|
|
282
|
+
}
|