tower / commit
feat(ui): show or hide every label, from the panel or with L
README.md | 4 +++- index.html | 4 +++- src/style.css | 2 ++ src/ui/app.ts | 25 +++++++++++++++++++++++++ src/views/map.ts | 8 +++++++- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7301248..6c1b31d 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,9 @@ interior — to see its detail card: its name, its blurb, where it is, and what made of it — a range's named summits, the sea running on a lake or sea *now* by the clock, how many trees a forest holds and how far apart, what a desert's landforms came to. **Shift-click two points** on the map to measure a sightline (Esc clears it), and -**F** fits the map. +**F** fits the map. **Show labels** in the View section — or **L**, in any view — hides +or shows every label at once: the map's place names and captions, and the captions over +the exterior and interior. The choice is remembered across reloads. Changes to the world — a new place, a moved summit, a storm in the weather plan — are made in `content/world.json`, not in the browser (D-072). diff --git a/index.html b/index.html index 2e45593..ca32cac 100644 --- a/index.html +++ b/index.html @@ -40,12 +40,14 @@ <details class="sec" id="view-section" open> <summary>View</summary> <label class="row" id="terrain-row"><input id="f-terrain" type="checkbox" checked /> Show terrain</label> + <label class="row"><input id="f-labels" type="checkbox" checked /> Show labels <span class="dim">— L</span></label> <div id="map-options"> <p class="hint">Generated live from the world's features, in your browser. On the exterior it is wrapped onto the hull — an x-ray of the inner surface, not what Tower looks like from outside.</p> <p class="hint">Click a place to select it. <b>Shift-click two points</b> to - measure a sightline between them; Esc clears it. <b>F</b> fits the map.</p> + measure a sightline between them; Esc clears it. <b>F</b> fits the map, + and <b>L</b> shows or hides the labels in any view.</p> </div> <div id="exterior-options" class="hidden"> <label>Hull diff --git a/src/style.css b/src/style.css index 2d45921..324ae74 100644 --- a/src/style.css +++ b/src/style.css @@ -27,6 +27,8 @@ canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block .views button.active { background: #1d2942; color: var(--accent); } #spin { left: auto; right: 12px; bottom: auto; top: 12px; } .labels3d { position: absolute; inset: 0; pointer-events: none; overflow: hidden; } +/* Show labels, off: the 3D views' captions go; the map stops drawing its names (map.ts). */ +#app.no-labels .labels3d { display: none; } .labels3d span { position: absolute; transform: translate(-50%, -50%); white-space: nowrap; font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; diff --git a/src/ui/app.ts b/src/ui/app.ts index 0a44625..c247041 100644 --- a/src/ui/app.ts +++ b/src/ui/app.ts @@ -23,6 +23,8 @@ type ViewName = 'map' | 'exterior' | 'interior' const VIEW_KEY = 'tower.view' /** The exterior's hull reading, remembered across reloads like the view itself (D-061). */ const HULL_KEY = 'tower.exteriorHull' +/** Whether labels are shown, in every view, remembered the same way. */ +const LABELS_KEY = 'tower.labels' const isView = (v: string | null): v is ViewName => v === 'map' || v === 'exterior' || v === 'interior' @@ -560,6 +562,11 @@ export class App { } // F frames the map, but not while flying: it sits beside W A S D. if (e.key === 'f' && this.view === 'map') this.map.fit() + if (e.key === 'l' || e.key === 'L') { + const box = $<HTMLInputElement>('f-labels') + box.checked = !box.checked + box.dispatchEvent(new Event('change')) + } } // ---------------------------------------------------------------------- view @@ -583,6 +590,24 @@ export class App { localStorage.setItem(HULL_KEY, ext.value) } catch { /* not remembering the hull is survivable */ } } + + // One switch for every view's labels: the map's names, and the 3D views' captions. + const labels = $<HTMLInputElement>('f-labels') + try { + if (localStorage.getItem(LABELS_KEY) === 'off') labels.checked = false + } catch { /* unreadable storage just means labels on */ } + const applyLabels = () => { + $('app').classList.toggle('no-labels', !labels.checked) + this.map.showLabels = labels.checked + this.map.draw() + } + labels.onchange = () => { + applyLabels() + try { + localStorage.setItem(LABELS_KEY, labels.checked ? 'on' : 'off') + } catch { /* not remembering it is survivable */ } + } + applyLabels() } // ------------------------------------------------------------------ readouts diff --git a/src/views/map.ts b/src/views/map.ts index 51f9f3c..7a1b9ce 100644 --- a/src/views/map.ts +++ b/src/views/map.ts @@ -40,6 +40,8 @@ export class TowerMap { /** The generated terrain, drawn under everything. Null until the first world is drawn. */ private terrain: CanvasImageSource | null = null showTerrain = true + /** Place names and the caps' captions. The scale bar stays: it is a measure, not a label. */ + showLabels = true private panning: { x: number; y: number; ox: number; oy: number } | null = null private moved = false @@ -327,7 +329,9 @@ export class TowerMap { } } - if (slatCount() === 0) { + if (!this.showLabels) { + // Nothing to caption. + } else if (slatCount() === 0) { // The single strip is centred on the map edge, so label it in the middle // where it can actually be read. ctx.fillText('NO SLATS — ONE UNBROKEN SURFACE', x0 + 6, y0 + h / 2) @@ -386,6 +390,7 @@ export class TowerMap { ctx.lineTo(farX, y0 + h) ctx.stroke() + if (!this.showLabels) return ctx.font = '11px ui-monospace, SFMono-Regular, Menlo, monospace' ctx.fillStyle = 'rgba(255,214,140,0.85)' ctx.fillText('SUN CAP', sunX + 8, y0 + 14) @@ -538,6 +543,7 @@ export class TowerMap { } private label(text: string, x: number, y: number, color: string, centred: boolean): void { + if (!this.showLabels) return const ctx = this.ctx const w = ctx.measureText(text).width const box = { x: centred ? x - w / 2 : x, y: y - 11, w, h: 14 }