Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1b4f5c8
refactor: remove sat composits from FE
suvanbanerjee Oct 1, 2026
bacf9c3
feat: load composit from API
suvanbanerjee Oct 1, 2026
67930f8
feat: add support for satellite partial composites and enhance stack …
suvanbanerjee Oct 1, 2026
43ea64d
refactor: update satellite layer opacity and gamma settings for rende…
suvanbanerjee Oct 1, 2026
2de757d
feat: enhance stack rendering with tone curves and blur
suvanbanerjee Oct 1, 2026
292f4ea
feat: add sentry log for missing satellite frames
suvanbanerjee Oct 14, 2026
169d046
fix(header): add trunc for overflowing titles on small screens
suvanbanerjee Oct 19, 2026
cb91d7b
feat: disable auto loading of stracks when clouds are off
suvanbanerjee Oct 21, 2026
2d7cfaa
feat: add fingerprint to sentry log for missing satellite frames to r…
suvanbanerjee Oct 21, 2026
bb191f1
feat: prevent duplicate logging of missing satellite frames
suvanbanerjee Oct 21, 2026
706f15b
feat: disable raster fade effect for improved layer transitions
suvanbanerjee Oct 21, 2026
5432b4d
chore: improve comments for clarity on stack and layer handling in ma…
suvanbanerjee Oct 21, 2026
1b31da3
Merge remote-tracking branch 'origin/development' into feat/tif-stacks
suvanbanerjee Oct 21, 2026
8791cbd
Merge remote-tracking branch 'origin/development' into fix/overflow-e…
suvanbanerjee Oct 21, 2026
53472d6
feat: remove blur effect from satellite
suvanbanerjee Oct 22, 2026
6ad5914
Merge pull request #799 from openclimatefix/feat/tif-stacks
suvanbanerjee Oct 23, 2026
159bf2e
Merge pull request #808 from openclimatefix/fix/overflow-eui-header
braddf Oct 23, 2026
88bcb47
chore: bump version to 1.0.1
braddf Oct 26, 2026
e77cd38
Merge remote-tracking branch 'origin/development' into development
braddf Oct 26, 2026
f270373
chore: update UAT for new ui
suvanbanerjee Oct 26, 2026
882aea3
chore: update documentation link to OCF Energy
suvanbanerjee Oct 26, 2026
4985792
Merge pull request #813 from openclimatefix/docs/update-eui-uat
suvanbanerjee Oct 26, 2026
8346c04
fix: map load handler calls the latest overlay, not the first render's
braddf Oct 26, 2026
39dc01b
fix: set lang on html
braddf Oct 26, 2026
fdf5f79
style: chart lines at 2px, bands drawn under everything
braddf Oct 27, 2026
e730a5f
Merge remote-tracking branch 'origin/development' into development
braddf Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/nowcasting-app/components/charts/forecast-header/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const ComparisonEcho: React.FC = () => {
*/
export const HEADER_ROW = "mx-1 mb-[5px] flex items-center gap-4";
export const HEADER_TITLE = "mr-auto flex min-w-0 items-center gap-2";
export const HEADER_FIGURES = "flex items-center gap-4 lg:gap-6";
export const HEADER_FIGURES = "flex shrink-0 items-center gap-4 lg:gap-6";

const ForecastHeaderUI: React.FC<ForecastHeaderProps> = ({
forecastNextPV,
Expand Down Expand Up @@ -175,7 +175,10 @@ const ForecastHeaderUI: React.FC<ForecastHeaderProps> = ({
picker — a word true of every country, so the picker was doing the identifying and
the heading was decoration. The picker moved out; the name moved in. */}
<div className={HEADER_TITLE}>
<span className="text-base leading-tight text-content lg:text-lg dash:text-2xl">
<span
className="truncate text-base leading-tight text-content lg:text-lg dash:text-2xl"
title={countryName}
>
{countryName}
</span>
<ComparisonEcho />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ const ForecastHeaderGSP: FC<ForecastHeaderGSPProps> = ({
return (
<div className={HEADER_ROW}>
<div className={HEADER_TITLE}>
<span className="text-base leading-tight text-content lg:text-lg dash:text-2xl">
<span className="min-w-0 text-base leading-tight text-content lg:text-lg dash:text-2xl">
{titleTooltipText.length ? (
<ForecastLabel className="" position={"left"} tip={titleTooltipContent}>
{title}
<span className="truncate">{title}</span>
</ForecastLabel>
) : (
title
<span className="block truncate" title={title}>
{title}
</span>
)}
</span>
</div>
Expand Down
104 changes: 53 additions & 51 deletions apps/nowcasting-app/components/charts/remix-line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,53 @@ const RemixLine: React.FC<RemixLineProps> = ({
</>
)}

{/* Bands first: paint order is JSX order, so anything declared after these sits on
top of them. Declared later they were tinting the cursor, LIVE and every line. */}
{pLevels.map(([lower, upper]) => (
<Area
key={`${lower}-${upper}`}
type="monotone"
dataKey={getPLevelRangeKey(lower, upper)}
dot={hoverDot}
activeDot={isSitesChart}
xAxisId={"x-axis"}
yAxisId={"y-axis"}
stroke={yellow}
fill={yellow}
// Lowers each band's opacity as more bands are added, so overlapping bands always look like P_LEVEL_BAND_COMBINED_OPACITY, not darker.
fillOpacity={1 - Math.pow(1 - P_LEVEL_BAND_COMBINED_OPACITY, 1 / pLevels.length)}
strokeWidth={0}
hide={!visibleLines.includes("FORECAST")}
isAnimationActive={false}
/>
))}

{data.length > 0 &&
data[0].SEASONAL_BOUNDS?.map((boundPair) => {
return (
<Area
key={`SEASONAL_BOUND_${boundPair.join("_")}`}
dataKey={`SEASONAL_BOUND_${boundPair.join("_")}`}
type="monotone"
dot={hoverDot}
activeDot={isSitesChart}
xAxisId={"x-axis"}
yAxisId={"y-axis"}
stroke={seasonal}
fill={seasonal}
fillOpacity={
(1 /
(Number(boundPair[1].replace("P", "")) -
Number(boundPair[0].replace("P", "")))) *
15
}
strokeWidth={0}
hide={!visibleLines.includes("SEASONAL_BOUNDS")}
isAnimationActive={false}
/>
);
})}

{/* The cursor carries no label and no grip. The period reads once, in the footer,
tethered to the scrub handle; the grip collided with the LIVE marker, both being
`--interactive` objects at the top of a reference line. Click-to-set-time
Expand Down Expand Up @@ -1123,7 +1170,7 @@ const RemixLine: React.FC<RemixLineProps> = ({
strokeDasharray="5 5"
strokeDashoffset={3}
stroke={orange} // blue
strokeWidth={largeScreenMode ? 4 : 1.5}
strokeWidth={largeScreenMode ? 4 : 2}
hide={!visibleLines.includes("N_HOUR_FORECAST")}
isAnimationActive={false}
/>
Expand All @@ -1136,32 +1183,13 @@ const RemixLine: React.FC<RemixLineProps> = ({
xAxisId={"x-axis"}
// strokeDasharray="10 10"
stroke={orange} // blue
strokeWidth={largeScreenMode ? 4 : 1.5}
strokeWidth={largeScreenMode ? 4 : 2}
hide={!visibleLines.includes("N_HOUR_FORECAST")}
isAnimationActive={false}
/>
</>
)}

{pLevels.map(([lower, upper]) => (
<Area
key={`${lower}-${upper}`}
type="monotone"
dataKey={getPLevelRangeKey(lower, upper)}
dot={hoverDot}
activeDot={isSitesChart}
xAxisId={"x-axis"}
yAxisId={"y-axis"}
stroke={yellow}
fill={yellow}
// Lowers each band's opacity as more bands are added, so overlapping bands always look like P_LEVEL_BAND_COMBINED_OPACITY, not darker.
fillOpacity={1 - Math.pow(1 - P_LEVEL_BAND_COMBINED_OPACITY, 1 / pLevels.length)}
strokeWidth={0}
hide={!visibleLines.includes("FORECAST")}
isAnimationActive={false}
/>
))}

<Line
type="monotone"
dataKey="SEASONAL_MEAN"
Expand All @@ -1183,32 +1211,6 @@ const RemixLine: React.FC<RemixLineProps> = ({
isAnimationActive={false}
/>

{data.length > 0 &&
data[0].SEASONAL_BOUNDS?.map((boundPair) => {
return (
<Area
key={`SEASONAL_BOUND_${boundPair.join("_")}`}
dataKey={`SEASONAL_BOUND_${boundPair.join("_")}`}
type="monotone"
dot={hoverDot}
activeDot={isSitesChart}
xAxisId={"x-axis"}
yAxisId={"y-axis"}
stroke={seasonal}
fill={seasonal}
fillOpacity={
(1 /
(Number(boundPair[1].replace("P", "")) -
Number(boundPair[0].replace("P", "")))) *
15
}
strokeWidth={0}
hide={!visibleLines.includes("SEASONAL_BOUNDS")}
isAnimationActive={false}
/>
);
})}

<Line
type="monotone"
dataKey="PAST_INTRADAY_ECMWF_ONLY"
Expand Down Expand Up @@ -1304,15 +1306,15 @@ const RemixLine: React.FC<RemixLineProps> = ({
xAxisId={"x-axis"}
yAxisId={"y-axis"}
stroke={toolTipColors.GENERATION}
strokeWidth={largeScreenMode ? 4 : 1.5}
strokeWidth={largeScreenMode ? 4 : 2}
strokeDasharray="5 5"
hide={!visibleLines.includes("GENERATION")}
isAnimationActive={false}
/>
<Line
type="monotone"
dataKey="GENERATION_UPDATED"
strokeWidth={largeScreenMode ? 4 : 1.5}
strokeWidth={largeScreenMode ? 4 : 2}
stroke={toolTipColors.GENERATION_UPDATED}
xAxisId={"x-axis"}
yAxisId={"y-axis"}
Expand All @@ -1332,7 +1334,7 @@ const RemixLine: React.FC<RemixLineProps> = ({
stroke={yellow} //yellow
fill="transparent"
fillOpacity={100}
strokeWidth={largeScreenMode ? 4 : 1.5}
strokeWidth={largeScreenMode ? 4 : 2}
hide={!visibleLines.includes("FORECAST")}
isAnimationActive={false}
/>
Expand All @@ -1347,7 +1349,7 @@ const RemixLine: React.FC<RemixLineProps> = ({
stroke={yellow} //yellow
fill="transparent"
fillOpacity={100}
strokeWidth={largeScreenMode ? 4 : 1.5}
strokeWidth={largeScreenMode ? 4 : 2}
hide={!visibleLines.includes("FORECAST")}
isAnimationActive={false}
/>
Expand Down
10 changes: 8 additions & 2 deletions apps/nowcasting-app/components/helpers/globalState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "./cookieStorage";
import { LoadingState, SitesEndpointStates } from "../types";
import { ActiveUnit } from "../map/types";
import type { ChannelSelection } from "./satelliteLayer";
import type { SatelliteChannel } from "./satelliteLayer";
import { getCountryConfig } from "../../config/countries";
import { ComparisonSelection } from "./comparison";
import type { CoverageGap } from "../map/country-coverage";
Expand Down Expand Up @@ -161,7 +161,7 @@ export type FlatGlobalStateType = {
nHourForecast: number;
pLevels: [number, number][];
showCloudLayer: boolean;
activeChannel: ChannelSelection;
activeChannel: SatelliteChannel;
showPvLayer: boolean;
/**
* The satellite fetch/decode pipeline's own status, lifted out of `map.tsx`'s local state by
Expand All @@ -172,6 +172,7 @@ export type FlatGlobalStateType = {
*/
isSatelliteLoading: boolean;
satelliteError: string | null;
satellitePartialComposite: string[] | null;
/**
* Enabled countries the map has nothing to draw for at the cursor. `pvLatestMap` writes it,
* since it holds the per-country pipelines; the header renders it beside the country toggle,
Expand Down Expand Up @@ -305,10 +306,15 @@ export const { useGlobalState, getGlobalState, setGlobalState } =
nHourForecast: 4,
pLevels: getValidatedPLevels(),
showCloudLayer: false,
// COMPOSITE_VISIBLE — the default satellite channel (see satelliteLayer.ts). Inlined
// as a literal rather than imported so this module's dependency on satelliteLayer stays
// type-only: a value import would pull geotiff (ESM) into every test that loads
// global state, which Jest can't transform.
activeChannel: "COMPOSITE_VISIBLE",
showPvLayer: true,
isSatelliteLoading: false,
satelliteError: null,
satellitePartialComposite: null,
coverageGaps: [],
chartSplitOverrides: getValidatedChartSplitOverrides()
});
Expand Down
Loading
Loading