├── README.md
├── index.html
├── index.js
├── index.zip
├── lake.jpg
├── manifest.json
├── spacegrotesk.woff2
└── weatherCodes.js
/README.md:
--------------------------------------------------------------------------------
1 | # startlake
2 |
3 | just another startpage. a weekend project that i kinda gave up on.
4 |
5 | 
6 |
7 | ## installing
8 |
9 | ### firefox
10 |
11 | regular firefox requires extensions to be [signed](https://extensionworkshop.com/documentation/publish/signing-and-distribution-overview/). firefox nightly does not require signing.
12 |
13 | 1. go to `about:addons`.
14 | 2. click the gear icon in the top right.
15 | 3. click "Install Add-on From File..."
16 | 4. select `index.zip` from this repo (if you are using nightly) or the `.xpi` file (if you are using regular firefox and have signed the extension).
17 | 5. create a new tab. if it asks you, select "Keep Changes".
18 |
19 | bonus: install the firefox color extension and use [this theme](https://color.firefox.com/?theme=XQAAAAK6AgAAAAAAAABBqYhm849SCicxcUEYWXcGHf3p79Ffm1p9Wc4wq53dKzq9lNGpZo8BuIsCkVkhGB-b71b_bH2GAn3WyUogVaz_7oMq3PdWBi1tWXc0s4NIAQJS28Fxe8MjMBa4kcq36Ap2Us_AykwqbGWT3hsVv7qSFMrFjAsHv3iRAYPPHY3TpdofkQjV7e6OjSzNQH5yVQRuXnnFhwwrQDYia_UIdBvjbErAMtrQh1v2_ova4_704BscrUZgYcyMx7CH_oR3VhNm4jn-xIiWEHn19HoT-4Vb2lUkMOTCdqdi-K-tp_x3HLIaCHBxDqK2b-EHYv90tRBKTi-EHnrEJxop91Od_oa1FDlwy5_XEqu-sSjioq-zb94BuJ5Pr9S300fE_ZYk8z3_WJIjAA) for matching firefox colors.
20 |
21 | ### chrome
22 |
23 | currently it doesn't work on chrome, but it shouldn't be too hard to modify.
24 |
25 | ## notes
26 |
27 | you can open the settings by clicking the hidden gear button in the top right corner. settings are stored in localstorage. the settings will automatically be visible if you do not have anything set.
28 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ~
7 |
8 |
9 |
187 |
188 |
189 |
190 |
191 |
192 |
199 |
200 |
12.00
201 |
wednesday
202 |
203 |
204 |
100°
205 |
partly cloudy
206 |
207 |
208 |
209 |
216 |
223 |
228 |
233 |
238 |
239 |
240 |
265 |
266 |
272 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import { weatherCodes } from './weatherCodes.js'
2 |
3 | document.addEventListener('DOMContentLoaded', () => {
4 | const settingsForm = document.getElementById('settings-form')
5 | const toggleSettingsButton = document.getElementById('toggle-settings')
6 |
7 | const settings = JSON.parse(localStorage.getItem('settings'))
8 |
9 | if (!settings) {
10 | settingsForm.style.display = 'block'
11 | } else {
12 | settingsForm.style.display = 'none'
13 | toggleSettingsButton.style.display = 'block'
14 | }
15 |
16 | toggleSettingsButton.addEventListener('click', () => {
17 | if (settingsForm.style.display === 'none') {
18 | settingsForm.style.display = 'block'
19 | } else {
20 | settingsForm.style.display = 'none'
21 | }
22 | })
23 | })
24 |
25 | document.getElementById('settings-form').addEventListener('submit', (event) => {
26 | event.preventDefault()
27 | lat = document.getElementById('latitude').value
28 | lon = document.getElementById('longitude').value
29 | units = document.getElementById('units').value
30 | const timeFormat = document.getElementById('time-format').value
31 | twelveHour = timeFormat === '12'
32 |
33 | localStorage.setItem(
34 | 'settings',
35 | JSON.stringify({ lat, lon, units, timeFormat })
36 | )
37 | document.getElementById('settings-form').style.display = 'none'
38 | document.getElementById('toggle-settings').style.display = 'block'
39 | localStorage.removeItem('weatherData')
40 | })
41 |
42 | const settings = JSON.parse(localStorage.getItem('settings')) || {
43 | lat: '0',
44 | lon: '0',
45 | units: 'fahrenheit',
46 | timeFormat: '12',
47 | }
48 |
49 | let lat = settings.lat
50 | let lon = settings.lon
51 | let units = settings.units
52 | let twelveHour = settings.timeFormat === '12'
53 |
54 | let prevSec = -1
55 | let isFetching = false
56 |
57 | async function updateClock() {
58 | const now = new Date()
59 | const currSec = now.getSeconds()
60 |
61 | if (currSec !== prevSec) {
62 | prevSec = currSec
63 |
64 | let hours = now.getHours()
65 | const ampm = hours >= 12 ? 'pm' : 'am'
66 | if (twelveHour) {
67 | hours = hours % 12 || 12
68 | }
69 |
70 | const minutes = now.getMinutes().toString().padStart(2, '0')
71 | const seconds = currSec.toString().padStart(2, '0')
72 |
73 | document.querySelector('.time').textContent = `${hours}:${minutes}`
74 | document.querySelector('.sec').textContent = seconds
75 | document.querySelector('.ampm').textContent = ampm
76 | document.querySelector('.date').textContent = `${
77 | now.getMonth() + 1
78 | }.${now.getDate()}`
79 | document.querySelector('.day').textContent = now
80 | .toLocaleDateString('en-US', { weekday: 'long' })
81 | .toLocaleLowerCase()
82 |
83 | checkWeather(now)
84 | }
85 |
86 | requestAnimationFrame(updateClock)
87 | }
88 |
89 | function checkWeather(now) {
90 | const cachedData = localStorage.getItem('weatherData')
91 | if (cachedData) {
92 | const data = JSON.parse(cachedData)
93 | const cachedTime = new Date(data.current.time)
94 |
95 | if (now - cachedTime < (15 * 60 + 10) * 1000) {
96 | updateWeather(data)
97 | return
98 | }
99 | }
100 |
101 | if (!isFetching) {
102 | fetchAPI()
103 | }
104 | }
105 |
106 | async function fetchAPI() {
107 | console.log(lat, lon, units)
108 | try {
109 | isFetching = true
110 | const response = await fetch(
111 | `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m,weather_code&temperature_unit=${units}`
112 | )
113 | const data = await response.json()
114 | console.log('successful fetch', data)
115 | localStorage.setItem('weatherData', JSON.stringify(data))
116 | updateWeather(data)
117 | isFetching = false
118 | } catch (error) {
119 | console.error('error fetching weather data:', error)
120 | isFetching = false
121 | }
122 | }
123 |
124 | function updateWeather(data) {
125 | const temperature = Math.round(data.current.temperature_2m)
126 | const weatherCode = data.current.weather_code
127 | const now = new Date()
128 | const isDaytime = now.getHours() >= 6 && now.getHours() < 18
129 |
130 | document.querySelector('.temp').textContent = `${temperature}°`
131 | document.querySelector('.weather').textContent =
132 | weatherCodes[weatherCode][
133 | isDaytime ? 'dayDescription' : 'nightDescription'
134 | ].toLocaleLowerCase()
135 | }
136 |
137 | updateClock()
138 |
--------------------------------------------------------------------------------
/index.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/refact0r/startlake/847d89eea9d599fd1a58afd26e9f67d0bad40567/index.zip
--------------------------------------------------------------------------------
/lake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/refact0r/startlake/847d89eea9d599fd1a58afd26e9f67d0bad40567/lake.jpg
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "startlake",
3 | "version": "1.0",
4 | "manifest_version": 2,
5 | "chrome_url_overrides": {
6 | "newtab": "index.html"
7 | },
8 | "browser_specific_settings": {
9 | "gecko": {
10 | "id": "startlake@refact0r.dev"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/spacegrotesk.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/refact0r/startlake/847d89eea9d599fd1a58afd26e9f67d0bad40567/spacegrotesk.woff2
--------------------------------------------------------------------------------
/weatherCodes.js:
--------------------------------------------------------------------------------
1 | export const weatherCodes = {
2 | 0: {
3 | dayDescription: 'Sunny',
4 | nightDescription: 'Clear',
5 | },
6 | 1: {
7 | dayDescription: 'Sunny',
8 | nightDescription: 'Clear',
9 | },
10 | 2: {
11 | dayDescription: 'Partly Cloudy',
12 | nightDescription: 'Partly Cloudy',
13 | },
14 | 3: {
15 | dayDescription: 'Cloudy',
16 | nightDescription: 'Cloudy',
17 | },
18 | 45: {
19 | dayDescription: 'Foggy',
20 | nightDescription: 'Foggy',
21 | },
22 | 48: {
23 | dayDescription: 'Foggy',
24 | nightDescription: 'Foggy',
25 | },
26 | 51: {
27 | dayDescription: 'L Drizzle',
28 | nightDescription: 'L Drizzle',
29 | },
30 | 53: {
31 | dayDescription: 'Drizzle',
32 | nightDescription: 'Drizzle',
33 | },
34 | 55: {
35 | dayDescription: 'H Drizzle',
36 | nightDescription: 'H Drizzle',
37 | },
38 | 56: {
39 | dayDescription: 'L Drizzle',
40 | nightDescription: 'L Drizzle',
41 | },
42 | 57: {
43 | dayDescription: 'Drizzle',
44 | nightDescription: 'Drizzle',
45 | },
46 | 61: {
47 | dayDescription: 'L Rain',
48 | nightDescription: 'L Rain',
49 | },
50 | 63: {
51 | dayDescription: 'Rain',
52 | nightDescription: 'Rain',
53 | },
54 | 65: {
55 | dayDescription: 'H Rain',
56 | nightDescription: 'H Rain',
57 | },
58 | 66: {
59 | dayDescription: 'L Rain',
60 | nightDescription: 'L Rain',
61 | },
62 | 67: {
63 | dayDescription: 'Rain',
64 | nightDescription: 'Rain',
65 | },
66 | 71: {
67 | dayDescription: 'L Snow',
68 | nightDescription: 'L Snow',
69 | },
70 | 73: {
71 | dayDescription: 'Snow',
72 | nightDescription: 'Snow',
73 | },
74 | 75: {
75 | dayDescription: 'H Snow',
76 | nightDescription: 'H Snow',
77 | },
78 | 77: {
79 | dayDescription: 'Snow Grains',
80 | nightDescription: 'Snow Grains',
81 | },
82 | 80: {
83 | dayDescription: 'L Showers',
84 | nightDescription: 'L Showers',
85 | },
86 | 81: {
87 | dayDescription: 'Showers',
88 | nightDescription: 'Showers',
89 | },
90 | 82: {
91 | dayDescription: 'H Showers',
92 | nightDescription: 'H Showers',
93 | },
94 | 85: {
95 | dayDescription: 'L Snow Showers',
96 | nightDescription: 'L Snow Showers',
97 | },
98 | 86: {
99 | dayDescription: 'Snow Showers',
100 | nightDescription: 'Snow Showers',
101 | },
102 | 95: {
103 | dayDescription: 'Thunderstorm',
104 | nightDescription: 'Thunderstorm',
105 | },
106 | 96: {
107 | dayDescription: 'L Hailstorm',
108 | nightDescription: 'L Hailstorm',
109 | },
110 | 99: {
111 | dayDescription: 'Hailstorm',
112 | nightDescription: 'Hailstorm',
113 | },
114 | }
115 |
--------------------------------------------------------------------------------