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 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
+ }