termarium 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +51 -0
- package/README.md +109 -0
- package/npm/bin/termarium.js +31 -0
- package/npm/scripts/install.js +174 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Termarium contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Termarium includes a filtered bright-star subset derived from the HYG Database
|
|
2
|
+
v4.2 by David Nash / Astronomy Nexus.
|
|
3
|
+
|
|
4
|
+
Source:
|
|
5
|
+
https://codeberg.org/astronexus/hyg
|
|
6
|
+
https://www.astronexus.com/projects/hyg
|
|
7
|
+
|
|
8
|
+
The HYG Database is licensed under Creative Commons Attribution-ShareAlike 4.0
|
|
9
|
+
International (CC BY-SA 4.0). The filtered file in this repository keeps only
|
|
10
|
+
the fields needed by Termarium:
|
|
11
|
+
|
|
12
|
+
hip, proper, ra, dec, mag, ci, con
|
|
13
|
+
|
|
14
|
+
Termarium also includes a small manually curated star-alias index that maps
|
|
15
|
+
common names, Bayer-style labels, HIP/HD-style identifiers, and selected Chinese
|
|
16
|
+
names back to bundled HIP stars for offline search. This alias layer is not an
|
|
17
|
+
authoritative nomenclature catalog; it is a convenience index for the TUI.
|
|
18
|
+
|
|
19
|
+
Termarium code is licensed under MIT. The bundled star data remains licensed
|
|
20
|
+
under CC BY-SA 4.0.
|
|
21
|
+
|
|
22
|
+
Termarium also includes a converted constellation-line catalog derived from
|
|
23
|
+
ConstellationLines by Marc van der Sluys / hemel.waarnemen.com.
|
|
24
|
+
|
|
25
|
+
Source:
|
|
26
|
+
https://github.com/MarcvdSluys/ConstellationLines
|
|
27
|
+
https://zenodo.org/doi/10.5281/zenodo.10397192
|
|
28
|
+
|
|
29
|
+
The ConstellationLines data is licensed under Creative Commons Attribution 4.0
|
|
30
|
+
International (CC BY 4.0). Termarium converts the source BSC/HR star-number
|
|
31
|
+
paths to HIP-number paths and filters them to the bundled bright-star subset.
|
|
32
|
+
|
|
33
|
+
Termarium includes a small converted deep-sky catalog derived from OpenNGC
|
|
34
|
+
v20260501 by Mattia Verga and contributors.
|
|
35
|
+
|
|
36
|
+
Source:
|
|
37
|
+
https://github.com/mattiaverga/OpenNGC
|
|
38
|
+
https://github.com/mattiaverga/OpenNGC/tree/v20260501
|
|
39
|
+
|
|
40
|
+
OpenNGC is licensed under Creative Commons Attribution-ShareAlike 4.0
|
|
41
|
+
International (CC BY-SA 4.0). Termarium keeps Messier objects, converts RA/Dec
|
|
42
|
+
to decimal hours/degrees, and adds manually curated entries for M40, M45, and
|
|
43
|
+
M102 where the OpenNGC Messier field is intentionally absent or ambiguous.
|
|
44
|
+
|
|
45
|
+
Termarium's offline planet positions use low-precision orbital elements and
|
|
46
|
+
methods from NASA/JPL Approximate Positions of the Planets:
|
|
47
|
+
https://ssd.jpl.nasa.gov/planets/approx_pos.html
|
|
48
|
+
|
|
49
|
+
Termarium's offline solar position, sunrise, sunset, and twilight calculations
|
|
50
|
+
follow the public NOAA Solar Calculation Details reference:
|
|
51
|
+
https://gml.noaa.gov/grad/solcalc/calcdetails.html
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Termarium 穹顶终端
|
|
2
|
+
|
|
3
|
+
Termarium is a quiet terminal planetarium. It opens as a full-screen TUI,
|
|
4
|
+
asks for your observing location the first time, then renders a real bright-star
|
|
5
|
+
sky map with constellations, planets, Messier objects, time travel, and a moon
|
|
6
|
+
phase panel.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
cargo install termarium
|
|
10
|
+
termarium
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install the prebuilt binary through npm:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g termarium
|
|
17
|
+
termarium
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The npm installer currently ships prebuilt binaries for macOS arm64,
|
|
21
|
+
Linux x64, and Windows x64.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- Full-screen terminal UI built with Rust, Ratatui, and Crossterm
|
|
26
|
+
- Real HYG v4.2 bright-star subset, bundled for offline use
|
|
27
|
+
- 88 western constellation figures from a bundled CC BY 4.0 line catalog
|
|
28
|
+
- Offline Messier deep-sky catalog derived from OpenNGC v20260501
|
|
29
|
+
- Time travel, city tour, alias-aware target search, constellation highlight, and tonight panel
|
|
30
|
+
- Crosshair pointer mode for selecting visible stars directly from the sky map
|
|
31
|
+
- Offline low-precision Venus, Mars, Jupiter, and Saturn positions
|
|
32
|
+
- Sun altitude, daylight/twilight state, sunset, and next sunrise
|
|
33
|
+
- First-run location setup with city presets, custom coordinates, and timezone
|
|
34
|
+
- RA/Dec to Alt/Az sky projection for the current observer and time
|
|
35
|
+
- Four themes plus ASCII/Unicode character modes
|
|
36
|
+
- English by default, with in-app Chinese toggle
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
termarium
|
|
42
|
+
termarium setup
|
|
43
|
+
termarium config
|
|
44
|
+
termarium catalog-info
|
|
45
|
+
termarium --lat 31.2304 --lon 121.4737 --name Shanghai
|
|
46
|
+
termarium --lat 31.2304 --lon 121.4737 --name Shanghai --lang zh
|
|
47
|
+
termarium --lat 31.2304 --lon 121.4737 --name Shanghai --tz Asia/Shanghai
|
|
48
|
+
termarium --theme aurora --charset unicode
|
|
49
|
+
termarium --time 2026-05-07T14:00:00Z
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Inside the TUI:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
q quit
|
|
56
|
+
Esc clear selected target; quit if nothing is selected
|
|
57
|
+
←/→ switch city preset
|
|
58
|
+
space pause / resume
|
|
59
|
+
[ / ] jump one hour
|
|
60
|
+
{ / } jump one day
|
|
61
|
+
r return to live sky
|
|
62
|
+
/ search targets
|
|
63
|
+
x pointer mode; arrows move the crosshair, hover selects a star
|
|
64
|
+
Tab next visible constellation
|
|
65
|
+
S-Tab previous visible constellation
|
|
66
|
+
h toggle tonight panel
|
|
67
|
+
o settings panel
|
|
68
|
+
v city tour
|
|
69
|
+
s setup location
|
|
70
|
+
? detailed help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Quick toggles still work, but they are also available in the settings panel:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
a toggle animations
|
|
77
|
+
p toggle planets
|
|
78
|
+
d toggle deep-sky objects
|
|
79
|
+
T cycle theme
|
|
80
|
+
u cycle charset
|
|
81
|
+
t toggle English / Chinese
|
|
82
|
+
m toggle moon panel
|
|
83
|
+
l toggle labels
|
|
84
|
+
c toggle constellation lines
|
|
85
|
+
+ / - adjust limiting magnitude
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Search accepts proper names, curated aliases, HIP/HD-style identifiers, western
|
|
89
|
+
constellation names, Chinese constellation names, planets, and Messier objects.
|
|
90
|
+
For example, `Tau Ceti`, `τ Ceti`, `天仓五`, `HD 10700`, and `HIP 8102` all
|
|
91
|
+
select the same star.
|
|
92
|
+
|
|
93
|
+
## Data And License
|
|
94
|
+
|
|
95
|
+
The application code is MIT licensed. The bundled star catalog is a filtered
|
|
96
|
+
subset derived from the HYG Database v4.2 and remains under CC BY-SA 4.0. The
|
|
97
|
+
constellation-line catalog is derived from Marc van der Sluys'
|
|
98
|
+
ConstellationLines data and remains under CC BY 4.0. The deep-sky catalog is
|
|
99
|
+
derived from OpenNGC v20260501 and remains under CC BY-SA 4.0. See `NOTICE` for
|
|
100
|
+
attribution. Planet and solar calculations are local approximate algorithms
|
|
101
|
+
based on NASA/JPL and NOAA public reference formulas.
|
|
102
|
+
|
|
103
|
+
## Development
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
cargo fmt
|
|
107
|
+
cargo test
|
|
108
|
+
cargo run
|
|
109
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require("node:child_process");
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
const executable = process.platform === "win32" ? "termarium.exe" : "termarium";
|
|
8
|
+
const bundledBinary =
|
|
9
|
+
process.env.TERMARIUM_BINARY_PATH ||
|
|
10
|
+
path.join(__dirname, "..", "vendor", executable);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(bundledBinary)) {
|
|
13
|
+
console.error("Termarium binary is missing.");
|
|
14
|
+
console.error("Try reinstalling with: npm install -g termarium --force");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const result = spawnSync(bundledBinary, process.argv.slice(2), {
|
|
19
|
+
stdio: "inherit",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (result.error) {
|
|
23
|
+
console.error(result.error.message);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (result.signal) {
|
|
28
|
+
process.kill(process.pid, result.signal);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.exit(result.status ?? 0);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const childProcess = require("node:child_process");
|
|
4
|
+
const crypto = require("node:crypto");
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const https = require("node:https");
|
|
7
|
+
const os = require("node:os");
|
|
8
|
+
const path = require("node:path");
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.resolve(__dirname, "..", "..");
|
|
11
|
+
const packageJson = require(path.join(packageRoot, "package.json"));
|
|
12
|
+
const repo = "https://github.com/7b7b7b/termarium";
|
|
13
|
+
const version = packageJson.version;
|
|
14
|
+
|
|
15
|
+
const targets = {
|
|
16
|
+
"darwin-arm64": {
|
|
17
|
+
target: "aarch64-apple-darwin",
|
|
18
|
+
archive: "tar.gz",
|
|
19
|
+
binary: "termarium",
|
|
20
|
+
},
|
|
21
|
+
"linux-x64": {
|
|
22
|
+
target: "x86_64-unknown-linux-gnu",
|
|
23
|
+
archive: "tar.gz",
|
|
24
|
+
binary: "termarium",
|
|
25
|
+
},
|
|
26
|
+
"win32-x64": {
|
|
27
|
+
target: "x86_64-pc-windows-msvc",
|
|
28
|
+
archive: "zip",
|
|
29
|
+
binary: "termarium.exe",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
main().catch((error) => {
|
|
34
|
+
console.error(`termarium postinstall failed: ${error.message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
if (process.env.TERMARIUM_NPM_SKIP_DOWNLOAD === "1") {
|
|
40
|
+
console.log("termarium: skipped binary download");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const platformKey = `${process.platform}-${process.arch}`;
|
|
45
|
+
const selected = targets[platformKey];
|
|
46
|
+
if (!selected) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`unsupported platform ${platformKey}. Supported platforms: ${Object.keys(targets).join(", ")}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const asset = `termarium-${selected.target}.${selected.archive}`;
|
|
53
|
+
const releaseBase = `${repo}/releases/download/v${version}`;
|
|
54
|
+
const archiveUrl = `${releaseBase}/${asset}`;
|
|
55
|
+
const checksumUrl = `${releaseBase}/SHA256SUMS`;
|
|
56
|
+
const vendorDir = path.join(packageRoot, "npm", "vendor");
|
|
57
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "termarium-npm-"));
|
|
58
|
+
const archivePath = path.join(tmpDir, asset);
|
|
59
|
+
const extractDir = path.join(tmpDir, "extract");
|
|
60
|
+
|
|
61
|
+
fs.mkdirSync(vendorDir, { recursive: true });
|
|
62
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
console.log(`termarium: downloading ${asset}`);
|
|
66
|
+
const archive = await download(archiveUrl);
|
|
67
|
+
fs.writeFileSync(archivePath, archive);
|
|
68
|
+
|
|
69
|
+
if (process.env.TERMARIUM_NPM_SKIP_CHECKSUM !== "1") {
|
|
70
|
+
const checksums = (await download(checksumUrl)).toString("utf8");
|
|
71
|
+
verifyChecksum(asset, archive, checksums);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
extractArchive(archivePath, extractDir, selected.archive);
|
|
75
|
+
|
|
76
|
+
const extractedBinary = path.join(extractDir, selected.binary);
|
|
77
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
78
|
+
throw new Error(`${selected.binary} was not found in ${asset}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const installedBinary = path.join(vendorDir, selected.binary);
|
|
82
|
+
fs.copyFileSync(extractedBinary, installedBinary);
|
|
83
|
+
if (process.platform !== "win32") {
|
|
84
|
+
fs.chmodSync(installedBinary, 0o755);
|
|
85
|
+
}
|
|
86
|
+
console.log(`termarium: installed ${selected.target} binary`);
|
|
87
|
+
} finally {
|
|
88
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function verifyChecksum(asset, archive, checksums) {
|
|
93
|
+
const line = checksums
|
|
94
|
+
.split(/\r?\n/)
|
|
95
|
+
.find((candidate) => candidate.includes(asset));
|
|
96
|
+
if (!line) {
|
|
97
|
+
throw new Error(`checksum for ${asset} was not found`);
|
|
98
|
+
}
|
|
99
|
+
const expected = line.trim().split(/\s+/)[0].toLowerCase();
|
|
100
|
+
const actual = crypto.createHash("sha256").update(archive).digest("hex");
|
|
101
|
+
if (actual !== expected) {
|
|
102
|
+
throw new Error(`checksum mismatch for ${asset}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function extractArchive(archivePath, extractDir, archive) {
|
|
107
|
+
if (archive === "tar.gz") {
|
|
108
|
+
run("tar", ["-xzf", archivePath, "-C", extractDir]);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (archive === "zip") {
|
|
112
|
+
const command = [
|
|
113
|
+
"$ErrorActionPreference = 'Stop'",
|
|
114
|
+
`Expand-Archive -LiteralPath '${escapePowerShell(archivePath)}' -DestinationPath '${escapePowerShell(extractDir)}' -Force`,
|
|
115
|
+
].join("; ");
|
|
116
|
+
run("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command]);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`unsupported archive type ${archive}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function run(command, args) {
|
|
123
|
+
const result = childProcess.spawnSync(command, args, { stdio: "inherit" });
|
|
124
|
+
if (result.error) {
|
|
125
|
+
throw result.error;
|
|
126
|
+
}
|
|
127
|
+
if (result.status !== 0) {
|
|
128
|
+
throw new Error(`${command} exited with status ${result.status}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function download(url, redirects = 0) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
const request = https.get(
|
|
135
|
+
url,
|
|
136
|
+
{
|
|
137
|
+
headers: {
|
|
138
|
+
"User-Agent": "termarium-npm-installer",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
(response) => {
|
|
142
|
+
const status = response.statusCode ?? 0;
|
|
143
|
+
if ([301, 302, 303, 307, 308].includes(status)) {
|
|
144
|
+
response.resume();
|
|
145
|
+
if (!response.headers.location) {
|
|
146
|
+
reject(new Error(`redirect from ${url} did not include a location`));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (redirects >= 5) {
|
|
150
|
+
reject(new Error(`too many redirects while downloading ${url}`));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
resolve(download(new URL(response.headers.location, url).toString(), redirects + 1));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (status < 200 || status >= 300) {
|
|
158
|
+
response.resume();
|
|
159
|
+
reject(new Error(`download failed for ${url}: HTTP ${status}`));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const chunks = [];
|
|
164
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
165
|
+
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
request.on("error", reject);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function escapePowerShell(value) {
|
|
173
|
+
return value.replace(/'/g, "''");
|
|
174
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "termarium",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Termarium 穹顶终端: a quiet terminal planetarium.",
|
|
5
|
+
"license": "MIT AND CC-BY-SA-4.0 AND CC-BY-4.0",
|
|
6
|
+
"homepage": "https://github.com/7b7b7b/termarium#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/7b7b7b/termarium.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/7b7b7b/termarium/issues"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"termarium": "npm/bin/termarium.js"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"postinstall": "node npm/scripts/install.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"npm/bin",
|
|
22
|
+
"npm/scripts",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"NOTICE"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"terminal",
|
|
29
|
+
"tui",
|
|
30
|
+
"astronomy",
|
|
31
|
+
"stars",
|
|
32
|
+
"planetarium"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
40
|
+
}
|