@stokelp/ui 2.112.0 → 2.113.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.
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../text/Text.cjs");let t=require("@stokelp/styled-system/jsx"),n=require("react/jsx-runtime");var r=1,i=[`origin`,`rawMaterialOrigin`,`temperature`,`cutting`,`cooking`,`treatment`,`label`,`packaging`,`grammageGsm`,`recycledPct`,`thicknessMicron`],a=[`temperature`,`cutting`,`cooking`],o=[`grammageGsm`,`recycledPct`,`thicknessMicron`],s={egal:{bg:`#E6F3D9`,color:`#3B6D11`},proche:{bg:`#F0F6DC`,color:`#5E8516`},eloigne:{bg:`#FDF0E0`,color:`#C2740C`},non_correspondant:{bg:`#FCEBEB`,color:`#A32D2D`}};function c(e){return e>=70?`#3B6D11`:e>=40?`#854F0B`:`#A32D2D`}function l(e){return e>=70?`#639922`:e>=40?`#BA7517`:`#E24B4A`}function u(e){return e?.level!=null}function d(e){return e?.matched!=null&&e?.total!=null}function f(e){return u(e)||d(e)}var p=({percent:d,detail:p,labels:m,typologyId:h})=>{let g=Number(h)===r||o.some(e=>p[e]?.level!=null),_=new Set(g?a:o),v=i.filter(e=>!_.has(e)),y=v.filter(e=>f(p[e])),b=v.filter(e=>!f(p[e])&&m.attributes[e]!=null);return(0,n.jsxs)(t.Box,{bg:`white`,borderRadius:`radius-8`,p:`space-16`,minW:`220px`,maxW:`240px`,css:{"& *":{fontFamily:`inherit`}},children:[(0,n.jsxs)(t.HStack,{justify:`space-between`,alignItems:`baseline`,mb:`space-8`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.md`,fontWeight:`bold`,children:m.title}),(0,n.jsxs)(e.Text,{as:`span`,fontSize:`body.lg`,fontWeight:`medium`,style:{color:c(d)},children:[Math.round(d),` %`]})]}),(0,n.jsx)(t.Box,{h:`1px`,bg:`grey.100`,mb:`space-8`}),y.length>0&&(0,n.jsxs)(t.VStack,{alignItems:`stretch`,gap:`space-4`,mb:`space-8`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`bold`,color:`grey.400`,children:m.compared}),y.map(r=>{let i=p[r];if(u(i)){let a=i.level,o=s[a];return(0,n.jsxs)(t.HStack,{alignItems:`center`,gap:`space-8`,justify:`space-between`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,color:`grey.500`,minW:`80px`,children:m.attributes[r]}),(0,n.jsx)(t.Box,{px:`space-8`,py:`2px`,borderRadius:`full`,flexShrink:`0`,style:{backgroundColor:o.bg},children:(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`medium`,style:{color:o.color,whiteSpace:`nowrap`},children:m.levels?.[a]??a})})]},r)}let a=i.matched,o=i.total,c=o>0?a/o*100:0;return(0,n.jsxs)(t.HStack,{alignItems:`center`,gap:`space-8`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,color:`grey.500`,minW:`80px`,children:m.attributes[r]}),(0,n.jsx)(t.Box,{flex:`1`,h:`3px`,bg:`grey.100`,borderRadius:`full`,overflow:`hidden`,children:(0,n.jsx)(t.Box,{h:`100%`,borderRadius:`full`,style:{width:`${c}%`,background:l(c)}})}),(0,n.jsxs)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`medium`,minW:`40px`,textAlign:`right`,style:{color:l(c)},children:[a,`/`,o]})]},r)})]}),y.length>0&&b.length>0&&(0,n.jsx)(t.Box,{h:`1px`,bg:`grey.100`,mb:`space-8`}),b.length>0&&(0,n.jsxs)(t.VStack,{alignItems:`stretch`,gap:`space-4`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`bold`,color:`grey.400`,children:m.notSpecified}),(0,n.jsx)(t.HStack,{flexWrap:`wrap`,gap:`space-4`,children:b.map(t=>(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,color:`grey.400`,border:`1px solid`,borderColor:`grey.100`,px:`space-8`,py:`2px`,borderRadius:`full`,children:m.attributes[t]},t))})]}),m.footer&&(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.Box,{h:`1px`,bg:`grey.100`,mt:`space-8`,mb:`space-8`}),(0,n.jsx)(e.Text,{as:`span`,fontSize:`9px`,color:`grey.300`,style:{lineHeight:1},children:m.footer})]})]})};exports.ChatScoreBreakdown=p;
1
+ "use client";const e=require("../text/Text.cjs");let t=require("@stokelp/styled-system/jsx"),n=require("react/jsx-runtime");var r=1,i=[`origin`,`rawMaterialOrigin`,`temperature`,`cutting`,`cooking`,`treatment`,`label`,`packaging`,`grammageGsm`,`recycledPct`,`thicknessMicron`],a=[`temperature`,`cutting`,`cooking`],o=[`grammageGsm`,`recycledPct`,`thicknessMicron`],s={egal:{bg:`#E6F3D9`,color:`#3B6D11`},proche:{bg:`#F0F6DC`,color:`#5E8516`},eloigne:{bg:`#FDF0E0`,color:`#C2740C`},non_correspondant:{bg:`#FCEBEB`,color:`#A32D2D`}};function c(e){return e>=70?`#3B6D11`:e>=40?`#854F0B`:`#A32D2D`}function l(e){return e>=70?`#639922`:e>=40?`#BA7517`:`#E24B4A`}function u(e){return e?.level!=null}function d(e){return e?.matched!=null&&e?.total!=null}function f(e){return u(e)||d(e)}var p=({percent:d,detail:p,labels:m,typologyId:h})=>{let g=Number(h)===r||o.some(e=>p[e]?.level!=null),_=new Set(g?a:o),v=i.filter(e=>!_.has(e)),y=v.filter(e=>f(p[e])),b=v.filter(e=>!f(p[e])&&m.attributes[e]!=null);return(0,n.jsxs)(t.Box,{bg:`white`,borderRadius:`radius-8`,p:`space-16`,minW:`220px`,maxW:`240px`,css:{"& *":{fontFamily:`inherit`}},children:[(0,n.jsxs)(t.HStack,{justify:`space-between`,alignItems:`baseline`,mb:`space-8`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.md`,fontWeight:`bold`,children:m.title}),(0,n.jsxs)(e.Text,{as:`span`,fontSize:`body.lg`,fontWeight:`medium`,style:{color:c(d)},children:[Math.round(d),` %`]})]}),(0,n.jsx)(t.Box,{h:`1px`,bg:`grey.100`,mb:`space-8`}),y.length>0&&(0,n.jsxs)(t.VStack,{alignItems:`stretch`,gap:`space-4`,mb:`space-8`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`bold`,color:`grey.400`,children:m.compared}),y.map(r=>{let i=p[r];if(u(i)){let a=i.level,o=s[a];return(0,n.jsxs)(t.HStack,{h:`18px`,alignItems:`center`,gap:`space-8`,justify:`space-between`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,color:`grey.500`,minW:`80px`,children:m.attributes[r]}),(0,n.jsx)(t.Box,{display:`flex`,alignItems:`center`,justifyContent:`center`,h:`100%`,px:`space-6`,borderRadius:`full`,flexShrink:`0`,style:{backgroundColor:o.bg},children:(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`medium`,style:{color:o.color,whiteSpace:`nowrap`,lineHeight:1},children:m.levels?.[a]??a})})]},r)}let a=i.matched,o=i.total,c=o>0?a/o*100:0;return(0,n.jsxs)(t.HStack,{h:`18px`,alignItems:`center`,gap:`space-8`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,color:`grey.500`,minW:`80px`,children:m.attributes[r]}),(0,n.jsx)(t.Box,{flex:`1`,h:`3px`,bg:`grey.100`,borderRadius:`full`,overflow:`hidden`,children:(0,n.jsx)(t.Box,{h:`100%`,borderRadius:`full`,style:{width:`${c}%`,background:l(c)}})}),(0,n.jsxs)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`medium`,minW:`40px`,textAlign:`right`,style:{color:l(c),lineHeight:1},children:[a,`/`,o]})]},r)})]}),y.length>0&&b.length>0&&(0,n.jsx)(t.Box,{h:`1px`,bg:`grey.100`,mb:`space-8`}),b.length>0&&(0,n.jsxs)(t.VStack,{alignItems:`stretch`,gap:`space-4`,children:[(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,fontWeight:`bold`,color:`grey.400`,children:m.notSpecified}),(0,n.jsx)(t.HStack,{flexWrap:`wrap`,gap:`space-4`,children:b.map(t=>(0,n.jsx)(e.Text,{as:`span`,fontSize:`body.sm`,color:`grey.400`,border:`1px solid`,borderColor:`grey.100`,px:`space-8`,py:`2px`,borderRadius:`full`,children:m.attributes[t]},t))})]}),m.footer&&(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.Box,{h:`1px`,bg:`grey.100`,mt:`space-8`,mb:`space-8`}),(0,n.jsx)(e.Text,{as:`span`,fontSize:`9px`,color:`grey.300`,style:{lineHeight:1},children:m.footer})]})]})};exports.ChatScoreBreakdown=p;
2
2
  //# sourceMappingURL=ChatScoreBreakdown.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatScoreBreakdown.cjs","names":[],"sources":["../../../src/components/chat/ChatScoreBreakdown.tsx"],"sourcesContent":["import { Box, HStack, VStack } from '@stokelp/styled-system/jsx'\nimport { Text } from '~/components'\n\nexport type ScoreSetAttributeKey =\n | 'origin'\n | 'rawMaterialOrigin'\n | 'temperature'\n | 'cutting'\n | 'cooking'\n | 'treatment'\n | 'label'\n | 'packaging'\n\nexport type ScoreNumericAttributeKey = 'grammageGsm' | 'recycledPct' | 'thicknessMicron'\n\nexport type ScoreAttributeKey = ScoreSetAttributeKey | ScoreNumericAttributeKey\n\n/** Proximity bucket for free numeric packaging specs: egal (equal) → non_correspondant (no match). */\nexport type ScoreLevel = 'egal' | 'proche' | 'eloigne' | 'non_correspondant'\n\nexport type ScoreAttributeDetail = {\n matched: number | null\n total: number | null\n level?: ScoreLevel | null\n}\n\nexport type ChatScoreBreakdownLabels = {\n title: string\n compared: string\n notSpecified: string\n attributes: Record<ScoreSetAttributeKey, string> & Partial<Record<ScoreNumericAttributeKey, string>>\n levels?: Record<ScoreLevel, string>\n footer?: string\n}\n\nexport type ChatScoreBreakdownProps = {\n percent: number\n detail: Record<string, ScoreAttributeDetail>\n labels: ChatScoreBreakdownLabels\n /** Typology id of the AO product (1 = packaging). When provided, drives which attribute family is shown; falls back to inferring from the numeric specs when absent. */\n typologyId?: number\n}\n\nconst PACKAGING_TYPOLOGY_ID = 1\n\nconst ATTRIBUTE_ORDER: ScoreAttributeKey[] = [\n 'origin',\n 'rawMaterialOrigin',\n 'temperature',\n 'cutting',\n 'cooking',\n 'treatment',\n 'label',\n 'packaging',\n 'grammageGsm',\n 'recycledPct',\n 'thicknessMicron',\n]\n\n// Attributes that only exist for food products; hidden for packaging products.\nconst FOOD_ONLY_KEYS: ScoreAttributeKey[] = ['temperature', 'cutting', 'cooking']\n// Free numeric specs that only exist for packaging products; hidden for food products.\nconst PACKAGING_ONLY_KEYS: ScoreNumericAttributeKey[] = ['grammageGsm', 'recycledPct', 'thicknessMicron']\n\nconst LEVEL_COLORS: Record<ScoreLevel, { bg: string; color: string }> = {\n egal: { bg: '#E6F3D9', color: '#3B6D11' },\n proche: { bg: '#F0F6DC', color: '#5E8516' },\n eloigne: { bg: '#FDF0E0', color: '#C2740C' },\n non_correspondant: { bg: '#FCEBEB', color: '#A32D2D' },\n}\n\nfunction getHeadlineColor(percent: number): string {\n if (percent >= 70) return '#3B6D11'\n if (percent >= 40) return '#854F0B'\n return '#A32D2D'\n}\n\nfunction getBarColor(ratioPercent: number): string {\n if (ratioPercent >= 70) return '#639922'\n if (ratioPercent >= 40) return '#BA7517'\n return '#E24B4A'\n}\n\nfunction hasLevel(detail?: ScoreAttributeDetail): boolean {\n return detail?.level != null\n}\n\nfunction isFraction(detail?: ScoreAttributeDetail): boolean {\n return detail?.matched != null && detail?.total != null\n}\n\nfunction isComparable(detail?: ScoreAttributeDetail): boolean {\n return hasLevel(detail) || isFraction(detail)\n}\n\nexport const ChatScoreBreakdown = ({ percent, detail, labels, typologyId }: ChatScoreBreakdownProps) => {\n // A packaging product hides food attributes (temperature/cutting/cooking) and shows the\n // numeric specs, and vice versa. Two independent signals mark a packaging product, combined\n // so neither can wrongly override the other: the AO product typology (authoritative, and the\n // only signal when no numeric spec is comparable), and the presence of a comparable numeric\n // spec (covers cases where the typology is absent or not sent as the expected value).\n const isPackaging =\n Number(typologyId) === PACKAGING_TYPOLOGY_ID || PACKAGING_ONLY_KEYS.some(key => detail[key]?.level != null)\n const hiddenKeys = new Set<ScoreAttributeKey>(isPackaging ? FOOD_ONLY_KEYS : PACKAGING_ONLY_KEYS)\n const visibleKeys = ATTRIBUTE_ORDER.filter(key => !hiddenKeys.has(key))\n\n const compared = visibleKeys.filter(key => isComparable(detail[key]))\n const notSpecified = visibleKeys.filter(key => !isComparable(detail[key]) && labels.attributes[key] != null)\n\n return (\n <Box\n bg=\"white\"\n borderRadius=\"radius-8\"\n p=\"space-16\"\n minW=\"220px\"\n maxW=\"240px\"\n css={{ '& *': { fontFamily: 'inherit' } }}\n >\n <HStack justify=\"space-between\" alignItems=\"baseline\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.md\" fontWeight=\"bold\">\n {labels.title}\n </Text>\n <Text as=\"span\" fontSize=\"body.lg\" fontWeight=\"medium\" style={{ color: getHeadlineColor(percent) }}>\n {Math.round(percent)} %\n </Text>\n </HStack>\n\n <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />\n\n {compared.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.compared}\n </Text>\n {compared.map(key => {\n const entry = detail[key]\n if (hasLevel(entry)) {\n const level = entry.level as ScoreLevel\n const colors = LEVEL_COLORS[level]\n return (\n <HStack key={key} alignItems=\"center\" gap=\"space-8\" justify=\"space-between\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box px=\"space-8\" py=\"2px\" borderRadius=\"full\" flexShrink=\"0\" style={{ backgroundColor: colors.bg }}>\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n style={{ color: colors.color, whiteSpace: 'nowrap' }}\n >\n {labels.levels?.[level] ?? level}\n </Text>\n </Box>\n </HStack>\n )\n }\n\n const matched = entry.matched as number\n const total = entry.total as number\n const ratioPercent = total > 0 ? (matched / total) * 100 : 0\n return (\n <HStack key={key} alignItems=\"center\" gap=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box flex=\"1\" h=\"3px\" bg=\"grey.100\" borderRadius=\"full\" overflow=\"hidden\">\n <Box\n h=\"100%\"\n borderRadius=\"full\"\n style={{ width: `${ratioPercent}%`, background: getBarColor(ratioPercent) }}\n />\n </Box>\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n minW=\"40px\"\n textAlign=\"right\"\n style={{ color: getBarColor(ratioPercent) }}\n >\n {matched}/{total}\n </Text>\n </HStack>\n )\n })}\n </VStack>\n )}\n\n {compared.length > 0 && notSpecified.length > 0 && <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />}\n\n {notSpecified.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.notSpecified}\n </Text>\n <HStack flexWrap=\"wrap\" gap=\"space-4\">\n {notSpecified.map(key => (\n <Text\n key={key}\n as=\"span\"\n fontSize=\"body.sm\"\n color=\"grey.400\"\n border=\"1px solid\"\n borderColor=\"grey.100\"\n px=\"space-8\"\n py=\"2px\"\n borderRadius=\"full\"\n >\n {labels.attributes[key]}\n </Text>\n ))}\n </HStack>\n </VStack>\n )}\n\n {labels.footer && (\n <>\n <Box h=\"1px\" bg=\"grey.100\" mt=\"space-8\" mb=\"space-8\" />\n <Text as=\"span\" fontSize=\"9px\" color=\"grey.300\" style={{ lineHeight: 1 }}>\n {labels.footer}\n </Text>\n </>\n )}\n </Box>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChatScoreBreakdown.cjs","names":[],"sources":["../../../src/components/chat/ChatScoreBreakdown.tsx"],"sourcesContent":["import { Box, HStack, VStack } from '@stokelp/styled-system/jsx'\nimport { Text } from '~/components'\n\nexport type ScoreSetAttributeKey =\n | 'origin'\n | 'rawMaterialOrigin'\n | 'temperature'\n | 'cutting'\n | 'cooking'\n | 'treatment'\n | 'label'\n | 'packaging'\n\nexport type ScoreNumericAttributeKey = 'grammageGsm' | 'recycledPct' | 'thicknessMicron'\n\nexport type ScoreAttributeKey = ScoreSetAttributeKey | ScoreNumericAttributeKey\n\n/** Proximity bucket for free numeric packaging specs: egal (equal) → non_correspondant (no match). */\nexport type ScoreLevel = 'egal' | 'proche' | 'eloigne' | 'non_correspondant'\n\nexport type ScoreAttributeDetail = {\n matched: number | null\n total: number | null\n level?: ScoreLevel | null\n}\n\nexport type ChatScoreBreakdownLabels = {\n title: string\n compared: string\n notSpecified: string\n attributes: Record<ScoreSetAttributeKey, string> & Partial<Record<ScoreNumericAttributeKey, string>>\n levels?: Record<ScoreLevel, string>\n footer?: string\n}\n\nexport type ChatScoreBreakdownProps = {\n percent: number\n detail: Record<string, ScoreAttributeDetail>\n labels: ChatScoreBreakdownLabels\n /** Typology id of the AO product (1 = packaging). When provided, drives which attribute family is shown; falls back to inferring from the numeric specs when absent. */\n typologyId?: number\n}\n\nconst PACKAGING_TYPOLOGY_ID = 1\n\nconst ATTRIBUTE_ORDER: ScoreAttributeKey[] = [\n 'origin',\n 'rawMaterialOrigin',\n 'temperature',\n 'cutting',\n 'cooking',\n 'treatment',\n 'label',\n 'packaging',\n 'grammageGsm',\n 'recycledPct',\n 'thicknessMicron',\n]\n\n// Attributes that only exist for food products; hidden for packaging products.\nconst FOOD_ONLY_KEYS: ScoreAttributeKey[] = ['temperature', 'cutting', 'cooking']\n// Free numeric specs that only exist for packaging products; hidden for food products.\nconst PACKAGING_ONLY_KEYS: ScoreNumericAttributeKey[] = ['grammageGsm', 'recycledPct', 'thicknessMicron']\n\nconst LEVEL_COLORS: Record<ScoreLevel, { bg: string; color: string }> = {\n egal: { bg: '#E6F3D9', color: '#3B6D11' },\n proche: { bg: '#F0F6DC', color: '#5E8516' },\n eloigne: { bg: '#FDF0E0', color: '#C2740C' },\n non_correspondant: { bg: '#FCEBEB', color: '#A32D2D' },\n}\n\nfunction getHeadlineColor(percent: number): string {\n if (percent >= 70) return '#3B6D11'\n if (percent >= 40) return '#854F0B'\n return '#A32D2D'\n}\n\nfunction getBarColor(ratioPercent: number): string {\n if (ratioPercent >= 70) return '#639922'\n if (ratioPercent >= 40) return '#BA7517'\n return '#E24B4A'\n}\n\nfunction hasLevel(detail?: ScoreAttributeDetail): boolean {\n return detail?.level != null\n}\n\nfunction isFraction(detail?: ScoreAttributeDetail): boolean {\n return detail?.matched != null && detail?.total != null\n}\n\nfunction isComparable(detail?: ScoreAttributeDetail): boolean {\n return hasLevel(detail) || isFraction(detail)\n}\n\nexport const ChatScoreBreakdown = ({ percent, detail, labels, typologyId }: ChatScoreBreakdownProps) => {\n // A packaging product hides food attributes (temperature/cutting/cooking) and shows the\n // numeric specs, and vice versa. Two independent signals mark a packaging product, combined\n // so neither can wrongly override the other: the AO product typology (authoritative, and the\n // only signal when no numeric spec is comparable), and the presence of a comparable numeric\n // spec (covers cases where the typology is absent or not sent as the expected value).\n const isPackaging =\n Number(typologyId) === PACKAGING_TYPOLOGY_ID || PACKAGING_ONLY_KEYS.some(key => detail[key]?.level != null)\n const hiddenKeys = new Set<ScoreAttributeKey>(isPackaging ? FOOD_ONLY_KEYS : PACKAGING_ONLY_KEYS)\n const visibleKeys = ATTRIBUTE_ORDER.filter(key => !hiddenKeys.has(key))\n\n const compared = visibleKeys.filter(key => isComparable(detail[key]))\n const notSpecified = visibleKeys.filter(key => !isComparable(detail[key]) && labels.attributes[key] != null)\n\n return (\n <Box\n bg=\"white\"\n borderRadius=\"radius-8\"\n p=\"space-16\"\n minW=\"220px\"\n maxW=\"240px\"\n css={{ '& *': { fontFamily: 'inherit' } }}\n >\n <HStack justify=\"space-between\" alignItems=\"baseline\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.md\" fontWeight=\"bold\">\n {labels.title}\n </Text>\n <Text as=\"span\" fontSize=\"body.lg\" fontWeight=\"medium\" style={{ color: getHeadlineColor(percent) }}>\n {Math.round(percent)} %\n </Text>\n </HStack>\n\n <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />\n\n {compared.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.compared}\n </Text>\n {compared.map(key => {\n const entry = detail[key]\n if (hasLevel(entry)) {\n const level = entry.level as ScoreLevel\n const colors = LEVEL_COLORS[level]\n return (\n <HStack key={key} h=\"18px\" alignItems=\"center\" gap=\"space-8\" justify=\"space-between\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n h=\"100%\"\n px=\"space-6\"\n borderRadius=\"full\"\n flexShrink=\"0\"\n style={{ backgroundColor: colors.bg }}\n >\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n style={{ color: colors.color, whiteSpace: 'nowrap', lineHeight: 1 }}\n >\n {labels.levels?.[level] ?? level}\n </Text>\n </Box>\n </HStack>\n )\n }\n\n const matched = entry.matched as number\n const total = entry.total as number\n const ratioPercent = total > 0 ? (matched / total) * 100 : 0\n return (\n <HStack key={key} h=\"18px\" alignItems=\"center\" gap=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box flex=\"1\" h=\"3px\" bg=\"grey.100\" borderRadius=\"full\" overflow=\"hidden\">\n <Box\n h=\"100%\"\n borderRadius=\"full\"\n style={{ width: `${ratioPercent}%`, background: getBarColor(ratioPercent) }}\n />\n </Box>\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n minW=\"40px\"\n textAlign=\"right\"\n style={{ color: getBarColor(ratioPercent), lineHeight: 1 }}\n >\n {matched}/{total}\n </Text>\n </HStack>\n )\n })}\n </VStack>\n )}\n\n {compared.length > 0 && notSpecified.length > 0 && <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />}\n\n {notSpecified.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.notSpecified}\n </Text>\n <HStack flexWrap=\"wrap\" gap=\"space-4\">\n {notSpecified.map(key => (\n <Text\n key={key}\n as=\"span\"\n fontSize=\"body.sm\"\n color=\"grey.400\"\n border=\"1px solid\"\n borderColor=\"grey.100\"\n px=\"space-8\"\n py=\"2px\"\n borderRadius=\"full\"\n >\n {labels.attributes[key]}\n </Text>\n ))}\n </HStack>\n </VStack>\n )}\n\n {labels.footer && (\n <>\n <Box h=\"1px\" bg=\"grey.100\" mt=\"space-8\" mb=\"space-8\" />\n <Text as=\"span\" fontSize=\"9px\" color=\"grey.300\" style={{ lineHeight: 1 }}>\n {labels.footer}\n </Text>\n </>\n )}\n </Box>\n )\n}\n"],"mappings":"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"}
@@ -103,6 +103,7 @@ var _ = ({ percent: h, detail: _, labels: v, typologyId: y }) => {
103
103
  if (m(i)) {
104
104
  let s = i.level, c = d[s];
105
105
  return /* @__PURE__ */ o(n, {
106
+ h: "18px",
106
107
  alignItems: "center",
107
108
  gap: "space-8",
108
109
  justify: "space-between",
@@ -113,8 +114,11 @@ var _ = ({ percent: h, detail: _, labels: v, typologyId: y }) => {
113
114
  minW: "80px",
114
115
  children: v.attributes[r]
115
116
  }), /* @__PURE__ */ a(t, {
116
- px: "space-8",
117
- py: "2px",
117
+ display: "flex",
118
+ alignItems: "center",
119
+ justifyContent: "center",
120
+ h: "100%",
121
+ px: "space-6",
118
122
  borderRadius: "full",
119
123
  flexShrink: "0",
120
124
  style: { backgroundColor: c.bg },
@@ -124,7 +128,8 @@ var _ = ({ percent: h, detail: _, labels: v, typologyId: y }) => {
124
128
  fontWeight: "medium",
125
129
  style: {
126
130
  color: c.color,
127
- whiteSpace: "nowrap"
131
+ whiteSpace: "nowrap",
132
+ lineHeight: 1
128
133
  },
129
134
  children: v.levels?.[s] ?? s
130
135
  })
@@ -133,6 +138,7 @@ var _ = ({ percent: h, detail: _, labels: v, typologyId: y }) => {
133
138
  }
134
139
  let s = i.matched, c = i.total, l = c > 0 ? s / c * 100 : 0;
135
140
  return /* @__PURE__ */ o(n, {
141
+ h: "18px",
136
142
  alignItems: "center",
137
143
  gap: "space-8",
138
144
  children: [
@@ -164,7 +170,10 @@ var _ = ({ percent: h, detail: _, labels: v, typologyId: y }) => {
164
170
  fontWeight: "medium",
165
171
  minW: "40px",
166
172
  textAlign: "right",
167
- style: { color: p(l) },
173
+ style: {
174
+ color: p(l),
175
+ lineHeight: 1
176
+ },
168
177
  children: [
169
178
  s,
170
179
  "/",
@@ -1 +1 @@
1
- {"version":3,"file":"ChatScoreBreakdown.js","names":[],"sources":["../../../src/components/chat/ChatScoreBreakdown.tsx"],"sourcesContent":["import { Box, HStack, VStack } from '@stokelp/styled-system/jsx'\nimport { Text } from '~/components'\n\nexport type ScoreSetAttributeKey =\n | 'origin'\n | 'rawMaterialOrigin'\n | 'temperature'\n | 'cutting'\n | 'cooking'\n | 'treatment'\n | 'label'\n | 'packaging'\n\nexport type ScoreNumericAttributeKey = 'grammageGsm' | 'recycledPct' | 'thicknessMicron'\n\nexport type ScoreAttributeKey = ScoreSetAttributeKey | ScoreNumericAttributeKey\n\n/** Proximity bucket for free numeric packaging specs: egal (equal) → non_correspondant (no match). */\nexport type ScoreLevel = 'egal' | 'proche' | 'eloigne' | 'non_correspondant'\n\nexport type ScoreAttributeDetail = {\n matched: number | null\n total: number | null\n level?: ScoreLevel | null\n}\n\nexport type ChatScoreBreakdownLabels = {\n title: string\n compared: string\n notSpecified: string\n attributes: Record<ScoreSetAttributeKey, string> & Partial<Record<ScoreNumericAttributeKey, string>>\n levels?: Record<ScoreLevel, string>\n footer?: string\n}\n\nexport type ChatScoreBreakdownProps = {\n percent: number\n detail: Record<string, ScoreAttributeDetail>\n labels: ChatScoreBreakdownLabels\n /** Typology id of the AO product (1 = packaging). When provided, drives which attribute family is shown; falls back to inferring from the numeric specs when absent. */\n typologyId?: number\n}\n\nconst PACKAGING_TYPOLOGY_ID = 1\n\nconst ATTRIBUTE_ORDER: ScoreAttributeKey[] = [\n 'origin',\n 'rawMaterialOrigin',\n 'temperature',\n 'cutting',\n 'cooking',\n 'treatment',\n 'label',\n 'packaging',\n 'grammageGsm',\n 'recycledPct',\n 'thicknessMicron',\n]\n\n// Attributes that only exist for food products; hidden for packaging products.\nconst FOOD_ONLY_KEYS: ScoreAttributeKey[] = ['temperature', 'cutting', 'cooking']\n// Free numeric specs that only exist for packaging products; hidden for food products.\nconst PACKAGING_ONLY_KEYS: ScoreNumericAttributeKey[] = ['grammageGsm', 'recycledPct', 'thicknessMicron']\n\nconst LEVEL_COLORS: Record<ScoreLevel, { bg: string; color: string }> = {\n egal: { bg: '#E6F3D9', color: '#3B6D11' },\n proche: { bg: '#F0F6DC', color: '#5E8516' },\n eloigne: { bg: '#FDF0E0', color: '#C2740C' },\n non_correspondant: { bg: '#FCEBEB', color: '#A32D2D' },\n}\n\nfunction getHeadlineColor(percent: number): string {\n if (percent >= 70) return '#3B6D11'\n if (percent >= 40) return '#854F0B'\n return '#A32D2D'\n}\n\nfunction getBarColor(ratioPercent: number): string {\n if (ratioPercent >= 70) return '#639922'\n if (ratioPercent >= 40) return '#BA7517'\n return '#E24B4A'\n}\n\nfunction hasLevel(detail?: ScoreAttributeDetail): boolean {\n return detail?.level != null\n}\n\nfunction isFraction(detail?: ScoreAttributeDetail): boolean {\n return detail?.matched != null && detail?.total != null\n}\n\nfunction isComparable(detail?: ScoreAttributeDetail): boolean {\n return hasLevel(detail) || isFraction(detail)\n}\n\nexport const ChatScoreBreakdown = ({ percent, detail, labels, typologyId }: ChatScoreBreakdownProps) => {\n // A packaging product hides food attributes (temperature/cutting/cooking) and shows the\n // numeric specs, and vice versa. Two independent signals mark a packaging product, combined\n // so neither can wrongly override the other: the AO product typology (authoritative, and the\n // only signal when no numeric spec is comparable), and the presence of a comparable numeric\n // spec (covers cases where the typology is absent or not sent as the expected value).\n const isPackaging =\n Number(typologyId) === PACKAGING_TYPOLOGY_ID || PACKAGING_ONLY_KEYS.some(key => detail[key]?.level != null)\n const hiddenKeys = new Set<ScoreAttributeKey>(isPackaging ? FOOD_ONLY_KEYS : PACKAGING_ONLY_KEYS)\n const visibleKeys = ATTRIBUTE_ORDER.filter(key => !hiddenKeys.has(key))\n\n const compared = visibleKeys.filter(key => isComparable(detail[key]))\n const notSpecified = visibleKeys.filter(key => !isComparable(detail[key]) && labels.attributes[key] != null)\n\n return (\n <Box\n bg=\"white\"\n borderRadius=\"radius-8\"\n p=\"space-16\"\n minW=\"220px\"\n maxW=\"240px\"\n css={{ '& *': { fontFamily: 'inherit' } }}\n >\n <HStack justify=\"space-between\" alignItems=\"baseline\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.md\" fontWeight=\"bold\">\n {labels.title}\n </Text>\n <Text as=\"span\" fontSize=\"body.lg\" fontWeight=\"medium\" style={{ color: getHeadlineColor(percent) }}>\n {Math.round(percent)} %\n </Text>\n </HStack>\n\n <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />\n\n {compared.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.compared}\n </Text>\n {compared.map(key => {\n const entry = detail[key]\n if (hasLevel(entry)) {\n const level = entry.level as ScoreLevel\n const colors = LEVEL_COLORS[level]\n return (\n <HStack key={key} alignItems=\"center\" gap=\"space-8\" justify=\"space-between\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box px=\"space-8\" py=\"2px\" borderRadius=\"full\" flexShrink=\"0\" style={{ backgroundColor: colors.bg }}>\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n style={{ color: colors.color, whiteSpace: 'nowrap' }}\n >\n {labels.levels?.[level] ?? level}\n </Text>\n </Box>\n </HStack>\n )\n }\n\n const matched = entry.matched as number\n const total = entry.total as number\n const ratioPercent = total > 0 ? (matched / total) * 100 : 0\n return (\n <HStack key={key} alignItems=\"center\" gap=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box flex=\"1\" h=\"3px\" bg=\"grey.100\" borderRadius=\"full\" overflow=\"hidden\">\n <Box\n h=\"100%\"\n borderRadius=\"full\"\n style={{ width: `${ratioPercent}%`, background: getBarColor(ratioPercent) }}\n />\n </Box>\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n minW=\"40px\"\n textAlign=\"right\"\n style={{ color: getBarColor(ratioPercent) }}\n >\n {matched}/{total}\n </Text>\n </HStack>\n )\n })}\n </VStack>\n )}\n\n {compared.length > 0 && notSpecified.length > 0 && <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />}\n\n {notSpecified.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.notSpecified}\n </Text>\n <HStack flexWrap=\"wrap\" gap=\"space-4\">\n {notSpecified.map(key => (\n <Text\n key={key}\n as=\"span\"\n fontSize=\"body.sm\"\n color=\"grey.400\"\n border=\"1px solid\"\n borderColor=\"grey.100\"\n px=\"space-8\"\n py=\"2px\"\n borderRadius=\"full\"\n >\n {labels.attributes[key]}\n </Text>\n ))}\n </HStack>\n </VStack>\n )}\n\n {labels.footer && (\n <>\n <Box h=\"1px\" bg=\"grey.100\" mt=\"space-8\" mb=\"space-8\" />\n <Text as=\"span\" fontSize=\"9px\" color=\"grey.300\" style={{ lineHeight: 1 }}>\n {labels.footer}\n </Text>\n </>\n )}\n </Box>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChatScoreBreakdown.js","names":[],"sources":["../../../src/components/chat/ChatScoreBreakdown.tsx"],"sourcesContent":["import { Box, HStack, VStack } from '@stokelp/styled-system/jsx'\nimport { Text } from '~/components'\n\nexport type ScoreSetAttributeKey =\n | 'origin'\n | 'rawMaterialOrigin'\n | 'temperature'\n | 'cutting'\n | 'cooking'\n | 'treatment'\n | 'label'\n | 'packaging'\n\nexport type ScoreNumericAttributeKey = 'grammageGsm' | 'recycledPct' | 'thicknessMicron'\n\nexport type ScoreAttributeKey = ScoreSetAttributeKey | ScoreNumericAttributeKey\n\n/** Proximity bucket for free numeric packaging specs: egal (equal) → non_correspondant (no match). */\nexport type ScoreLevel = 'egal' | 'proche' | 'eloigne' | 'non_correspondant'\n\nexport type ScoreAttributeDetail = {\n matched: number | null\n total: number | null\n level?: ScoreLevel | null\n}\n\nexport type ChatScoreBreakdownLabels = {\n title: string\n compared: string\n notSpecified: string\n attributes: Record<ScoreSetAttributeKey, string> & Partial<Record<ScoreNumericAttributeKey, string>>\n levels?: Record<ScoreLevel, string>\n footer?: string\n}\n\nexport type ChatScoreBreakdownProps = {\n percent: number\n detail: Record<string, ScoreAttributeDetail>\n labels: ChatScoreBreakdownLabels\n /** Typology id of the AO product (1 = packaging). When provided, drives which attribute family is shown; falls back to inferring from the numeric specs when absent. */\n typologyId?: number\n}\n\nconst PACKAGING_TYPOLOGY_ID = 1\n\nconst ATTRIBUTE_ORDER: ScoreAttributeKey[] = [\n 'origin',\n 'rawMaterialOrigin',\n 'temperature',\n 'cutting',\n 'cooking',\n 'treatment',\n 'label',\n 'packaging',\n 'grammageGsm',\n 'recycledPct',\n 'thicknessMicron',\n]\n\n// Attributes that only exist for food products; hidden for packaging products.\nconst FOOD_ONLY_KEYS: ScoreAttributeKey[] = ['temperature', 'cutting', 'cooking']\n// Free numeric specs that only exist for packaging products; hidden for food products.\nconst PACKAGING_ONLY_KEYS: ScoreNumericAttributeKey[] = ['grammageGsm', 'recycledPct', 'thicknessMicron']\n\nconst LEVEL_COLORS: Record<ScoreLevel, { bg: string; color: string }> = {\n egal: { bg: '#E6F3D9', color: '#3B6D11' },\n proche: { bg: '#F0F6DC', color: '#5E8516' },\n eloigne: { bg: '#FDF0E0', color: '#C2740C' },\n non_correspondant: { bg: '#FCEBEB', color: '#A32D2D' },\n}\n\nfunction getHeadlineColor(percent: number): string {\n if (percent >= 70) return '#3B6D11'\n if (percent >= 40) return '#854F0B'\n return '#A32D2D'\n}\n\nfunction getBarColor(ratioPercent: number): string {\n if (ratioPercent >= 70) return '#639922'\n if (ratioPercent >= 40) return '#BA7517'\n return '#E24B4A'\n}\n\nfunction hasLevel(detail?: ScoreAttributeDetail): boolean {\n return detail?.level != null\n}\n\nfunction isFraction(detail?: ScoreAttributeDetail): boolean {\n return detail?.matched != null && detail?.total != null\n}\n\nfunction isComparable(detail?: ScoreAttributeDetail): boolean {\n return hasLevel(detail) || isFraction(detail)\n}\n\nexport const ChatScoreBreakdown = ({ percent, detail, labels, typologyId }: ChatScoreBreakdownProps) => {\n // A packaging product hides food attributes (temperature/cutting/cooking) and shows the\n // numeric specs, and vice versa. Two independent signals mark a packaging product, combined\n // so neither can wrongly override the other: the AO product typology (authoritative, and the\n // only signal when no numeric spec is comparable), and the presence of a comparable numeric\n // spec (covers cases where the typology is absent or not sent as the expected value).\n const isPackaging =\n Number(typologyId) === PACKAGING_TYPOLOGY_ID || PACKAGING_ONLY_KEYS.some(key => detail[key]?.level != null)\n const hiddenKeys = new Set<ScoreAttributeKey>(isPackaging ? FOOD_ONLY_KEYS : PACKAGING_ONLY_KEYS)\n const visibleKeys = ATTRIBUTE_ORDER.filter(key => !hiddenKeys.has(key))\n\n const compared = visibleKeys.filter(key => isComparable(detail[key]))\n const notSpecified = visibleKeys.filter(key => !isComparable(detail[key]) && labels.attributes[key] != null)\n\n return (\n <Box\n bg=\"white\"\n borderRadius=\"radius-8\"\n p=\"space-16\"\n minW=\"220px\"\n maxW=\"240px\"\n css={{ '& *': { fontFamily: 'inherit' } }}\n >\n <HStack justify=\"space-between\" alignItems=\"baseline\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.md\" fontWeight=\"bold\">\n {labels.title}\n </Text>\n <Text as=\"span\" fontSize=\"body.lg\" fontWeight=\"medium\" style={{ color: getHeadlineColor(percent) }}>\n {Math.round(percent)} %\n </Text>\n </HStack>\n\n <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />\n\n {compared.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\" mb=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.compared}\n </Text>\n {compared.map(key => {\n const entry = detail[key]\n if (hasLevel(entry)) {\n const level = entry.level as ScoreLevel\n const colors = LEVEL_COLORS[level]\n return (\n <HStack key={key} h=\"18px\" alignItems=\"center\" gap=\"space-8\" justify=\"space-between\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n h=\"100%\"\n px=\"space-6\"\n borderRadius=\"full\"\n flexShrink=\"0\"\n style={{ backgroundColor: colors.bg }}\n >\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n style={{ color: colors.color, whiteSpace: 'nowrap', lineHeight: 1 }}\n >\n {labels.levels?.[level] ?? level}\n </Text>\n </Box>\n </HStack>\n )\n }\n\n const matched = entry.matched as number\n const total = entry.total as number\n const ratioPercent = total > 0 ? (matched / total) * 100 : 0\n return (\n <HStack key={key} h=\"18px\" alignItems=\"center\" gap=\"space-8\">\n <Text as=\"span\" fontSize=\"body.sm\" color=\"grey.500\" minW=\"80px\">\n {labels.attributes[key]}\n </Text>\n <Box flex=\"1\" h=\"3px\" bg=\"grey.100\" borderRadius=\"full\" overflow=\"hidden\">\n <Box\n h=\"100%\"\n borderRadius=\"full\"\n style={{ width: `${ratioPercent}%`, background: getBarColor(ratioPercent) }}\n />\n </Box>\n <Text\n as=\"span\"\n fontSize=\"body.sm\"\n fontWeight=\"medium\"\n minW=\"40px\"\n textAlign=\"right\"\n style={{ color: getBarColor(ratioPercent), lineHeight: 1 }}\n >\n {matched}/{total}\n </Text>\n </HStack>\n )\n })}\n </VStack>\n )}\n\n {compared.length > 0 && notSpecified.length > 0 && <Box h=\"1px\" bg=\"grey.100\" mb=\"space-8\" />}\n\n {notSpecified.length > 0 && (\n <VStack alignItems=\"stretch\" gap=\"space-4\">\n <Text as=\"span\" fontSize=\"body.sm\" fontWeight=\"bold\" color=\"grey.400\">\n {labels.notSpecified}\n </Text>\n <HStack flexWrap=\"wrap\" gap=\"space-4\">\n {notSpecified.map(key => (\n <Text\n key={key}\n as=\"span\"\n fontSize=\"body.sm\"\n color=\"grey.400\"\n border=\"1px solid\"\n borderColor=\"grey.100\"\n px=\"space-8\"\n py=\"2px\"\n borderRadius=\"full\"\n >\n {labels.attributes[key]}\n </Text>\n ))}\n </HStack>\n </VStack>\n )}\n\n {labels.footer && (\n <>\n <Box h=\"1px\" bg=\"grey.100\" mt=\"space-8\" mb=\"space-8\" />\n <Text as=\"span\" fontSize=\"9px\" color=\"grey.300\" style={{ lineHeight: 1 }}>\n {labels.footer}\n </Text>\n </>\n )}\n </Box>\n )\n}\n"],"mappings":"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"}