From 8069c2ea839f7c240bc0fcda6e6a39fe5c2329c9 Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Thu, 12 Feb 2026 17:54:15 -0600 Subject: [PATCH 01/10] await regions source data if not loaded --- components/map.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/components/map.tsx b/components/map.tsx index a6c6b67..eae7e72 100644 --- a/components/map.tsx +++ b/components/map.tsx @@ -52,7 +52,9 @@ const MapComponent = () => { const getInitialZoom = useCallback((): number => { const width = window.innerWidth const sidebarBreakpoint = theme?.breakpoints?.[1] ?? '64em' - const hasSidebar = window.matchMedia(`(min-width: ${sidebarBreakpoint})`).matches + const hasSidebar = window.matchMedia( + `(min-width: ${sidebarBreakpoint})`, + ).matches const mapWidth = hasSidebar ? width * (2 / 3) : width return Math.log2(mapWidth) - 6.3 }, [theme.breakpoints]) @@ -187,7 +189,17 @@ const MapComponent = () => { zoom: zoom, }) if (!selectedBuilding) { - updateGeographies() + if (map.isSourceLoaded('regions')) { + updateGeographies() + } else { + const handleSourceData = (e: MapSourceDataEvent) => { + if (e.sourceId === 'regions' && e.isSourceLoaded) { + map.off('sourcedata', handleSourceData) + updateGeographies() + } + } + map.on('sourcedata', handleSourceData) + } } } map.on('moveend', handleMoveEnd) From b87b6a4ad33e46e731096fdcd3444d1f330262d9 Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Thu, 12 Feb 2026 17:59:19 -0600 Subject: [PATCH 02/10] move to store so all callers await data --- components/map.tsx | 12 +------ lib/store.ts | 84 ++++++++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/components/map.tsx b/components/map.tsx index eae7e72..da76a73 100644 --- a/components/map.tsx +++ b/components/map.tsx @@ -189,17 +189,7 @@ const MapComponent = () => { zoom: zoom, }) if (!selectedBuilding) { - if (map.isSourceLoaded('regions')) { - updateGeographies() - } else { - const handleSourceData = (e: MapSourceDataEvent) => { - if (e.sourceId === 'regions' && e.isSourceLoaded) { - map.off('sourcedata', handleSourceData) - updateGeographies() - } - } - map.on('sourcedata', handleSourceData) - } + updateGeographies() } } map.on('moveend', handleMoveEnd) diff --git a/lib/store.ts b/lib/store.ts index 35a7115..31fa2c0 100644 --- a/lib/store.ts +++ b/lib/store.ts @@ -1,5 +1,5 @@ import { create } from 'zustand' -import { Map } from 'maplibre-gl' +import { Map, MapSourceDataEvent } from 'maplibre-gl' import { Location, Building, Geography, GeographyKey } from '../types/location' import { GEOGRAPHY_MIN_ZOOM, LAYERS, RISKS } from './config' import { clearSelectedBuildingUrl, updateMapViewUrl } from './url-utils' @@ -145,43 +145,61 @@ export const useStore = create((set, get) => ({ const { map } = get() if (!map) return - const zoom = map.getZoom() - const point = map.project([lng, lat]) + const query = () => { + const zoom = map.getZoom() + const point = map.project([lng, lat]) - const queryIfZoom = ( - minZoom: number, - layers: string[], - ): Geography | null => { - if (zoom < minZoom) return null - const features = map.queryRenderedFeatures(point, { layers }) - return features.length > 0 ? (features[0].properties as Geography) : null - } + const queryIfZoom = ( + minZoom: number, + layers: string[], + ): Geography | null => { + if (zoom < minZoom) return null + const features = map.queryRenderedFeatures(point, { layers }) + return features.length > 0 + ? (features[0].properties as Geography) + : null + } - // Query nation from source tiles directly (rendered features can miss) - const queryNation = (): Geography | null => { - const features = map.querySourceFeatures(LAYERS.nation.sourceId, { - sourceLayer: LAYERS.nation.layerName, + // Query nation from source tiles directly (rendered features can miss) + const queryNation = (): Geography | null => { + const features = map.querySourceFeatures(LAYERS.nation.sourceId, { + sourceLayer: LAYERS.nation.layerName, + }) + return features.length > 0 + ? (features[0].properties as Geography) + : null + } + + set({ + activeGeographies: { + nation: queryNation(), + state: queryIfZoom(GEOGRAPHY_MIN_ZOOM.state, [ + LAYERS.states.layerIds.fill, + ]), + county: queryIfZoom(GEOGRAPHY_MIN_ZOOM.county, [ + LAYERS.counties.layerIds.fill, + ]), + censusTract: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusTract, [ + LAYERS.censusTracts.layerIds.fill, + ]), + censusBlock: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusBlock, [ + LAYERS.censusBlocks.layerIds.fill, + ]), + }, }) - return features.length > 0 ? (features[0].properties as Geography) : null } - set({ - activeGeographies: { - nation: queryNation(), - state: queryIfZoom(GEOGRAPHY_MIN_ZOOM.state, [ - LAYERS.states.layerIds.fill, - ]), - county: queryIfZoom(GEOGRAPHY_MIN_ZOOM.county, [ - LAYERS.counties.layerIds.fill, - ]), - censusTract: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusTract, [ - LAYERS.censusTracts.layerIds.fill, - ]), - censusBlock: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusBlock, [ - LAYERS.censusBlocks.layerIds.fill, - ]), - }, - }) + if (map.isSourceLoaded('regions')) { + query() + } else { + const handleSourceData = (e: MapSourceDataEvent) => { + if (e.sourceId === 'regions' && e.isSourceLoaded) { + map.off('sourcedata', handleSourceData) + query() + } + } + map.on('sourcedata', handleSourceData) + } }, clearSelections: () => { set({ From cb0fca9dedb121063f1fc9112ec64f8b7433fd5f Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 09:19:50 -0600 Subject: [PATCH 03/10] move to utility and use config sourceid --- components/building-points.tsx | 25 ++-------- components/geocode/geocode.tsx | 35 ++++---------- components/map.tsx | 24 ++++------ lib/config.ts | 3 ++ lib/map-utils.ts | 15 ++++++ lib/store.ts | 87 ++++++++++++++-------------------- 6 files changed, 78 insertions(+), 111 deletions(-) create mode 100644 lib/map-utils.ts diff --git a/components/building-points.tsx b/components/building-points.tsx index ad6740f..fc73086 100644 --- a/components/building-points.tsx +++ b/components/building-points.tsx @@ -1,11 +1,8 @@ import { useCallback, useEffect, useMemo } from 'react' -import { - ExpressionSpecification, - MapMouseEvent, - MapSourceDataEvent, -} from 'maplibre-gl' +import { ExpressionSpecification, MapMouseEvent } from 'maplibre-gl' import { useStore } from '@/lib/store' import { LAYERS } from '@/lib/config' +import { ensureSourceLoaded } from '@/lib/map-utils' import { useColormap } from '@/lib/colormaps' import { getBuildingRiskKey } from '@/lib/risk-utils' import { useBuildingUtils } from '@/hooks/useBuildingUtils' @@ -71,21 +68,9 @@ const BuildingPoints = () => { if (feature.geometry.type !== 'Point') return const [lng, lat] = feature.geometry.coordinates - const handleMoveEnd = () => { - if (map.isSourceLoaded(LAYERS.buildings.sourceId)) { - highlightBuildingAtLocation(lng, lat, { easeTo: false }) - } else { - const handleSourceData = (e: MapSourceDataEvent) => { - if ( - e.sourceId === LAYERS.buildings.sourceId && - e.isSourceLoaded - ) { - map.off('sourcedata', handleSourceData) - highlightBuildingAtLocation(lng, lat, { easeTo: false }) - } - } - map.on('sourcedata', handleSourceData) - } + const handleMoveEnd = async () => { + await ensureSourceLoaded(map, LAYERS.buildings.sourceId) + highlightBuildingAtLocation(lng, lat, { easeTo: false }) } map.once('moveend', handleMoveEnd) diff --git a/components/geocode/geocode.tsx b/components/geocode/geocode.tsx index cde0abb..8db4ad9 100644 --- a/components/geocode/geocode.tsx +++ b/components/geocode/geocode.tsx @@ -1,7 +1,7 @@ import { useState, useRef, useEffect } from 'react' import { Box, Flex } from 'theme-ui' import { mix } from '@theme-ui/color' -import { MapSourceDataEvent } from 'maplibre-gl' +import { ensureSourceLoaded } from '@/lib/map-utils' //@ts-expect-error - carbonplan components types not available import { Button, Input, Row, Column } from '@carbonplan/components' //@ts-expect-error - carbonplan layouts types not available @@ -204,31 +204,14 @@ const Geocode = () => { // Highlight building after map movement completes if (location.address.houseNumber) { - const handleMoveEnd = () => { - if (map.isSourceLoaded(LAYERS.buildings.sourceId)) { - const success = highlightBuildingAtLocation( - location.position.lng, - location.position.lat, - { easeTo: false, fetchAddress: false }, - ) - if (success) setSelectedLocation(location) - } else { - const handleSourceData = (e: MapSourceDataEvent) => { - if ( - e.sourceId === LAYERS.buildings.sourceId && - e.isSourceLoaded - ) { - map.off('sourcedata', handleSourceData) - const success = highlightBuildingAtLocation( - location.position.lng, - location.position.lat, - { easeTo: false, fetchAddress: false }, - ) - if (success) setSelectedLocation(location) - } - } - map.on('sourcedata', handleSourceData) - } + const handleMoveEnd = async () => { + await ensureSourceLoaded(map, LAYERS.buildings.sourceId) + const success = highlightBuildingAtLocation( + location.position.lng, + location.position.lat, + { easeTo: false, fetchAddress: false }, + ) + if (success) setSelectedLocation(location) } map.once('moveend', handleMoveEnd) } diff --git a/components/map.tsx b/components/map.tsx index da76a73..a0fe42d 100644 --- a/components/map.tsx +++ b/components/map.tsx @@ -7,7 +7,6 @@ import { removeProtocol, LayerSpecification, SourceSpecification, - MapSourceDataEvent, } from 'maplibre-gl' import 'maplibre-gl/dist/maplibre-gl.css' import { Protocol } from 'pmtiles' @@ -28,6 +27,7 @@ import { useMapControlStyles, } from './' import { LAYERS } from '@/lib/config' +import { ensureSourceLoaded } from '@/lib/map-utils' import { getRiskSources, insertRiskLayers } from '@/lib/risk-layers' import { getMapViewFromQuery, @@ -52,9 +52,7 @@ const MapComponent = () => { const getInitialZoom = useCallback((): number => { const width = window.innerWidth const sidebarBreakpoint = theme?.breakpoints?.[1] ?? '64em' - const hasSidebar = window.matchMedia( - `(min-width: ${sidebarBreakpoint})`, - ).matches + const hasSidebar = window.matchMedia(`(min-width: ${sidebarBreakpoint})`).matches const mapWidth = hasSidebar ? width * (2 / 3) : width return Math.log2(mapWidth) - 6.3 }, [theme.breakpoints]) @@ -203,7 +201,7 @@ const MapComponent = () => { if (!map) return const handleIdle = () => { const layerExists = map.getLayer(LAYERS.counties.layerIds.fill) - const sourceLoaded = map.isSourceLoaded('regions') + const sourceLoaded = map.isSourceLoaded(LAYERS.regions.sourceId) if (layerExists && sourceLoaded) { map.off('idle', handleIdle) @@ -239,17 +237,15 @@ const MapComponent = () => { if (!selectionCoordinates) return const { lat, lng } = selectionCoordinates - const handleSourceData = (e: MapSourceDataEvent) => { - if (e.sourceId === LAYERS.buildings.sourceId && e.isSourceLoaded) { - map.off('sourcedata', handleSourceData) - const found = highlightBuildingAtLocation(lng, lat) - if (!found) { - clearSelections() - updateGeographies() - } + const init = async () => { + await ensureSourceLoaded(map, LAYERS.buildings.sourceId) + const found = highlightBuildingAtLocation(lng, lat) + if (!found) { + clearSelections() + updateGeographies() } } - map.on('sourcedata', handleSourceData) + init() }, [ map, router.isReady, diff --git a/lib/config.ts b/lib/config.ts index a71400c..c10e5be 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -21,6 +21,9 @@ export const DATA_URLS = { } export const LAYERS = { + regions: { + sourceId: 'regions', + }, buildings: { layerName: 'risk', sourceId: 'buildings', diff --git a/lib/map-utils.ts b/lib/map-utils.ts new file mode 100644 index 0000000..e5ef0d1 --- /dev/null +++ b/lib/map-utils.ts @@ -0,0 +1,15 @@ +import { Map, MapSourceDataEvent } from 'maplibre-gl' + +export const ensureSourceLoaded = (map: Map, sourceId: string) => { + if (map.getSource(sourceId) && map.isSourceLoaded(sourceId)) + return Promise.resolve() + return new Promise((resolve) => { + const handleSourceData = (e: MapSourceDataEvent) => { + if (e.sourceId === sourceId && e.isSourceLoaded) { + map.off('sourcedata', handleSourceData) + resolve() + } + } + map.on('sourcedata', handleSourceData) + }) +} diff --git a/lib/store.ts b/lib/store.ts index 31fa2c0..8da4b3e 100644 --- a/lib/store.ts +++ b/lib/store.ts @@ -1,5 +1,6 @@ import { create } from 'zustand' -import { Map, MapSourceDataEvent } from 'maplibre-gl' +import { Map } from 'maplibre-gl' +import { ensureSourceLoaded } from './map-utils' import { Location, Building, Geography, GeographyKey } from '../types/location' import { GEOGRAPHY_MIN_ZOOM, LAYERS, RISKS } from './config' import { clearSelectedBuildingUrl, updateMapViewUrl } from './url-utils' @@ -141,65 +142,49 @@ export const useStore = create((set, get) => ({ advancedMode: process.env.NEXT_PUBLIC_ADVANCED_MODE === 'true', toggleAdvancedMode: () => set((state) => ({ advancedMode: !state.advancedMode })), - queryGeographiesAtPoint: (lng: number, lat: number) => { + queryGeographiesAtPoint: async (lng: number, lat: number) => { const { map } = get() if (!map) return - const query = () => { - const zoom = map.getZoom() - const point = map.project([lng, lat]) + await ensureSourceLoaded(map, LAYERS.regions.sourceId) - const queryIfZoom = ( - minZoom: number, - layers: string[], - ): Geography | null => { - if (zoom < minZoom) return null - const features = map.queryRenderedFeatures(point, { layers }) - return features.length > 0 - ? (features[0].properties as Geography) - : null - } + const zoom = map.getZoom() + const point = map.project([lng, lat]) - // Query nation from source tiles directly (rendered features can miss) - const queryNation = (): Geography | null => { - const features = map.querySourceFeatures(LAYERS.nation.sourceId, { - sourceLayer: LAYERS.nation.layerName, - }) - return features.length > 0 - ? (features[0].properties as Geography) - : null - } + const queryIfZoom = ( + minZoom: number, + layers: string[], + ): Geography | null => { + if (zoom < minZoom) return null + const features = map.queryRenderedFeatures(point, { layers }) + return features.length > 0 ? (features[0].properties as Geography) : null + } - set({ - activeGeographies: { - nation: queryNation(), - state: queryIfZoom(GEOGRAPHY_MIN_ZOOM.state, [ - LAYERS.states.layerIds.fill, - ]), - county: queryIfZoom(GEOGRAPHY_MIN_ZOOM.county, [ - LAYERS.counties.layerIds.fill, - ]), - censusTract: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusTract, [ - LAYERS.censusTracts.layerIds.fill, - ]), - censusBlock: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusBlock, [ - LAYERS.censusBlocks.layerIds.fill, - ]), - }, + // Query nation from source tiles directly (rendered features can miss) + const queryNation = (): Geography | null => { + const features = map.querySourceFeatures(LAYERS.nation.sourceId, { + sourceLayer: LAYERS.nation.layerName, }) + return features.length > 0 ? (features[0].properties as Geography) : null } - if (map.isSourceLoaded('regions')) { - query() - } else { - const handleSourceData = (e: MapSourceDataEvent) => { - if (e.sourceId === 'regions' && e.isSourceLoaded) { - map.off('sourcedata', handleSourceData) - query() - } - } - map.on('sourcedata', handleSourceData) - } + set({ + activeGeographies: { + nation: queryNation(), + state: queryIfZoom(GEOGRAPHY_MIN_ZOOM.state, [ + LAYERS.states.layerIds.fill, + ]), + county: queryIfZoom(GEOGRAPHY_MIN_ZOOM.county, [ + LAYERS.counties.layerIds.fill, + ]), + censusTract: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusTract, [ + LAYERS.censusTracts.layerIds.fill, + ]), + censusBlock: queryIfZoom(GEOGRAPHY_MIN_ZOOM.censusBlock, [ + LAYERS.censusBlocks.layerIds.fill, + ]), + }, + }) }, clearSelections: () => { set({ From a6c4a7ccf4fe517c5d17797643105844c84863e7 Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 09:30:26 -0600 Subject: [PATCH 04/10] one more use of new utility --- components/map.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/components/map.tsx b/components/map.tsx index a0fe42d..68ba8e5 100644 --- a/components/map.tsx +++ b/components/map.tsx @@ -199,19 +199,11 @@ const MapComponent = () => { // initial region query useEffect(() => { if (!map) return - const handleIdle = () => { - const layerExists = map.getLayer(LAYERS.counties.layerIds.fill) - const sourceLoaded = map.isSourceLoaded(LAYERS.regions.sourceId) - - if (layerExists && sourceLoaded) { - map.off('idle', handleIdle) - updateGeographies() - } - } - map.on('idle', handleIdle) - return () => { - map.off('idle', handleIdle) + const init = async () => { + await ensureSourceLoaded(map, LAYERS.regions.sourceId) + updateGeographies() } + init() }, [map, updateGeographies]) useEffect(() => { From 267fe7d7afe761419903a6fb8ffd034261d99f2e Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 11:22:53 -0600 Subject: [PATCH 05/10] add typecheck script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index cd3775b..92ff9a9 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "typecheck": "tsc --noEmit" }, "dependencies": { "@carbonplan/charts": "^3.1.1", From d209c07be26c8f16a8846c934b35ddb505993d7b Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 11:25:00 -0600 Subject: [PATCH 06/10] add pre-commit config --- .pre-commit-config.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6f0a32b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +repos: + - repo: local + hooks: + - id: prettier + name: prettier + entry: npx prettier --write + language: system + files: "\\.(\ + js|jsx\ + |ts|tsx\ + |json\ + )$" + - id: lint + name: lint + entry: npx eslint --fix + language: system + files: "\\.(\ + js|jsx\ + |ts|tsx\ + )$" + - id: typecheck + name: typecheck + entry: npm run typecheck + language: system + pass_filenames: false From bbb2cdcc51a308d59ca555b479c707efa994b70b Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 11:33:44 -0600 Subject: [PATCH 07/10] add ci --- .github/workflows/main.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..fba4f4c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,38 @@ +name: CI + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22.x + - run: npm install + - uses: actions/setup-python@v5 + - uses: pre-commit/action@v3.0.1 + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + - run: npm install + - run: npm run typecheck + + build: + runs-on: ubuntu-latest + needs: [lint, typecheck] + steps: + - uses: actions/checkout@v4 + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + - run: npm install + - run: npm run build From bf72ff5903bf5df3257a8970db037697f06dd83d Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 11:34:04 -0600 Subject: [PATCH 08/10] format fixes --- README.md | 3 +-- components/geocode/menu.tsx | 14 ++++++++++++-- components/map-layers.tsx | 4 ++-- components/map.tsx | 4 +++- components/results/other-factors.tsx | 3 ++- components/zarr-layer.tsx | 3 +-- lib/risk-utils.ts | 3 +-- lib/store.ts | 3 ++- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6594ee0..5842d6d 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,10 @@ **explore building scale climate risk data** -This tool allows exploration of climate risk data we [developed](https://github.com/carbonplan/ocr). Read our [explainer](https://carbonplan.org/research/climate-risk-explainer), [methods](https://carbonplan.org/research/climate-risk-fire-methods), and [FAQ](https://carbonplan.org/research/climate-risk-faq) for more information. +This tool allows exploration of climate risk data we [developed](https://github.com/carbonplan/ocr). Read our [explainer](https://carbonplan.org/research/climate-risk-explainer), [methods](https://carbonplan.org/research/climate-risk-fire-methods), and [FAQ](https://carbonplan.org/research/climate-risk-faq) for more information. [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) - ## local development ```shell diff --git a/components/geocode/menu.tsx b/components/geocode/menu.tsx index f30dd0d..ac8b5e8 100644 --- a/components/geocode/menu.tsx +++ b/components/geocode/menu.tsx @@ -18,13 +18,23 @@ interface Props { type Ref = HTMLDivElement const Menu = forwardRef( - ({ suggestions, selectedIndex, errorMessage, onSelectSuggestion, listboxId, errorId }, ref) => { + ( + { + suggestions, + selectedIndex, + errorMessage, + onSelectSuggestion, + listboxId, + errorId, + }, + ref, + ) => { return ( {(suggestions.length > 0 || errorMessage) && ( { setRiskRaster(e.target.checked)} - aria-label="Toggle risk raster visibility" + aria-label='Toggle risk raster visibility' /> { setSatellite(e.target.checked)} - aria-label="Toggle satellite imagery visibility" + aria-label='Toggle satellite imagery visibility' /> { const getInitialZoom = useCallback((): number => { const width = window.innerWidth const sidebarBreakpoint = theme?.breakpoints?.[1] ?? '64em' - const hasSidebar = window.matchMedia(`(min-width: ${sidebarBreakpoint})`).matches + const hasSidebar = window.matchMedia( + `(min-width: ${sidebarBreakpoint})`, + ).matches const mapWidth = hasSidebar ? width * (2 / 3) : width return Math.log2(mapWidth) - 6.3 }, [theme.breakpoints]) diff --git a/components/results/other-factors.tsx b/components/results/other-factors.tsx index a4ebf29..ad71ec4 100644 --- a/components/results/other-factors.tsx +++ b/components/results/other-factors.tsx @@ -28,7 +28,8 @@ const OtherFactors = () => { The risk described above does not account for several important factors, including those below, which could influence the actual wildfire risk of a given location. For more information on these factors, see our{' '} - FAQ. + FAQ + . { const setZarrLoading = useStore((state) => state.setZarrLoading) const layerRef = useRef(null) - const riskAttribute = - timePeriod === 'current' ? 'rps_2011' : 'rps_2047' + const riskAttribute = timePeriod === 'current' ? 'rps_2011' : 'rps_2047' const customFrag = useMemo(() => { const boundaries = colorLimits.binBoundaries || [] diff --git a/lib/risk-utils.ts b/lib/risk-utils.ts index 95cbc6f..f383e14 100644 --- a/lib/risk-utils.ts +++ b/lib/risk-utils.ts @@ -25,8 +25,7 @@ export const getGeographyMedianRiskKey: ( ) => (typeof GEOGRAPHY_ATTRIBUTE_KEYS)[keyof typeof GEOGRAPHY_ATTRIBUTE_KEYS] = ( timePeriod: ScenarioKey, ) => { - const key = - timePeriod === 'current' ? 'rps_2011_median' : 'rps_2047_median' + const key = timePeriod === 'current' ? 'rps_2011_median' : 'rps_2047_median' return GEOGRAPHY_ATTRIBUTE_KEYS[key] } diff --git a/lib/store.ts b/lib/store.ts index 8da4b3e..a360d62 100644 --- a/lib/store.ts +++ b/lib/store.ts @@ -105,7 +105,8 @@ export const useStore = create((set, get) => ({ selectedGeographyLevel: 'nation', setSelectedGeographyLevel: (level) => set({ selectedGeographyLevel: level }), hasManuallySelectedGeography: false, - setHasManuallySelectedGeography: (value) => set({ hasManuallySelectedGeography: value }), + setHasManuallySelectedGeography: (value) => + set({ hasManuallySelectedGeography: value }), showGeographyHighlight: false, setShowGeographyHighlight: (show) => set({ showGeographyHighlight: show }), geographyLayerVisibility: { From 9e85e8ea0fd1c9c704656afcea50437cc648b30f Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 11:43:57 -0600 Subject: [PATCH 09/10] use npm ci --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fba4f4c..cc21640 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 22.x - - run: npm install + - run: npm ci - uses: actions/setup-python@v5 - uses: pre-commit/action@v3.0.1 @@ -22,7 +22,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22.x - - run: npm install + - run: npm ci - run: npm run typecheck build: @@ -34,5 +34,5 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22.x - - run: npm install + - run: npm ci - run: npm run build From c882aede723bdbc59184affb2408988a01fa56a1 Mon Sep 17 00:00:00 2001 From: Shane Loeffler Date: Fri, 13 Feb 2026 13:41:42 -0600 Subject: [PATCH 10/10] don't send download abort errors to plausible --- components/results/download.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/results/download.tsx b/components/results/download.tsx index 5ed9163..6b29d19 100644 --- a/components/results/download.tsx +++ b/components/results/download.tsx @@ -117,11 +117,13 @@ export const Download = () => { document.body.removeChild(a) setLoading((prev) => ({ ...prev, [format]: false })) - } catch { - track('data_download_error', { - geography: selectedGeographyLevel, - geoid: geoid ?? '', - }) + } catch (error) { + if (!(error instanceof DOMException && error.name === 'AbortError')) { + track('data_download_error', { + geography: selectedGeographyLevel, + geoid: geoid ?? '', + }) + } setLoading((prev) => ({ ...prev, [format]: false })) } }