@@ -219,6 +219,9 @@ function formatXyDataset(
219219 color: accent .value ,
220220 temperatureColors ,
221221 useArea: true ,
222+ dashIndices: dataset
223+ .map ((item , index ) => (item .hasAnomaly ? index : - 1 ))
224+ .filter (index => index !== - 1 ),
222225 }
223226
224227 if (selectedGranularity === ' weekly' && isWeeklyDataset (dataset )) {
@@ -275,19 +278,26 @@ function formatXyDataset(
275278function extractSeriesPoints(
276279 selectedGranularity : ChartTimeGranularity ,
277280 dataset : EvolutionData ,
278- ): Array <{ timestamp: number ; value: number }> {
281+ ): Array <{ timestamp: number ; value: number ; hasAnomaly : boolean }> {
279282 if (selectedGranularity === ' weekly' && isWeeklyDataset (dataset )) {
280- return dataset .map (d => ({ timestamp: d .timestampEnd , value: d .value }))
283+ return dataset .map (d => ({
284+ timestamp: d .timestampEnd ,
285+ value: d .value ,
286+ hasAnomaly: !! d .hasAnomaly ,
287+ }))
281288 }
282289 if (
283290 (selectedGranularity === ' daily' && isDailyDataset (dataset )) ||
284291 (selectedGranularity === ' monthly' && isMonthlyDataset (dataset )) ||
285292 (selectedGranularity === ' yearly' && isYearlyDataset (dataset ))
286293 ) {
287- return (dataset as Array <{ timestamp: number ; value: number }>).map (d => ({
288- timestamp: d .timestamp ,
289- value: d .value ,
290- }))
294+ return (dataset as Array <{ timestamp: number ; value: number ; hasAnomaly? : boolean }>).map (
295+ d => ({
296+ timestamp: d .timestamp ,
297+ value: d .value ,
298+ hasAnomaly: !! d .hasAnomaly ,
299+ }),
300+ )
291301 }
292302 return []
293303}
@@ -380,6 +390,11 @@ const isEstimationGranularity = computed(
380390const supportsEstimation = computed (
381391 () => isEstimationGranularity .value && selectedMetric .value !== ' contributors' ,
382392)
393+
394+ const hasDownloadAnomalies = computed (() =>
395+ normalisedDataset .value ?.some (datapoint => !! datapoint .dashIndices .length ),
396+ )
397+
383398const shouldRenderEstimationOverlay = computed (() => ! pending .value && supportsEstimation .value )
384399
385400const startDate = usePermalink <string >(' start' , ' ' , {
@@ -955,11 +970,13 @@ const effectiveDataSingle = computed<EvolutionData>(() => {
955970 granularity: displayedGranularity .value ,
956971 })
957972 }
973+
958974 return applyDataCorrection (
959975 data as Array <{ value: number }>,
960976 settings .value .chartFilter ,
961977 ) as EvolutionData
962978 }
979+
963980 return data
964981})
965982
@@ -991,7 +1008,10 @@ const chartData = computed<{
9911008 const granularity = displayedGranularity .value
9921009
9931010 const timestampSet = new Set <number >()
994- const pointsByPackage = new Map <string , Array <{ timestamp: number ; value: number }>>()
1011+ const pointsByPackage = new Map <
1012+ string ,
1013+ Array < { timestamp : number ; value : number ; hasAnomaly ?: boolean }>
1014+ > ()
9951015
9961016 for (const pkg of names ) {
9971017 let data = state .evolutionsByPackage [pkg ] ?? []
@@ -1005,6 +1025,7 @@ const chartData = computed<{
10051025 ) as EvolutionData
10061026 }
10071027 const points = extractSeriesPoints (granularity , data )
1028+
10081029 pointsByPackage .set (pkg , points )
10091030 for (const p of points ) timestampSet .add (p .timestamp )
10101031 }
@@ -1014,15 +1035,23 @@ const chartData = computed<{
10141035
10151036 const dataset: VueUiXyDatasetItem [] = names .map (pkg => {
10161037 const points = pointsByPackage .get (pkg ) ?? []
1017- const map = new Map <number , number >()
1018- for (const p of points ) map .set (p .timestamp , p .value )
1038+ const valueByTimestamp = new Map <number , number >()
1039+ const anomalyTimestamps = new Set <number >()
1040+ for (const p of points ) {
1041+ valueByTimestamp .set (p .timestamp , p .value )
1042+ if (p .hasAnomaly ) anomalyTimestamps .add (p .timestamp )
1043+ }
10191044
1020- const series = dates .map (t => map .get (t ) ?? 0 )
1045+ const series = dates .map (t => valueByTimestamp .get (t ) ?? 0 )
1046+ const dashIndices = dates
1047+ .map ((t , index ) => (anomalyTimestamps .has (t ) ? index : - 1 ))
1048+ .filter (index => index !== - 1 )
10211049
10221050 const item: VueUiXyDatasetItem = {
10231051 name: pkg ,
10241052 type: ' line' ,
10251053 series ,
1054+ dashIndices ,
10261055 } as VueUiXyDatasetItem
10271056
10281057 if (isListedFramework (pkg )) {
@@ -1045,6 +1074,7 @@ const normalisedDataset = computed(() => {
10451074 return {
10461075 ... d ,
10471076 series: [... d .series .slice (0 , - 1 ), projectedLastValue ],
1077+ dashIndices: d .dashIndices ?? [],
10481078 }
10491079 })
10501080})
@@ -1408,7 +1438,10 @@ function drawSvgPrintLegend(svg: Record<string, any>) {
14081438 })
14091439
14101440 // Inject the estimation legend item when necessary
1411- if (supportsEstimation .value && ! isEndDateOnPeriodEnd .value && ! isZoomed .value ) {
1441+ if (
1442+ (supportsEstimation .value && ! isEndDateOnPeriodEnd .value && ! isZoomed .value ) ||
1443+ hasDownloadAnomalies .value
1444+ ) {
14121445 seriesNames .push (`
14131446 <line
14141447 x1="${svg .drawingArea .left + 12 }"
@@ -1955,7 +1988,10 @@ watch(selectedMetric, value => {
19551988 </template >
19561989
19571990 <!-- Estimation extra legend item -->
1958- <div class =" flex gap-1 place-items-center" v-if =" supportsEstimation" >
1991+ <div
1992+ class =" flex gap-1 place-items-center"
1993+ v-if =" supportsEstimation || hasDownloadAnomalies"
1994+ >
19591995 <svg viewBox =" 0 0 20 2" width =" 20" >
19601996 <line
19611997 x1 =" 0"
0 commit comments