diff --git a/assets/css/base.css b/assets/css/base.css index 14e2ec1..06ca1ef 100644 --- a/assets/css/base.css +++ b/assets/css/base.css @@ -1,3 +1,15 @@ +/* ── SELF-HOSTED FONTS ───────────────────────────────────────────────── */ +@font-face { font-family: 'Manrope'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/manrope-400.woff2') format('woff2'); } +@font-face { font-family: 'Manrope'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/manrope-500.woff2') format('woff2'); } +@font-face { font-family: 'Manrope'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/manrope-600.woff2') format('woff2'); } +@font-face { font-family: 'Manrope'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/manrope-700.woff2') format('woff2'); } +@font-face { font-family: 'Fraunces'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/fraunces-700-normal.woff2') format('woff2'); } +@font-face { font-family: 'Fraunces'; font-style: normal; font-weight: 900; font-display: swap; src: url('../fonts/fraunces-900-normal.woff2') format('woff2'); } +@font-face { font-family: 'Fraunces'; font-style: italic; font-weight: 300; font-display: swap; src: url('../fonts/fraunces-300-italic.woff2') format('woff2'); } +@font-face { font-family: 'Fraunces'; font-style: italic; font-weight: 700; font-display: swap; src: url('../fonts/fraunces-700-italic.woff2') format('woff2'); } +@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/jetbrains-mono-400.woff2') format('woff2'); } +@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/jetbrains-mono-600.woff2') format('woff2'); } + /* ════════════════════════════════════════════════════════════════════════ base.css — Fonts, reset, page shell, and top navigation. ════════════════════════════════════════════════════════════════════════ diff --git a/assets/fonts/fraunces-300-italic.woff2 b/assets/fonts/fraunces-300-italic.woff2 new file mode 100644 index 0000000..f15af2e Binary files /dev/null and b/assets/fonts/fraunces-300-italic.woff2 differ diff --git a/assets/fonts/fraunces-700-italic.woff2 b/assets/fonts/fraunces-700-italic.woff2 new file mode 100644 index 0000000..313590d Binary files /dev/null and b/assets/fonts/fraunces-700-italic.woff2 differ diff --git a/assets/fonts/fraunces-700-normal.woff2 b/assets/fonts/fraunces-700-normal.woff2 new file mode 100644 index 0000000..71bbace Binary files /dev/null and b/assets/fonts/fraunces-700-normal.woff2 differ diff --git a/assets/fonts/fraunces-900-normal.woff2 b/assets/fonts/fraunces-900-normal.woff2 new file mode 100644 index 0000000..32f67e3 Binary files /dev/null and b/assets/fonts/fraunces-900-normal.woff2 differ diff --git a/assets/fonts/jetbrains-mono-400.woff2 b/assets/fonts/jetbrains-mono-400.woff2 new file mode 100644 index 0000000..5858873 Binary files /dev/null and b/assets/fonts/jetbrains-mono-400.woff2 differ diff --git a/assets/fonts/jetbrains-mono-600.woff2 b/assets/fonts/jetbrains-mono-600.woff2 new file mode 100644 index 0000000..59c24e7 Binary files /dev/null and b/assets/fonts/jetbrains-mono-600.woff2 differ diff --git a/assets/fonts/manrope-400.woff2 b/assets/fonts/manrope-400.woff2 new file mode 100644 index 0000000..7eaaa95 Binary files /dev/null and b/assets/fonts/manrope-400.woff2 differ diff --git a/assets/fonts/manrope-500.woff2 b/assets/fonts/manrope-500.woff2 new file mode 100644 index 0000000..70447ee Binary files /dev/null and b/assets/fonts/manrope-500.woff2 differ diff --git a/assets/fonts/manrope-600.woff2 b/assets/fonts/manrope-600.woff2 new file mode 100644 index 0000000..7cc09a3 Binary files /dev/null and b/assets/fonts/manrope-600.woff2 differ diff --git a/assets/fonts/manrope-700.woff2 b/assets/fonts/manrope-700.woff2 new file mode 100644 index 0000000..84f7af5 Binary files /dev/null and b/assets/fonts/manrope-700.woff2 differ diff --git a/assets/js/app.js b/assets/js/app.js index 97650d4..2de30e5 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -54,8 +54,8 @@ export function UTCIForecast() { visibleCols, setVisibleCols, toggleCol, showDecimals, toggleShowDecimals, showUnits, toggleShowUnits, - activityOptions, placeOptions, - activityValue, activityLabel, placeValue, placeLabel, + activityOptions, placeOptions, workOptions, + activityValue, activityLabel, placeValue, placeLabel, workValue, workLabel, skinType, setSkinType, vehicleType, setVehicleType, vehicleVent, setVehicleVent, @@ -321,12 +321,14 @@ export function UTCIForecast() { proPromptSource=${proPromptSource} setProPromptSource=${setProPromptSource} activeProfile=${activeProfile} activateProfile=${activateProfile} activeCols=${activeCols} visibleCols=${visibleCols} - activityOptions=${activityOptions} placeOptions=${placeOptions} + activityOptions=${activityOptions} placeOptions=${placeOptions} workOptions=${workOptions} activityValue=${activityValue} activityLabel=${activityLabel} placeValue=${placeValue} placeLabel=${placeLabel} + workValue=${workValue} workLabel=${workLabel} outdoorsVariant=${outdoorsVariant} setOutdoorsVariantAndSave=${setOutdoorsVariantAndSave} setActiveProfile=${setActiveProfile} setVisibleCols=${setVisibleCols} setIndoorMode=${setIndoorMode} setIndoorManaged=${setIndoorManaged} + setBuildingType=${setBuildingType} dayTabsRef=${dayTabsRef} canScrollLeft=${canScrollLeft} canScrollRight=${canScrollRight} scrollDayTabs=${scrollDayTabs} />`} diff --git a/assets/js/components/DayTabs.js b/assets/js/components/DayTabs.js index e659274..61ec342 100644 --- a/assets/js/components/DayTabs.js +++ b/assets/js/components/DayTabs.js @@ -22,10 +22,13 @@ // visibleCols - object of col-key -> boolean visibility // activityOptions - dropdown options for Activities selector // placeOptions - dropdown options for Places selector +// workOptions - dropdown options for Work selector // activityValue - current activity dropdown value // activityLabel - current activity dropdown label // placeValue - current place dropdown value // placeLabel - current place dropdown label +// workValue - current work dropdown value +// workLabel - current work dropdown label // outdoorsVariant - current outdoors sub-variant key // setOutdoorsVariantAndSave - setter that also persists to localStorage // setActiveProfile - raw setter for activeProfile @@ -41,7 +44,7 @@ import { h, Fragment } from '../../vendor/preact.js'; import htm from '../../vendor/htm.js'; import { confidenceBand } from '../utils.js'; -import { FREE_DAYS, FILTER_PROFILES, OUTDOORS_VARIANTS, profileButtonOrder, activityVariantKeys, placeVariantKeys } from '../config.js'; +import { FREE_DAYS, FILTER_PROFILES, OUTDOORS_VARIANTS, profileButtonOrder, activityVariantKeys, placeVariantKeys, workVariantKeys } from '../config.js'; import { CloudIcon, PrecipIcon, CustomSelect } from '../components.js'; const html = htm.bind(h); @@ -54,12 +57,13 @@ export function DayTabs({ proPromptSource, setProPromptSource, activeProfile, activateProfile, activeCols, visibleCols, - activityOptions, placeOptions, + activityOptions, placeOptions, workOptions, activityValue, activityLabel, placeValue, placeLabel, + workValue, workLabel, outdoorsVariant, setOutdoorsVariantAndSave, setActiveProfile, setVisibleCols, - setIndoorMode, setIndoorManaged, + setIndoorMode, setIndoorManaged, setBuildingType, dayTabsRef, canScrollLeft, canScrollRight, scrollDayTabs, }) { return html` @@ -398,11 +402,34 @@ export function DayTabs({ <${CustomSelect} key="activities" value=${activityValue} - isOn=${activeProfile === 'farming' || (activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant))} + isOn=${activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant)} noHide=${true} hideLabel="🎯 Activities" buttonLabel=${activityValue === 'off' ? '🎯 Activities' : ` ${activityLabel}`} options=${activityOptions} + onChange=${(v) => { + if (v === 'off') { activateProfile('basic'); return; } + if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) { + setProPromptSource(`variant:${v}`); + setProPromptDay(0); + return; + } + try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch (e) { /* ignore */ } + setActiveProfile('outdoors'); + setOutdoorsVariantAndSave(v); + setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols }); + setIndoorMode('off'); + setIndoorManaged(false); + }} + /> + <${CustomSelect} + key="work" + value=${workValue} + isOn=${activeProfile === 'farming' || (activeProfile === 'outdoors' && workVariantKeys.includes(outdoorsVariant))} + noHide=${true} + hideLabel="💼 Work" + buttonLabel=${workValue === 'off' ? '💼 Work' : ` ${workLabel}`} + options=${workOptions} onChange=${(v) => { if (v === 'off') { activateProfile('basic'); return; } if (v === 'farming') { activateProfile('farming'); return; } @@ -415,8 +442,14 @@ export function DayTabs({ setActiveProfile('outdoors'); setOutdoorsVariantAndSave(v); setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols }); - setIndoorMode('off'); - setIndoorManaged(false); + if (v === 'office') { + setBuildingType('office'); + setIndoorMode('on'); + setIndoorManaged(false); + } else { + setIndoorMode('off'); + setIndoorManaged(false); + } }} /> diff --git a/assets/js/config.js b/assets/js/config.js index bbe94a8..c4ce865 100644 --- a/assets/js/config.js +++ b/assets/js/config.js @@ -94,6 +94,10 @@ export const OUTDOORS_VARIANTS = { hiking: { name: 'Hiking', proOnly: true, cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } }, photography: { name: 'Photography', proOnly: true, cols: { hour: true, air: true, rh: false, dew: false, wind: false, dir: false, cloud: true, sun: true, direct: true, diffuse: true, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } }, fishing: { name: 'Fishing', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } }, + // Work variants + market: { name: 'Market Trading', cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } }, + windowcleaning: { name: 'Window Cleaning', proOnly: true, cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } }, + office: { name: 'Office', proOnly: true, cols: { hour: true, air: true, rh: true, dew: false, wind: false, dir: false, cloud: true, sun: true, direct: true, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: true, vis: false, aqi: true, pollen: false } }, }; // Pollen type options shown in the pulldown selector. @@ -128,13 +132,19 @@ export const variantIcons = { hiking: '🏔️', photography: '📸', fishing: '🎣', + market: '🏪', + windowcleaning: '🪟', + office: '🏢', }; // Which variant keys appear in the Activities dropdown. -export const activityVariantKeys = ['cycling', 'running', 'dogwalk', 'sailing', 'wintersports', 'naturist', 'hiking', 'photography', 'fishing']; +export const activityVariantKeys = ['dogwalk', 'running', 'cycling', 'hiking', 'fishing', 'sailing', 'wintersports', 'photography', 'naturist']; // Which variant keys appear in the Places dropdown. -export const placeVariantKeys = ['urban', 'beach', 'events', 'park', 'airport', 'festival', 'construction']; +export const placeVariantKeys = ['urban', 'park', 'beach', 'events', 'festival', 'airport']; + +// Which variant keys appear in the Work dropdown. +export const workVariantKeys = ['farming', 'construction', 'market', 'windowcleaning', 'office']; // Tooltip text shown when the user clicks/hovers a table column header. export const COL_DESCRIPTIONS = { diff --git a/assets/js/hooks/useAppState.js b/assets/js/hooks/useAppState.js index 35de0e1..52fff94 100644 --- a/assets/js/hooks/useAppState.js +++ b/assets/js/hooks/useAppState.js @@ -26,7 +26,7 @@ import { useState, useEffect, useRef, useCallback } from '../../vendor/preact-ho import { FREE_DAYS, FILTER_PROFILES, OUTDOORS_VARIANTS, profileButtonOrder, variantIcons, - activityVariantKeys, placeVariantKeys, + activityVariantKeys, placeVariantKeys, workVariantKeys, } from '../config.js'; import { utciCategory } from '../utils.js'; import { buildHourlyRows } from '../compute.js'; @@ -167,8 +167,20 @@ export function useAppState() { // ── 5. SKIN, VEHICLE, INDOOR, POLLEN ───────────────────────────────── const [skinType, setSkinType] = useState('II'); - const [vehicleType, setVehicleType] = useState('car'); - const [vehicleVent, setVehicleVent] = useState(false); + const [vehicleType, setVehicleType] = useState(() => { + try { return localStorage.getItem('sunscope_vehicle_type') || 'car'; } catch (e) { return 'car'; } + }); + const setVehicleTypeAndSave = (v) => { + try { localStorage.setItem('sunscope_vehicle_type', v); } catch (e) { /* ignore */ } + setVehicleType(v); + }; + const [vehicleVent, setVehicleVent] = useState(() => { + try { return localStorage.getItem('sunscope_vehicle_vent') === '1'; } catch (e) { return false; } + }); + const setVehicleVentAndSave = (v) => { + try { localStorage.setItem('sunscope_vehicle_vent', v ? '1' : '0'); } catch (e) { /* ignore */ } + setVehicleVent(v); + }; const [outdoorsVariant, setOutdoorsVariant] = useState(() => { try { return localStorage.getItem('sunscope_outdoors_variant') || 'urban'; } catch (e) { return 'urban'; } @@ -190,15 +202,40 @@ export function useAppState() { ? (OUTDOORS_VARIANTS[outdoorsVariant]?.cols ?? FILTER_PROFILES.outdoors.cols) : (FILTER_PROFILES[activeProfile]?.cols ?? FILTER_PROFILES.basic.cols); - const [buildingType, setBuildingType] = useState('brick'); - const [indoorManaged, setIndoorManaged] = useState(false); - const [indoorMode, setIndoorMode] = useState(() => { + const [buildingType, setBuildingType] = useState(() => { try { - const saved = localStorage.getItem('sunscope_profile') || 'basic'; - const cols = FILTER_PROFILES[saved]?.cols ?? FILTER_PROFILES.basic.cols; + const saved = localStorage.getItem('sunscope_building_type'); + if (saved) return saved; + // Fall back to office if the saved variant is office and no explicit building saved + const savedVariant = localStorage.getItem('sunscope_outdoors_variant') || ''; + if (savedVariant === 'office') return 'office'; + } catch (e) { /* ignore */ } + return 'brick'; + }); + const setBuildingTypeAndSave = (v) => { + try { localStorage.setItem('sunscope_building_type', v); } catch (e) { /* ignore */ } + setBuildingType(v); + }; + const [indoorManaged, setIndoorManaged] = useState(() => { + try { return localStorage.getItem('sunscope_indoor_managed') === '1'; } catch (e) { return false; } + }); + const setIndoorManagedAndSave = (v) => { + try { localStorage.setItem('sunscope_indoor_managed', v ? '1' : '0'); } catch (e) { /* ignore */ } + setIndoorManaged(v); + }; + const [indoorMode, setIndoorMode] = useState(() => { + try { + const saved = localStorage.getItem('sunscope_indoor_mode'); + if (saved) return saved; + const profile = localStorage.getItem('sunscope_profile') || 'basic'; + const cols = FILTER_PROFILES[profile]?.cols ?? FILTER_PROFILES.basic.cols; return (cols['indoorT'] || cols['managedT']) ? 'on' : 'off'; } catch (e) { return 'off'; } }); + const setIndoorModeAndSave = (v) => { + try { localStorage.setItem('sunscope_indoor_mode', v); } catch (e) { /* ignore */ } + setIndoorMode(v); + }; const [pollenType, setPollenType] = useState(() => { try { return localStorage.getItem('sunscope_pollen_type') || 'all_pollen'; } catch (e) { return 'all_pollen'; } @@ -233,19 +270,13 @@ export function useAppState() { const searchTimeout = useRef(null); // Derived selectors used by profile controls - const activityOptions = [ - { - value: 'farming', - label: `${FILTER_PROFILES.farming.icon} ${FILTER_PROFILES.farming.label}`, - }, - ...activityVariantKeys.map((k) => { - const v = OUTDOORS_VARIANTS[k]; - return { - value: k, - label: `${variantIcons[k]} ${v.name}${v.proOnly && !isPro ? ' 🔒' : ''}`, - }; - }), - ]; + const activityOptions = activityVariantKeys.map((k) => { + const v = OUTDOORS_VARIANTS[k]; + return { + value: k, + label: `${variantIcons[k]} ${v.name}${v.proOnly && !isPro ? ' 🔒' : ''}`, + }; + }); const placeOptions = placeVariantKeys.map((k) => { const v = OUTDOORS_VARIANTS[k]; return { @@ -253,17 +284,31 @@ export function useAppState() { label: `${variantIcons[k]} ${v.name}${v.proOnly && !isPro ? ' 🔒' : ''}`, }; }); + const workOptions = workVariantKeys.map((k) => { + const v = k === 'farming' + ? { name: FILTER_PROFILES.farming.label, proOnly: false } + : OUTDOORS_VARIANTS[k]; + const icon = k === 'farming' ? FILTER_PROFILES.farming.icon : variantIcons[k]; + return { + value: k, + label: `${icon} ${v.name}${v.proOnly && !isPro ? ' 🔒' : ''}`, + }; + }); - const activityValue = activeProfile === 'farming' - ? 'farming' - : activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant) - ? outdoorsVariant - : 'off'; + const activityValue = activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant) + ? outdoorsVariant + : 'off'; const activityLabel = activityOptions.find((o) => o.value === activityValue)?.label; const placeValue = activeProfile === 'outdoors' && placeVariantKeys.includes(outdoorsVariant) ? outdoorsVariant : 'off'; const placeLabel = placeOptions.find((o) => o.value === placeValue)?.label; + const workValue = activeProfile === 'farming' + ? 'farming' + : activeProfile === 'outdoors' && workVariantKeys.includes(outdoorsVariant) + ? outdoorsVariant + : 'off'; + const workLabel = workOptions.find((o) => o.value === workValue)?.label; // ── 6. TABLE REFS ──────────────────────────────────────────────────── const headStickyRef = useRef(null); @@ -439,17 +484,18 @@ export function useAppState() { activeProfile, setActiveProfile, activateProfile, activeCols, visibleCols, setVisibleCols, toggleCol, - activityOptions, placeOptions, + activityOptions, placeOptions, workOptions, activityValue, activityLabel, placeValue, placeLabel, + workValue, workLabel, // skin, vehicle, indoor, pollen skinType, setSkinType, - vehicleType, setVehicleType, - vehicleVent, setVehicleVent, + vehicleType, setVehicleType: setVehicleTypeAndSave, + vehicleVent, setVehicleVent: setVehicleVentAndSave, outdoorsVariant, setOutdoorsVariantAndSave, - buildingType, setBuildingType, - indoorManaged, setIndoorManaged, - indoorMode, setIndoorMode, + buildingType, setBuildingType: setBuildingTypeAndSave, + indoorManaged, setIndoorManaged: setIndoorManagedAndSave, + indoorMode, setIndoorMode: setIndoorModeAndSave, pollenType, setPollenTypeAndSave, showDecimals, toggleShowDecimals, showUnits, toggleShowUnits, diff --git a/assets/js/utils.js b/assets/js/utils.js index b40a759..8151adf 100644 --- a/assets/js/utils.js +++ b/assets/js/utils.js @@ -211,6 +211,7 @@ export const BUILDING_TYPES = { timber: { name: 'Timber Frame / New Build', uWall: 0.22, lagHours: 2, glazingRatio: 0.22, gValue: 0.35, orientFactor: 0.50, curtainBlock: 0.70, ventAlpha: 0.35, solarScale: 0.08, baseTemp: 17.0, internalGain: 0.8, retainedScale: 0.45 }, flat: { name: 'Top-floor Flat', uWall: 0.40, lagHours: 3, glazingRatio: 0.18, gValue: 0.63, orientFactor: 0.50, curtainBlock: 0.75, ventAlpha: 0.20, solarScale: 0.11, baseTemp: 17.0, internalGain: 0.9, retainedScale: 0.45 }, conservatory:{ name: 'Conservatory / Sun Room', uWall: 1.20, lagHours: 1, glazingRatio: 0.70, gValue: 0.72, orientFactor: 0.70, curtainBlock: 0.50, ventAlpha: 0.50, solarScale: 0.045, baseTemp: 12.0, internalGain: 0.2, retainedScale: 0.05 }, + office: { name: 'Office Block', uWall: 0.25, lagHours: 2, glazingRatio: 0.45, gValue: 0.55, orientFactor: 0.65, curtainBlock: 0.50, ventAlpha: 0.15, solarScale: 0.055, baseTemp: 18.5, internalGain: 2.5, retainedScale: 0.30 }, }; // ------------------------------------------------------------------- diff --git a/assets/sunscope.css b/assets/sunscope.css index da44800..1532dec 100644 --- a/assets/sunscope.css +++ b/assets/sunscope.css @@ -29,5 +29,5 @@ @import "./css/base.css"; @import "./css/header.css"; -@import "./css/table.css?v=2"; +@import "./css/table.css"; @import "./css/ui.css"; diff --git a/index.html b/index.html index 8a9ff5f..68d4638 100644 --- a/index.html +++ b/index.html @@ -68,15 +68,7 @@ } - - - diff --git a/package-lock.json b/package-lock.json index 6cd18f9..08e9ca5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,11 @@ "": { "name": "sunscope", "version": "1.0.0", + "dependencies": { + "@fontsource/fraunces": "^5.2.9", + "@fontsource/jetbrains-mono": "^5.2.8", + "@fontsource/manrope": "^5.2.8" + }, "devDependencies": { "esbuild": "^0.21.5" } @@ -402,6 +407,33 @@ "node": ">=12" } }, + "node_modules/@fontsource/fraunces": { + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/@fontsource/fraunces/-/fraunces-5.2.9.tgz", + "integrity": "sha512-XDzuddBtoC7BZgZdBn6b7hsFZY2+V1hgN7yca5fBTKuHjb/lOd45a0Ji8dTUgFhPoL7RdGupo+bC2BFSt6UH8Q==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/manrope": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/manrope/-/manrope-5.2.8.tgz", + "integrity": "sha512-gJHJmcuUk7qWcNCfcAri/DJQtXtBYqi9yKratr4jXhSo0I3xUtNNKI+igQIcw5c+m95g0vounk8ZnX/kb8o0TA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", diff --git a/package.json b/package.json index a72f1f0..9f5ec61 100644 --- a/package.json +++ b/package.json @@ -9,5 +9,10 @@ }, "devDependencies": { "esbuild": "^0.21.5" + }, + "dependencies": { + "@fontsource/fraunces": "^5.2.9", + "@fontsource/jetbrains-mono": "^5.2.8", + "@fontsource/manrope": "^5.2.8" } }