Files
omarchy/shell/plugins/panels/elsewhen/Sun.js
T
David Heinemeier HanssonandClaude Opus 5.5 e7218e36f2 Move Elsewhen into Omarchy as omarchy.elsewhen (#13429)
* Move Elsewhen into Omarchy as omarchy.elsewhen

The world clock ships in the shell tree instead of its own package. A
migration renames existing bar entries with their settings and removes
the retired package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove Elsewhen's shelved Earth row, overlap band and sky tint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tidy Elsewhen's models, data helper and docs, and drop currency

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Restructure Elsewhen's panel and globe on the shared shell components

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show Elsewhen's bar globe upright

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the shared city search for Elsewhen's globe jump bar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep legacy Elsewhen entries single and retry offline geocodes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fetch Elsewhen's weather with curl instead of a Python helper

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:15:00 +02:00

111 lines
3.5 KiB
JavaScript

.pragma library
.import "GlobeModel.js" as Solar
// Sunrise and sunset for a place on its own day, from the globe's own
// subsolarPoint so the strip and the terminator cannot disagree.
var DEG = Math.PI / 180
// Refraction plus the sun's radius, the figure published sunrise tables use.
var HORIZON = -0.833
function wrap180(deg) {
return ((deg + 540) % 360) - 180
}
// Local solar noon. The subsolar meridian moves 15 degrees an hour, so three
// corrections take the residual below a second.
function solarNoonMs(lon, nearMs) {
var t = nearMs
for (var i = 0; i < 3; i++) {
var sub = Solar.subsolarPoint(t)
t += wrap180(sub.lon - lon) / 15 * 3600000
}
return t
}
// The zone's own local midnight, as a UTC instant.
function localMidnightMs(ms, offsetMinutes) {
var local = ms + offsetMinutes * 60000
return Math.floor(local / 86400000) * 86400000 - offsetMinutes * 60000
}
// Sunrise and sunset for the local day containing `ms`, as instants and as
// minutes from local midnight. The minutes may fall outside 0..1440 for a zone
// far from its own sun. `kind` is "normal", "midnightSun" or "polarNight".
function sunTimes(lat, lon, ms, offsetMinutes) {
var midnight = localMidnightMs(ms, offsetMinutes)
var noon = solarNoonMs(lon, midnight + 43200000)
var dec = Solar.subsolarPoint(noon).lat
var cosH = (Math.sin(HORIZON * DEG) - Math.sin(lat * DEG) * Math.sin(dec * DEG))
/ (Math.cos(lat * DEG) * Math.cos(dec * DEG))
if (cosH <= -1)
return { kind: "midnightSun", noonMs: noon, riseMs: null, setMs: null,
riseMinutes: null, setMinutes: null, dayMinutes: 1440 }
if (cosH >= 1)
return { kind: "polarNight", noonMs: noon, riseMs: null, setMs: null,
riseMinutes: null, setMinutes: null, dayMinutes: 0 }
var halfDayMs = Math.acos(cosH) / DEG / 15 * 3600000
var riseMs = noon - halfDayMs
var setMs = noon + halfDayMs
return {
kind: "normal",
noonMs: noon,
riseMs: riseMs,
setMs: setMs,
riseMinutes: (riseMs - midnight) / 60000,
setMinutes: (setMs - midnight) / 60000,
dayMinutes: (setMs - riseMs) / 60000
}
}
// The lit parts of a 24-hour strip as 0..1 spans. The day is also drawn a day
// either side, so a sunset past midnight lights the start of the bar too.
function litSpans(times) {
if (!times) return []
if (times.kind === "polarNight") return []
if (times.kind === "midnightSun") return [{ x0: 0, x1: 1 }]
var out = []
for (var shift = -1440; shift <= 1440; shift += 1440) {
var a = Math.max(0, Math.min(1440, times.riseMinutes + shift))
var b = Math.max(0, Math.min(1440, times.setMinutes + shift))
if (b > a) out.push({ x0: a / 1440, x1: b / 1440 })
}
return out
}
// Where an event's tick goes, or null when it falls off this bar.
function eventMark(minutes) {
if (minutes === null || minutes === undefined) return null
if (minutes < 0 || minutes > 1440) return null
return minutes / 1440
}
// Daylight at `minutes`, read from the same three days litSpans draws.
function litAt(times, minutes) {
if (!times) return false
if (times.kind === "midnightSun") return true
if (times.kind === "polarNight") return false
for (var shift = -1440; shift <= 1440; shift += 1440)
if (minutes >= times.riseMinutes + shift && minutes < times.setMinutes + shift)
return true
return false
}
if (typeof module !== "undefined") {
module.exports = {
solarNoonMs: solarNoonMs,
localMidnightMs: localMidnightMs,
sunTimes: sunTimes,
litSpans: litSpans,
eventMark: eventMark,
litAt: litAt
}
}