Time overlays on the map
Status: Built (2026-10-31): D1–D13; Q1–Q5 settled in §7. Slices 1–2 in PR #1199, slices 3–4 in the follow-up PR. Follows the Timeline epic #707: the map stamp (#713 B4), the Time HUD (#712, PR #727) and Display Not Real Time (#993). Touches
Views/TimeHudView.axaml,MainWindow.axaml,ViewModels/MainViewModel.cs,ViewModels/TimelineViewModel.csand the readout row ofViews/TimelineView.axaml.
A user with weather wind barbs on the map reported two problems:
- The map shows the same time twice. Top left:
● 10/31/2026 2:00 PM · in 23 min. Bottom centre:PINNED | 10/31/2026 2:00 PM in 23 min | ‹ › | Live | ▭. - The bottom bar changes width when the time is stepped. It's centred, so ‹ and › move under the pointer. A second click on › can land on ‹ or Live and undo the step.
This note explains why each overlay exists, why they now overlap, and why the bar moves. It then proposes numbered decisions an implementation PR can follow.
1. What's there today
1.1 The two overlays
| Map stamp | Time HUD | |
|---|---|---|
| Where | Top left, 12 px in (MainWindow.axaml:648–678) |
Bottom centre, 14 px above the edge (MainWindow.axaml:630–636, Views/TimeHudView.axaml) |
| Added by | #713 B4 (slice 1) | #712 (slice 6, PR #727) |
| Purpose | A read-only caption, so the map (and any capture of it) says which time it shows while the Timeline dock holds the controls | The Timeline's everyday controls while the dock is closed, so nothing larger than a 40 px bar sits over the map |
| Interactive | No (IsHitTestVisible="False") |
Yes: time (focuses the bar for ←/→), ‹ ›, Live, dock toggle; message chip with an action |
| Text | StampText (TimelineViewModel.cs:362–370) |
ModeLabel, CurrentTimeLabel, OffsetText, drawing… (TimeHudView.axaml:118–154) |
| Visible when | IsMapStampVisible => Timeline.IsNotRealTime \|\| (Timeline.IsActive && IsBottomDockShown) (MainViewModel.cs:387) |
IsTimeHudVisible => Timeline.IsActive && !(IsBottomDockShown && IsTimelineTabSelected) (MainViewModel.cs:381) |
As designed in #712/#713, the two were mutually exclusive. The stamp shows
while the dock is open and the HUD is hidden, and the HUD shows while the dock
is closed. Both are inside MapArea, so capture_app_screenshot region
Map (Services/AppScreenshotProvider.cs:60) includes whichever one is shown.
1.2 What each one shows, by state
The time format follows the Local/UTC preference (TimeFormatting.Format,
ActiveFormat).
| State | Map stamp | Time HUD bar |
|---|---|---|
| Live, dock closed | hidden | [● LIVE] 10/31/2026 2:00 PM now │ ‹ › │ ▭ |
| Pinned future, dock closed | hidden | [PINNED] 10/31/2026 2:00 PM in 23 min │ ‹ › [Live] │ ▭ |
| Pinned past, dock closed | hidden | [PINNED] 10/31/2026 8:00 AM 5 h 37 ago │ ‹ › [Live] │ ▭ |
| Drawing a new time | grey dot, 10/31 1:00 PM · drawing 2:00 PM… |
adds drawing… before the separator |
| Dock open, Timeline tab | ● LIVE · … or ● … · in 23 min |
hidden |
| Dock open, Logs or Clearance tab | shown | shown (the dock toggle then switches the dock to Timeline, MainViewModel.cs:1500–1508) |
Pinned with S-124 / dated S-101 data (IsNotRealTime) |
always shown: Display Not Real Time · … · in 23 min |
shown too while the Timeline tab isn't visible |
| Window < 900 px | dock hidden (DockMinimumWindowWidth, MainViewModel.cs:344) |
HUD is the only control; the dock toggle stays but is disabled |
| Map < 640 px / < 540 px | — | drops the offset, then the PINNED pill (TimeHudView.axaml.cs:17–20, .axaml:76–82) |
The bottom dock's own readout row (Views/TimelineView.axaml:283–412) has a
mode pill, the time and the offset on the left. On the right it has ⏮ ‹ [step
menu] › ⏭ [Go live] [window preset].
2. Problems
2.1 Why both overlays show (complaint 1)
The overlap is a regression, not a design choice. The two rules drifted apart:
- #922 (Clearance tab, 2026-10-12) gave the bottom dock more than one tab.
It changed the HUD rule to "hidden only while the dock shows the Timeline
tab" (
ed74cfb1,MainViewModel.cs:381). That's right: on the Clearance or Logs tab the Timeline controls are gone, so the HUD comes back. But the stamp rule still says "shown while the dock is shown", whatever the tab. With the Logs tab open (the new Logs panel, #1080, makes this common), the map gets both. - #993 (2026-10-18) made the stamp stay visible "always while the display isn't real time" (S-98 §12.7.2). With the dock closed, the HUD is also shown, so a pinned map with navigational warnings has both, and the stamp is the only one that says Display Not Real Time.
The screenshot shows a plain stamp (no Display Not Real Time) over weather data, which isn't date-dependent. So it's the first case: the dock was open on a non-Timeline tab.
2.2 Why the bar moves under the pointer (complaint 2)
The bar is a horizontal StackPanel in a Panel with
HorizontalAlignment="Center" (TimeHudView.axaml:85, 114, 116). Its width
is the sum of its children, and several of them change size or appear when
the time changes. Any width change ΔW moves the left edge by ΔW/2. A
control at the left also moves by however much the content before it grew.
Children that change on a step (rough widths at 12–13 px):
| Child | Live → Pinned (› pressed) | Changes while stepping |
|---|---|---|
| Mode pill | ● LIVE (dark, dot, ~54 px) → PINNED (outlined, no dot, ~59 px) |
— |
| Time (mono) | same | 2:00 PM → 12:00 PM adds a character (~8 px) |
| Offset | now (~22 px) → in 1 h / in 23 min (~40–55 px) |
in 1 h → in 11 h 30 → in 2 d 4 h |
drawing… |
appears after every step while the map draws (~50 px), then disappears | yes, twice per step |
| Live button | appears right of › (~52 px with spacing) | — |
Live → Pinned grows the content left of ‹ by about 35 px and the content
right of › by about 52 px. ‹ › move left by roughly (35 − 87/2) ≈ 8 px, and a
filled Live button now starts 6 px right of ›. Then drawing… appears
left of ‹ and pushes ‹ › right by about 25 px until the frame lands, then
they jump back. A user who clicks › repeatedly is clicking a moving target,
and the targets next to it, ‹ and Live, both undo the step. The
centring, the content-sized text and the controls appearing between the
pointer and the edge all add to the problem.
The dock has the same bug. In TimelineView.axaml:334–411 the step group
is a right-aligned StackPanel. Go live appears inside it, right of ⏭,
when the time is pinned, so ⏮ ‹ [step] › ⏭ jump about 80 px left on the first
step. The step menu's label (StepLabel) also changes width with the step
kind, which moves › and ⏭.
2.3 Other findings
| # | Finding | Where |
|---|---|---|
| P1 | One state, three ways. In Live the HUD reads ● LIVE, the time, then now; the stamp adds a fourth. Pinned reads PINNED, the offset and a Live button. The words carry the mode; the pill is redundant with them. |
HUD bar |
| P2 | "Live" means two things. The dark LIVE pill is a status. The Live primary button is an action. The dock calls the same action Go live (TimelinePanel_GoLive). Next to PINNED, a button reading Live looks like a second status. |
TimeHud_Live vs TimelinePanel_GoLive |
| P3 | "PINNED" is jargon and suggests a map pin (we already have route waypoints and pick markers). It's all caps, which reads as an alarm, but being pinned is a normal choice. | TimelinePanel_Pinned |
| P4 | Hierarchy. The pill, the 13 px semibold mono time and the accent-coloured offset all compete. In the pinned state the filled primary Live button is the loudest thing on the map, louder than the time. | HUD bar |
| P5 | Not theme-aware. The live pill is hard-coded #18181B with white text (dark-on-dark in the dark theme). The chip tints #D1FFFBEB/#D1FEF2F2 and the text #B45309/#B91C1C are light-theme only. The dock readout repeats the #18181B pill. |
TimeHudView.axaml:16–39, 50–55; TimelineView.axaml:20–26 |
| P6 | The dock toggle is cryptic. PanelBottom is a generic layout glyph with no label. In a narrow window it stays visible but disabled (CanExecute => !IsWindowNarrow), a dead button with no explanation. When the dock is open on another tab it switches tab, but the tooltip still says "Open timeline (T)". |
TimeHudView.axaml:181–189; MainViewModel.cs:1500–1509 |
| P7 | Discoverability of the step. ‹ › step "the next time sample", but the HUD doesn't say how far (hour, sample of which layer). The dock has a step menu; the HUD has nothing, so you only find out by stepping. | Tooltip_TimelineNextStep |
| P8 | Keyboard. ←/→/N/T work while the map, the Timeline or the HUD has focus (MainWindow.axaml.cs:1662–1692). But the only hint is the time's tooltip ("click, then ← → to step"), and a pointer user has no reason to click the time. Tab order inside the HUD is fine. |
Tooltip_TimeHudTime |
| P9 | Accessibility. The bar has no group name. The mode is only in a pill with no automation name (the live pill has no AutomationId either). The offset isn't part of any accessible name, so a screen reader hears the time but not whether it's live. |
TimeHudView.axaml:118–150 |
| P10 | Display Not Real Time lives only in the stamp. While the HUD is the time surface, the S-98 indication is in a different corner from the control that caused it (the pin). | §2.1 |
3. Decisions
One time surface at a time
D1. Exactly one time overlay on the map. The rule depends on whether the Timeline tab is on screen:
| Timeline tab on screen? | Map shows |
|---|---|
| No: dock closed, window < 900 px, or dock on Logs / Clearance | Time HUD (controls) |
| Yes | Map stamp (caption) |
In code: IsMapStampVisible => Timeline.IsActive && IsBottomDockShown && IsTimelineTabSelected, which is the exact complement of IsTimeHudVisible
while the timeline is active. That fixes complaint 1 and restores the
#712/#713 intent.
D2. Display Not Real Time follows the surface, not the stamp. When
IsNotRealTime is true, whichever surface is shown says so:
- Stamp: unchanged (
Display Not Real Time · time · offset). - HUD: the status slot (D5) reads
NOT REAL TIMEin the warning tone, instead of the plain pinned dot. The full phrase Display Not Real Time is the slot's tooltip and accessible name.
The HUD is in the Map capture region, so captures still carry it.
IsMapStampVisible no longer ORs in IsNotRealTime.
D3. The two surfaces read the same, in the same order. Each one is a
status dot, the time, then the offset. They differ only in role: the stamp
is a caption (no controls, top left, small), the HUD is the control (bottom
centre). The stamp drops the LIVE · prefix and uses the green dot plus a
muted now, exactly as the HUD does (D5). Then the user reads one format
everywhere, including the dock's readout.
A bar whose controls never move
D4. The HUD has a fixed geometry. The bar is a Grid with fixed column
widths, not a content-sized StackPanel. Its total width therefore doesn't
change with the mode, the time, the offset or drawing. Centring is harmless
because there's nothing to re-centre. The width may change only when:
- the map crosses a width class (
compact< 640,tiny< 540, D10), - the time format or culture changes (Local ↔ UTC, a 12/24-hour locale), or
- the dock toggle's availability changes (window crosses 900 px, D9).
None of these can happen during a click on ‹ or ›. As a belt and braces, the controls group is on the right of the bar and the variable text on the left, so even a future slot that misbehaves can't push the buttons from the pointer side.
Slots, left to right (regular class):
| Slot | Width | Content |
|---|---|---|
| Status | 14 px | dot: green (Live), accent (pinned), grey pulsing (drawing), warning (not real time) |
| Time | measured, see D6 | CurrentTimeLabel, mono, left-aligned |
| Offset | 84 px, left-aligned, ellipsis, full text in tooltip | now (muted) · in 23 min · 5 h 37 ago · in 2 d 4 h |
| Separator | 9 px | — |
| ‹ | 28 px | previous step |
| › | 28 px | next step |
| Live | 64 px | toggle, D5 |
| Separator | 9 px | — |
| Dock | 28 px | D9 |
D5. Live becomes one fixed-size toggle; the mode pill goes. A single
ToggleButton in a fixed 64 px slot, always present, replaces both the
● LIVE / PINNED pill and the Live button that appears and disappears:
- Live: checked, quiet fill, green dot and
Live. Clicking it again does nothing (it's already live). The tooltip says "Showing now and keeping up with it." - Pinned: unchecked, outlined,
Go live(the dock's word, see P2). The tooltip is the currentTooltip_TimelineNow.
The left status dot and the muted now carry Live at a glance. The pinned
state needs no word: the accent dot, the offset (in 23 min) and an
unchecked Go live say it. That drops PINNED from the HUD (P1, P3).
The button is never the primary (filled accent) style, so it no longer
outshouts the time (P4).
D6. Stable text slots. The time slot's width is measured once per
format from a template string with the widest digits and meridiem
(12/28/2026 12:00 PM, or 28.12.2026 23:00, or 2026-12-28 23:00Z),
and not from the live text. It's recomputed on TimeFormatChanged. The
time keeps the mono face. The offset uses MonoFontFamily too (or
FontFeatures="+tnum" if the UI font supports tabular figures), so
in 1 h → in 11 h doesn't jitter. It's left-aligned so its start never
moves.
D7. drawing… leaves the row. While the map draws, the status dot goes
grey and pulses, and the time button's tooltip reads drawing 2:00 PM…. No
text is inserted into the bar. If drawing takes longer than ~1 s, the
existing message chip above the bar (F4, which grows upward and doesn't move
the bar) shows Drawing 2:00 PM… · Cancel, using the existing
CancelDrawingCommand.
D8. Step buttons keep their place at the ends of the range. At the first or last step ‹ / › are disabled, not hidden. (They aren't hidden today either; this pins it down as a rule.)
The dock toggle
D9. Say what the toggle does, and hide it when it can't. Keep the
PanelBottom icon, but its tooltip and accessible name follow the state:
Open timeline (T) with the dock closed, Show timeline (T) with the
dock open on another tab. Below 900 px, where the dock can't show, hide it
(the width change happens on a window resize, never under a click).
Narrow maps
D10. Narrow classes swap whole slots. compact (< 640 px) drops the
offset slot. tiny (< 540 px) also shortens Go live to an icon-only
toggle (28 px, Live glyph, same tooltip). Each class has fixed widths, so
D4 holds within a class. There's no pinned label to drop any more (D5).
Theme, keyboard, accessibility
D11. Theme brushes only. The surface stays MapOverlaySurfaceBrush. The
dot and toggle use SuccessColor, AccentBrush, MutedColor, and the
warning/error tokens the rest of the viewer uses. Delete the hard-coded
#18181B, #D1FFFBEB, #D1FEF2F2, #B45309 and #B91C1C (HUD and dock
pill) in favour of theme resources, with dark variants defined next to
MapOverlaySurfaceBrush in App.axaml.
D12. Name the parts. The bar gets AutomationProperties.Name "Time".
The time button's name becomes the full reading, e.g. "2:00 PM, in 23
minutes, pinned" or "2:00 PM, live". The Live toggle exposes its toggle
state. The status slot gets AutomationId="TimeHud.Status" (and the old
TimeHud.PinnedPill id is retired; the preview packages allow clean
breaks). The tooltips on ‹ › add the step and its key: "Next hour (→)",
"Next sample of cbofs (→)", from the same StepLabel the dock uses (P7,
P8).
The dock readout
D13. The dock's step group gets the same rule. In
TimelineView.axaml, Go live becomes the same always-present toggle in
a fixed slot, and the step menu button gets a MinWidth that fits its
longest label (measured as in D6). Then ⏮ ‹ [step] › ⏭ stay put across
Live/Pinned. The dock's mode pill follows D5 (dot plus offset, no PINNED
word) so the dock, the HUD and the stamp read alike.
4. Mock-ups
The HUD, regular width. │ is a separator, [ ] a button; column positions
are the same in every row.
Live
╭──────────────────────────────────────────────────────────────────────────╮
│ ● 10/31/2026 2:00 PM now │ ‹ › [● Live ] │ ▭ │
╰──────────────────────────────────────────────────────────────────────────╯
Pinned, future (after pressing ›)
╭──────────────────────────────────────────────────────────────────────────╮
│ ● 10/31/2026 3:00 PM in 1 h │ ‹ › [Go live] │ ▭ │
╰──────────────────────────────────────────────────────────────────────────╯
Pinned, past, while drawing (grey pulsing dot, nothing inserted)
╭──────────────────────────────────────────────────────────────────────────╮
│ ◌ 10/31/2026 8:00 AM 5 h 37 ago │ ‹ › [Go live] │ ▭ │
╰──────────────────────────────────────────────────────────────────────────╯
Pinned with navigational warnings loaded (warning tone)
╭──────────────────────────────────────────────────────────────────────────╮
│ ▲ NOT REAL TIME 10/31 3:00 PM in 1 h│ ‹ › [Go live] │ ▭ │
╰──────────────────────────────────────────────────────────────────────────╯
For the not-real-time row, the time slot shortens to the date-less form so the total width is unchanged. Whether that's acceptable is open question Q2.
The message chip stays above the bar, centred, growing upward:
╭──────────────────────────────────────────╮
│ No data for 2 of 5 layers Next data › │
╰──────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────╮
│ ● 10/31/2026 3:00 PM in 1 h │ ‹ › [Go live] │ ▭ │
╰──────────────────────────────────────────────────────────────────────────╯
The map stamp (dock open on the Timeline tab only), top left:
╭──────────────────────────────╮
│ ● 10/31/2026 2:00 PM · now │ Live
╰──────────────────────────────╯
╭──────────────────────────────────╮
│ ● 10/31/2026 3:00 PM · in 1 h │ Pinned
╰──────────────────────────────────╯
5. Out of scope
- A time picker in the HUD (F6 stays: clicking the time focuses the bar).
- Backdrop blur behind the surfaces (#733).
- Map- or data-area local time. Times are always the user's Local/UTC preference in the machine zone.
- Putting charts and notices on the clock; Live stays the default.
6. Suggested slices
- One overlay at a time — D1, D2 (HUD side: the warning status slot),
D3.
MainViewModelvisibility rules andStampText. Tests inMultiDockActivityTabTestsfor every row of §1.2, especially Logs and Clearance tabs andIsNotRealTimewith the dock closed. - Fixed HUD geometry — D4–D8, D10. Rewrite the bar as a fixed-column
Gridwith the Live toggle; movedrawing…to the dot and the chip. ATimeHudViewTestscase asserts that the screen bounds ofTimeHud.Previous,TimeHud.Nextand the Live toggle are identical across Live → step → drawing → step (in 1 h→in 11 h 30) → Go live, and across 12- and 24-hour cultures within one format. - Dock readout — D13, with the same bounds test on
Timeline.PreviousStep/Timeline.NextStep/Timeline.GoLive. - Toggle, theme and accessibility polish — D9, D11, D12: dynamic dock
tooltip and hiding, theme tokens for the pill and chip tints, automation
names, step-aware tooltips. Update
docs/viewer pages andwhats-new.mdfor the visible changes.
7. Settled questions
Settled 2026-10-31: the owner took the proposals.
- Q1. The word for Pinned. It leaves the map surfaces (D5); the HUD and
stamp carry the mode through the Live / Go live toggle and the offset. The
word Pinned stays where it already is outside the map: the dock's
accessible names, docs, and MCP's
mode: "pinned". No rename. - Q2. Not real time in the HUD. Built (slice 2) as the second,
fixed-height line: Display Not Real Time in full, in the warning tone,
above the controls row, with the status dot in the warning colour. The HUD
is anchored at the bottom, so the line grows the bar upward and never moves
‹ ›. This replaces the
NOT REAL TIMEslot with the date-less time from the mock-up: that version changed the time format on the first step, and the full phrase is the S-98 §12.7.2 wording. - Q3. Clicking Live while live. A no-op, so repeated clicks are safe.
- Q4. Stamp with the Timeline tab shown. Kept: the dock's readout is far
from the map, and captures of the
Mapregion need the time. - Q5. Offset slot width. Fixed 84 px; longer gaps ellipsize with the full text in the tooltip (as in the D4 table). No per-culture measuring.