@mailwoman/map-tui 9.1.0 → 9.2.0

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.
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ import type { TileSource } from "./tile-source.ts";
7
+ /**
8
+ * The subset of a readable stream the browser drives. Structural so `process.stdin` satisfies it without the app being
9
+ * welded to it — the same reasoning asciify applies to its own output type.
10
+ */
11
+ export interface BrowserInput {
12
+ setRawMode?(mode: boolean): unknown;
13
+ setEncoding(encoding: "utf8"): unknown;
14
+ resume(): unknown;
15
+ pause(): unknown;
16
+ on(event: "data", listener: (chunk: string) => void): unknown;
17
+ off(event: "data", listener: (chunk: string) => void): unknown;
18
+ }
19
+ /**
20
+ * The subset of a writable terminal the browser drives.
21
+ */
22
+ export interface BrowserOutput {
23
+ write(chunk: string): unknown;
24
+ columns?: number | undefined;
25
+ rows?: number | undefined;
26
+ on(event: "resize", listener: () => void): unknown;
27
+ off(event: "resize", listener: () => void): unknown;
28
+ }
29
+ export interface MapBrowserOptions {
30
+ source: TileSource;
31
+ input: BrowserInput;
32
+ output: BrowserOutput;
33
+ lat: number;
34
+ lon: number;
35
+ zoom: number;
36
+ }
37
+ export declare class MapBrowser {
38
+ private readonly source;
39
+ private readonly renderer;
40
+ private readonly input;
41
+ private readonly output;
42
+ private readonly terminal;
43
+ private centerLon;
44
+ private centerLat;
45
+ private zoom;
46
+ private columns;
47
+ private paneRows;
48
+ private drag;
49
+ private renderInFlight;
50
+ private renderQueued;
51
+ private started;
52
+ private restored;
53
+ private error;
54
+ private resolveExit;
55
+ /**
56
+ * The trailing bytes of the last chunk that could not be decoded yet — a sequence the kernel split across two reads.
57
+ * Threading it back through `decodeInputChunk` is what keeps a split mouse report from being read as an Esc keypress
58
+ * (which used to quit); the decoder itself stays pure.
59
+ */
60
+ private pendingInput;
61
+ private readonly onData;
62
+ private readonly onResize;
63
+ constructor(options: MapBrowserOptions);
64
+ /**
65
+ * Runs until the user quits, resolving with the process exit code (0 for a normal quit, 130 for Ctrl+C). The terminal
66
+ * is restored before this resolves.
67
+ */
68
+ run(): Promise<number>;
69
+ /**
70
+ * Asks the browser to exit with a code. Safe to call from a signal handler, and a no-op once an exit is already under
71
+ * way.
72
+ */
73
+ requestExit(code: number): void;
74
+ /**
75
+ * Enters the alternate screen and takes over input. Paired with {@link restore}.
76
+ */
77
+ start(): void;
78
+ /**
79
+ * Puts the terminal back exactly as it was found. Idempotent: the normal exit path, a signal handler and a
80
+ * process-level `exit` hook may each call it.
81
+ */
82
+ restore(): void;
83
+ private handleInput;
84
+ private panBySteps;
85
+ /**
86
+ * Moves the center by a cell delta, through world pixels so the step is the same distance on screen at every latitude
87
+ * — the naive degrees-per-keypress version crawls at the equator and sprints near the poles.
88
+ */
89
+ private panByCells;
90
+ private setCenter;
91
+ /**
92
+ * Longitude/latitude at the center of a pane cell.
93
+ */
94
+ private cellToLonLat;
95
+ /**
96
+ * Zooms one or more whole levels. With an anchor cell (the wheel's pointer), the center shifts so whatever was under
97
+ * the pointer stays under it; without one, the pane center holds.
98
+ */
99
+ private zoomBy;
100
+ private withinPane;
101
+ private beginDrag;
102
+ /**
103
+ * Pans relative to where the drag STARTED, not the previous motion report. Accumulating per-report deltas would
104
+ * drift, since each one is rounded to a whole cell.
105
+ */
106
+ private continueDrag;
107
+ /**
108
+ * A press and release with no motion between them is a click, which centers the map — mapscii's behavior, and the
109
+ * reason centering waits for the release rather than acting on the press.
110
+ */
111
+ private endDrag;
112
+ /**
113
+ * Reads the terminal's size and re-sizes the pane around the status bar.
114
+ */
115
+ private measure;
116
+ /**
117
+ * Requests a frame. Renders never overlap: a request arriving mid-render is coalesced into one more pass, so a held
118
+ * arrow key queues a single redraw rather than a backlog of them.
119
+ */
120
+ private scheduleRender;
121
+ private renderLoop;
122
+ private renderOnce;
123
+ private statusText;
124
+ private drawStatusBar;
125
+ }
126
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../browser.ts"],"names":[],"mappings":"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"}
package/out/browser.js ADDED
@@ -0,0 +1,386 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ /**
7
+ * The interactive map browser — a full-screen, alternate-screen terminal app over `MapRenderer`.
8
+ *
9
+ * `MapBrowser` owns exactly three things the frame-first library deliberately does not: terminal MODE (alternate
10
+ * screen, hidden cursor, raw stdin, mouse reporting), viewport STATE (center, zoom, drag anchor), and the write path.
11
+ * Frames still come from `MapRenderer.renderFrame` as values; `blitFrame` copies one into an `AsciifyTerminal`, whose
12
+ * damage diff decides what actually goes down the wire.
13
+ *
14
+ * The pane is the terminal minus its bottom row, which is the status bar. `AsciifyTerminal` is told that size and never
15
+ * addresses a cell outside it, so the two writers never fight over a cell.
16
+ *
17
+ * Every mode change made in {@link MapBrowser.start} is undone by {@link MapBrowser.restore}, which is idempotent so a
18
+ * signal handler, an `exit` hook and the normal path can all call it. A terminal left in raw mode with mouse reporting
19
+ * on is not a recoverable shell, so restore is the one operation that must survive any exit path.
20
+ */
21
+ import { AsciifyTerminal, cursorTo, SGR_RESET } from "@sister.software/asciify/tui";
22
+ import { blitFrame } from "./frame.js";
23
+ import { decodeInputChunk, MOUSE_DISABLE, MOUSE_ENABLE } from "./input.js";
24
+ import { lonLatToWorldPx, worldPxToLonLat } from "./mercator.js";
25
+ import { MapRenderer } from "./renderer.js";
26
+ const ALT_SCREEN_ENTER = "\u001B[?1049h";
27
+ const ALT_SCREEN_EXIT = "\u001B[?1049l";
28
+ const CURSOR_HIDE = "\u001B[?25l";
29
+ const CURSOR_SHOW = "\u001B[?25h";
30
+ const CLEAR_SCREEN = "\u001B[2J";
31
+ const CLEAR_LINE = "\u001B[2K";
32
+ const REVERSE_VIDEO = "\u001B[7m";
33
+ /**
34
+ * Subpixel dimensions of one braille cell — the unit every viewport-space conversion below works in.
35
+ */
36
+ const SUBPIXELS_PER_COLUMN = 2;
37
+ const SUBPIXELS_PER_ROW = 4;
38
+ /**
39
+ * Rows reserved at the bottom of the terminal for the status bar.
40
+ */
41
+ const STATUS_BAR_ROWS = 1;
42
+ /**
43
+ * Size assumed when the output reports none. A pty opened without a window size (util-linux `script`, some CI
44
+ * harnesses) reports 0 columns and 0 rows, which is neither an error nor a usable grid.
45
+ */
46
+ const FALLBACK_COLUMNS = 80;
47
+ const FALLBACK_ROWS = 24;
48
+ /**
49
+ * Floor on the pane's dimensions. Below this the renderer is being asked for a grid too small to mean anything, and a
50
+ * zero-sized one would divide by zero in the projection.
51
+ */
52
+ const MIN_COLUMNS = 4;
53
+ const MIN_PANE_ROWS = 1;
54
+ /**
55
+ * How much of the pane one arrow-key press travels. An eighth is far enough to feel like progress at a keystroke and
56
+ * short enough that the next frame still overlaps the last.
57
+ */
58
+ const PAN_FRACTION = 0.125;
59
+ /**
60
+ * Web-Mercator's latitude cutoff — the projection is undefined at the poles, and the square tile pyramid ends here.
61
+ */
62
+ const MERCATOR_LATITUDE_LIMIT = 85.05112878;
63
+ const COORDINATE_DIGITS = 4;
64
+ function clamp(value, min, max) {
65
+ return Math.min(Math.max(value, min), max);
66
+ }
67
+ /**
68
+ * Wraps a longitude into [-180, 180) so panning past the antimeridian continues rather than running off the pyramid.
69
+ */
70
+ function wrapLongitude(lon) {
71
+ const wrapped = (((lon + 180) % 360) + 360) % 360;
72
+ return wrapped - 180;
73
+ }
74
+ /**
75
+ * Clips a string to a cell budget, counting codepoints — the status bar's arrows are one cell each but two UTF-16
76
+ * units, and `String.prototype.slice` would cut one in half.
77
+ */
78
+ function clipToCells(text, cells) {
79
+ const codePoints = Array.from(text);
80
+ return codePoints.length <= cells ? text : codePoints.slice(0, cells).join("");
81
+ }
82
+ export class MapBrowser {
83
+ source;
84
+ renderer;
85
+ input;
86
+ output;
87
+ terminal;
88
+ centerLon;
89
+ centerLat;
90
+ zoom;
91
+ columns = FALLBACK_COLUMNS;
92
+ paneRows = FALLBACK_ROWS - STATUS_BAR_ROWS;
93
+ drag = null;
94
+ renderInFlight = false;
95
+ renderQueued = false;
96
+ started = false;
97
+ restored = false;
98
+ error = null;
99
+ resolveExit = null;
100
+ /**
101
+ * The trailing bytes of the last chunk that could not be decoded yet — a sequence the kernel split across two reads.
102
+ * Threading it back through `decodeInputChunk` is what keeps a split mouse report from being read as an Esc keypress
103
+ * (which used to quit); the decoder itself stays pure.
104
+ */
105
+ pendingInput = "";
106
+ onData = (chunk) => {
107
+ const decoded = decodeInputChunk(chunk, this.pendingInput);
108
+ this.pendingInput = decoded.pending;
109
+ for (const event of decoded.events) {
110
+ this.handleInput(event);
111
+ }
112
+ };
113
+ onResize = () => {
114
+ this.measure();
115
+ this.output.write(CLEAR_SCREEN);
116
+ this.terminal.invalidate();
117
+ this.scheduleRender();
118
+ };
119
+ constructor(options) {
120
+ this.source = options.source;
121
+ this.renderer = new MapRenderer(options.source);
122
+ this.input = options.input;
123
+ this.output = options.output;
124
+ this.centerLon = wrapLongitude(options.lon);
125
+ this.centerLat = clamp(options.lat, -MERCATOR_LATITUDE_LIMIT, MERCATOR_LATITUDE_LIMIT);
126
+ this.zoom = clamp(Math.round(options.zoom), options.source.minZoom, options.source.maxZoom);
127
+ this.terminal = new AsciifyTerminal(this.output, {
128
+ mode: "braille",
129
+ colorDepth: "truecolor",
130
+ synchronizedOutput: true,
131
+ });
132
+ }
133
+ /**
134
+ * Runs until the user quits, resolving with the process exit code (0 for a normal quit, 130 for Ctrl+C). The terminal
135
+ * is restored before this resolves.
136
+ */
137
+ async run() {
138
+ this.start();
139
+ const code = await new Promise((resolve) => {
140
+ this.resolveExit = resolve;
141
+ });
142
+ this.restore();
143
+ return code;
144
+ }
145
+ /**
146
+ * Asks the browser to exit with a code. Safe to call from a signal handler, and a no-op once an exit is already under
147
+ * way.
148
+ */
149
+ requestExit(code) {
150
+ const resolve = this.resolveExit;
151
+ if (!resolve)
152
+ return;
153
+ this.resolveExit = null;
154
+ resolve(code);
155
+ }
156
+ /**
157
+ * Enters the alternate screen and takes over input. Paired with {@link restore}.
158
+ */
159
+ start() {
160
+ if (this.started)
161
+ return;
162
+ this.started = true;
163
+ this.output.write(ALT_SCREEN_ENTER + CURSOR_HIDE + CLEAR_SCREEN + MOUSE_ENABLE);
164
+ this.input.setRawMode?.(true);
165
+ this.input.setEncoding("utf8");
166
+ this.input.resume();
167
+ this.input.on("data", this.onData);
168
+ this.output.on("resize", this.onResize);
169
+ this.measure();
170
+ this.scheduleRender();
171
+ }
172
+ /**
173
+ * Puts the terminal back exactly as it was found. Idempotent: the normal exit path, a signal handler and a
174
+ * process-level `exit` hook may each call it.
175
+ */
176
+ restore() {
177
+ if (!this.started || this.restored)
178
+ return;
179
+ this.restored = true;
180
+ this.input.off("data", this.onData);
181
+ this.output.off("resize", this.onResize);
182
+ this.input.setRawMode?.(false);
183
+ this.input.pause();
184
+ this.output.write(MOUSE_DISABLE + SGR_RESET + CURSOR_SHOW + ALT_SCREEN_EXIT);
185
+ }
186
+ handleInput(event) {
187
+ switch (event.kind) {
188
+ case "quit":
189
+ return this.requestExit(0);
190
+ case "interrupt":
191
+ return this.requestExit(130);
192
+ case "pan":
193
+ return this.panBySteps(event.dx, event.dy);
194
+ case "zoom":
195
+ return this.zoomBy(event.delta, null);
196
+ case "wheel":
197
+ return this.zoomBy(event.delta, { column: event.column, row: event.row });
198
+ case "press":
199
+ return this.beginDrag(event.column, event.row);
200
+ case "drag":
201
+ return this.continueDrag(event.column, event.row);
202
+ case "release":
203
+ return this.endDrag();
204
+ }
205
+ }
206
+ panBySteps(dx, dy) {
207
+ const columnStep = Math.max(1, Math.round(this.columns * PAN_FRACTION));
208
+ const rowStep = Math.max(1, Math.round(this.paneRows * PAN_FRACTION));
209
+ this.panByCells(dx * columnStep, dy * rowStep);
210
+ this.scheduleRender();
211
+ }
212
+ /**
213
+ * Moves the center by a cell delta, through world pixels so the step is the same distance on screen at every latitude
214
+ * — the naive degrees-per-keypress version crawls at the equator and sprints near the poles.
215
+ */
216
+ panByCells(columns, rows) {
217
+ const center = lonLatToWorldPx(this.centerLon, this.centerLat, this.zoom);
218
+ const next = worldPxToLonLat(center.x + columns * SUBPIXELS_PER_COLUMN, center.y + rows * SUBPIXELS_PER_ROW, this.zoom);
219
+ this.setCenter(next.lon, next.lat);
220
+ }
221
+ setCenter(lon, lat) {
222
+ this.centerLon = wrapLongitude(lon);
223
+ this.centerLat = clamp(lat, -MERCATOR_LATITUDE_LIMIT, MERCATOR_LATITUDE_LIMIT);
224
+ }
225
+ /**
226
+ * Longitude/latitude at the center of a pane cell.
227
+ */
228
+ cellToLonLat(column, row) {
229
+ const center = lonLatToWorldPx(this.centerLon, this.centerLat, this.zoom);
230
+ const originX = center.x - (this.columns * SUBPIXELS_PER_COLUMN) / 2;
231
+ const originY = center.y - (this.paneRows * SUBPIXELS_PER_ROW) / 2;
232
+ return worldPxToLonLat(originX + column * SUBPIXELS_PER_COLUMN + SUBPIXELS_PER_COLUMN / 2, originY + row * SUBPIXELS_PER_ROW + SUBPIXELS_PER_ROW / 2, this.zoom);
233
+ }
234
+ /**
235
+ * Zooms one or more whole levels. With an anchor cell (the wheel's pointer), the center shifts so whatever was under
236
+ * the pointer stays under it; without one, the pane center holds.
237
+ */
238
+ zoomBy(delta, anchor) {
239
+ const next = clamp(this.zoom + delta, this.source.minZoom, this.source.maxZoom);
240
+ if (next === this.zoom)
241
+ return;
242
+ if (!anchor || !this.withinPane(anchor.column, anchor.row)) {
243
+ this.zoom = next;
244
+ this.scheduleRender();
245
+ return;
246
+ }
247
+ const target = this.cellToLonLat(anchor.column, anchor.row);
248
+ this.zoom = next;
249
+ const landed = this.cellToLonLat(anchor.column, anchor.row);
250
+ const center = lonLatToWorldPx(this.centerLon, this.centerLat, this.zoom);
251
+ const targetPx = lonLatToWorldPx(target.lon, target.lat, this.zoom);
252
+ const landedPx = lonLatToWorldPx(landed.lon, landed.lat, this.zoom);
253
+ const corrected = worldPxToLonLat(center.x + (targetPx.x - landedPx.x), center.y + (targetPx.y - landedPx.y), this.zoom);
254
+ this.setCenter(corrected.lon, corrected.lat);
255
+ this.scheduleRender();
256
+ }
257
+ withinPane(column, row) {
258
+ return column >= 0 && column < this.columns && row >= 0 && row < this.paneRows;
259
+ }
260
+ beginDrag(column, row) {
261
+ if (!this.withinPane(column, row))
262
+ return;
263
+ this.drag = {
264
+ column,
265
+ row,
266
+ centerLon: this.centerLon,
267
+ centerLat: this.centerLat,
268
+ zoom: this.zoom,
269
+ moved: false,
270
+ };
271
+ }
272
+ /**
273
+ * Pans relative to where the drag STARTED, not the previous motion report. Accumulating per-report deltas would
274
+ * drift, since each one is rounded to a whole cell.
275
+ */
276
+ continueDrag(column, row) {
277
+ const anchor = this.drag;
278
+ if (!anchor || anchor.zoom !== this.zoom)
279
+ return;
280
+ anchor.moved = true;
281
+ const center = lonLatToWorldPx(anchor.centerLon, anchor.centerLat, anchor.zoom);
282
+ const next = worldPxToLonLat(center.x + (anchor.column - column) * SUBPIXELS_PER_COLUMN, center.y + (anchor.row - row) * SUBPIXELS_PER_ROW, anchor.zoom);
283
+ this.setCenter(next.lon, next.lat);
284
+ this.scheduleRender();
285
+ }
286
+ /**
287
+ * A press and release with no motion between them is a click, which centers the map — mapscii's behavior, and the
288
+ * reason centering waits for the release rather than acting on the press.
289
+ */
290
+ endDrag() {
291
+ const anchor = this.drag;
292
+ this.drag = null;
293
+ if (!anchor || anchor.moved)
294
+ return;
295
+ // A wheel event between press and release moved the map out from under the click, so the cell no longer names
296
+ // the place the user pointed at.
297
+ if (anchor.zoom !== this.zoom)
298
+ return;
299
+ const target = this.cellToLonLat(anchor.column, anchor.row);
300
+ this.setCenter(target.lon, target.lat);
301
+ this.scheduleRender();
302
+ }
303
+ /**
304
+ * Reads the terminal's size and re-sizes the pane around the status bar.
305
+ */
306
+ measure() {
307
+ // `||` and not `??`: a pty with no window size reports 0, which is as unusable as absent.
308
+ const columns = Math.floor(this.output.columns || FALLBACK_COLUMNS);
309
+ const rows = Math.floor(this.output.rows || FALLBACK_ROWS);
310
+ this.columns = Math.max(MIN_COLUMNS, columns);
311
+ this.paneRows = Math.max(MIN_PANE_ROWS, rows - STATUS_BAR_ROWS);
312
+ this.terminal.setSize(this.columns, this.paneRows);
313
+ }
314
+ /**
315
+ * Requests a frame. Renders never overlap: a request arriving mid-render is coalesced into one more pass, so a held
316
+ * arrow key queues a single redraw rather than a backlog of them.
317
+ */
318
+ scheduleRender() {
319
+ if (this.renderInFlight) {
320
+ this.renderQueued = true;
321
+ return;
322
+ }
323
+ void this.renderLoop();
324
+ }
325
+ async renderLoop() {
326
+ this.renderInFlight = true;
327
+ try {
328
+ do {
329
+ this.renderQueued = false;
330
+ await this.renderOnce();
331
+ } while (this.renderQueued && !this.restored);
332
+ }
333
+ finally {
334
+ this.renderInFlight = false;
335
+ }
336
+ }
337
+ async renderOnce() {
338
+ const viewport = {
339
+ centerLon: this.centerLon,
340
+ centerLat: this.centerLat,
341
+ zoom: this.zoom,
342
+ columns: this.columns,
343
+ rows: this.paneRows,
344
+ };
345
+ try {
346
+ const frame = await this.renderer.renderFrame(viewport);
347
+ // The terminal may have been restored while the tiles were in flight; writing then would paint over the
348
+ // user's shell.
349
+ if (this.restored)
350
+ return;
351
+ // A resize between the request and now leaves the frame the wrong shape for the pane — drop it and let the
352
+ // resize's own render answer instead.
353
+ if (frame.columns !== this.columns || frame.rows !== this.paneRows)
354
+ return;
355
+ this.error = null;
356
+ blitFrame(this.terminal, frame);
357
+ this.terminal.flush();
358
+ this.drawStatusBar(frame.attribution);
359
+ }
360
+ catch (error) {
361
+ this.error = error instanceof Error ? error.message : String(error);
362
+ if (!this.restored) {
363
+ this.drawStatusBar("");
364
+ }
365
+ }
366
+ }
367
+ statusText(attribution) {
368
+ if (this.error)
369
+ return `⚠ ${this.error} q quit`;
370
+ const lat = this.centerLat.toFixed(COORDINATE_DIGITS);
371
+ const lon = this.centerLon.toFixed(COORDINATE_DIGITS);
372
+ const status = `${lat},${lon} z${this.zoom} ←↑↓→ pan +/- zoom q quit`;
373
+ if (!attribution.length)
374
+ return status;
375
+ const credited = `${status} © ${attribution}`;
376
+ // Attribution is a courtesy to the tile source, never a reason to push the controls off the bar.
377
+ return Array.from(credited).length < this.columns ? credited : status;
378
+ }
379
+ drawStatusBar(attribution) {
380
+ // One cell short of the width: writing the bottom-right cell leaves some terminals in a pending-wrap state.
381
+ const width = Math.max(0, this.columns - 1);
382
+ const line = clipToCells(this.statusText(attribution), width).padEnd(width);
383
+ this.output.write(`${cursorTo(0, this.paneRows)}${CLEAR_LINE}${REVERSE_VIDEO}${line}${SGR_RESET}`);
384
+ }
385
+ }
386
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ /**
7
+ * A view request: an archive to read and where to open it.
8
+ */
9
+ export interface BrowseArgs {
10
+ mode: "browse";
11
+ /**
12
+ * Path to the PMTiles archive.
13
+ */
14
+ tiles: string;
15
+ lat: number;
16
+ lon: number;
17
+ /**
18
+ * Integer zoom level. The renderer draws whole tile-pyramid levels, so a fractional flag value is rounded here rather
19
+ * than carried as a lie through the viewport.
20
+ */
21
+ zoom: number;
22
+ }
23
+ export type CLIArgs = {
24
+ mode: "help";
25
+ } | {
26
+ mode: "version";
27
+ } | BrowseArgs;
28
+ /**
29
+ * A rejected command line. The message is user-facing: it says what was wrong AND what to pass instead, since the bin
30
+ * prints it verbatim to stderr.
31
+ */
32
+ export declare class CLIArgsError extends Error {
33
+ name: string;
34
+ }
35
+ /**
36
+ * Environment keys the CLI reads. Passed in rather than read here so the parser stays pure.
37
+ */
38
+ export interface CLIEnvironment {
39
+ MAILWOMAN_TILES?: string | undefined;
40
+ }
41
+ /**
42
+ * `--help` output. It doubles as the package's key reference, so the bindings listed here and the ones ./input.ts
43
+ * decodes are the same list said twice — a key added there without a line here is a key nobody finds.
44
+ */
45
+ export declare const HELP_TEXT = "map-tui \u2014 the whole world in your terminal\n\nUsage\n npx @mailwoman/map-tui --tiles <archive.pmtiles> [options]\n\nOptions\n --tiles <path|url> PMTiles archive \u2014 local path or https:// URL (default: $MAILWOMAN_TILES)\n --lat <degrees> Initial center latitude, -90..90 (default: 0)\n --lon <degrees> Initial center longitude, -180..180 (default: 0)\n --zoom <level> Initial zoom, 0..24 (default: 2)\n -h, --help Print this help and exit\n -v, --version Print the package version and exit\n\nKeys\n arrows, hjkl Pan\n +, =, a Zoom in\n -, _, z Zoom out\n q, Esc, Ctrl+C Quit\n\nMouse\n Wheel zooms toward the pointer, drag pans, click centers.\n\nTiles are never bundled with this package. Download a planet or region\narchive from https://protomaps.com/downloads and point --tiles at it.\n";
46
+ /**
47
+ * Parses a `map-tui` command line.
48
+ *
49
+ * @throws {CLIArgsError} On an unknown flag, an unparseable or out-of-range number, or a missing archive path.
50
+ */
51
+ export declare function parseCLIArgs(argv: readonly string[], environment?: CLIEnvironment): CLIArgs;
52
+ //# sourceMappingURL=cli-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../cli-args.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4CH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,QAAQ,CAAA;IACd;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG,UAAU,CAAA;AAEzE;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAC7B,IAAI,SAAiB;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACpC;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,+1BAwBrB,CAAA;AA4FD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,GAAE,cAAmB,GAAG,OAAO,CAuB/F"}