github jchultarsky/mirador v1.20.0
1.20.0 - 2026-10-07

3 hours ago

Release Notes

Changed

  • A setting given a word it does not take now stops startup and says what
    to write.
    [todo].sort, [notes].preview and [calendar].week_starts
    each take one of a few words, and a misspelt one — sort = "dues",
    preview = "besides", or week_starts = "Monday " with its trailing
    space — used to start the dashboard on the default without a word, so the
    setting looked broken. mirador now refuses the config, names the key and
    lists the words it takes, as it already did for the two units keys. Case
    still does not matter for these three, so every value a panel actually
    read still loads; only a word it was ignoring is refused.
  • The test suite no longer reaches the network or your data directory. It
    is documented as never touching the network, and that had stopped being true
    a second time: the weather and news panels start a fetch thread as they are
    built, and two tests built every widget, so each run asked Open-Meteo for a
    forecast and read the three shipped news feeds. Those tests, and almost
    every one that built a dashboard, also found the task list, notes,
    watchlist and world clocks in mirador's own data directory — reading your
    tasks, and on a machine that had none of the others yet, writing them there
    from the default config, after which your [clocks].zones and
    [stocks].symbols would never have been read, since a list seeds only
    while its file is missing. Under cfg(test) the three panels with a fetch
    thread are refused rather than built, the tests build them offline, and the
    data directory is one of the test run's own. No effect on the shipped
    binary.
  • A charging or full battery stays brass. The battery panel painted its
    label, its cell and its figure green whenever the laptop was charging or
    full, where the README and the panel's own notes said brass while there is
    plenty, the signal colours only for a charge that is low while the machine
    runs on it, and charging told by the label. The face now does what they
    say: CHARGING and FULL name the state, in brass, so plugging in no
    longer turns the panel green. Amber and red are unchanged.

Fixed

  • A symlinked config or data file stays a symlink. A save writes a new
    file and renames it into place, and a rename replaces whatever has the
    name — so a config.toml linked into a dotfiles repository, or a task list
    linked into a synced folder, was turned into a plain file by the first
    change, and the copy the link pointed at silently stopped receiving
    anything after it. Saves now go to the file at the end of the link, and a
    link to a file that does not exist yet creates that file — but not the
    folder it would be in, which is reported instead, so a link into a sync
    folder not yet set up never gets a default file put where the real one is
    about to arrive. A loop of links is refused with an error rather than
    replaced. A config linked into a read-only place, as Nix home-manager does,
    is no longer replaced by a plain file either, so a layout change there now
    reports that it could not be saved, reason first. --reset-config moves a
    linked config aside as the link itself and writes the defaults to a new
    file, so the file the link points at is left as it was. Both resets set a
    link aside even when its file is missing, and a backup that is such a link
    is never taken for a free name, so a later reset does not write through it.
  • A failed save no longer leaves a temporary file behind. Each save is
    written to a temporary under a name of its own first, and when the write or
    the rename failed it stayed there. A failed save is tried again at the next
    change, so a full disk, or a task list held open by another program on
    Windows, gathered one more .tmp file beside your data for every attempt.
  • A file you restricted is never written anywhere wider. The mode of a
    chmod 600 task list was copied onto the replacement only after the whole
    list had been written into it and flushed, so for that moment it sat in a
    file the umask had made readable by every account on the machine. The
    replacement is now created with the original's mode before anything is
    written into it.
  • [todo].horizon_days hides what it says it hides. It has been in every
    config since the first release, described as hiding tasks due further out
    than that many days, and nothing read it: a list set to a week still showed
    tasks due next year. A task due more than that many days from today is now
    hidden, and comes back on the day it comes within range. A task with no due
    date, or one already late, always shows, and 0 still shows everything.
    Saving a task past the horizon says so, the summary line says how many
    tasks are further out, and a list the horizon has emptied says so rather
    than "No tasks yet". A filter searches past the horizon, so a task given a
    due date further out than meant can still be found, edited and deleted.
  • [cpu].warn_pct and critical_pct are documented as not read. The
    shipped config set both, promising a readout that turned amber and red at
    those figures, and nothing ever did: the readout takes its colour from the
    graph's ramp, so it warms with the load instead. A config that sets them
    still loads. A new config leaves them out.
  • A paste of several lines into a one-line form adds one task and nothing
    else.
    A newline in a paste is Enter, and Enter saves the task, after
    which the rest of the paste reached the task list as commands: pasting
    Buy milk and and eggs added "Buy milk", then a opened a new task and
    "nd eggs" became a second one, and a line beginning with a space or dy
    would have marked a task done or deleted it. The paste now stops where the
    form closes. The stocks panel's symbol prompt had the same fault.
  • A paste no longer answers a delete confirmation. With a task, a note
    or a symbol waiting on y to be deleted, a paste was typed in as keys,
    so one beginning with y deleted it. The question now ignores a paste and
    waits for a key.
  • Browsing themes no longer remembers them. Every arrow key in the t
    picker wrote the theme under the cursor to state.toml, so a dashboard
    closed mid-browse opened next time in a theme nobody chose. Only Enter
    records one now.
  • Esc in arrange mode undoes a resize made in it. A Ctrl+arrow resize
    inside the mode was written to the config three quarters of a second
    later, so Esc put the widths back on screen and the next launch brought
    the resize back. Resizes made in the mode are now kept or discarded with
    the rest of the arrangement.
  • Refusing to switch off the last panel no longer says the layout could
    not be saved.
    The refusal was reported as a failed save on the status
    bar, where it stayed after the picker closed until some later layout
    change succeeded. It is said in the picker, and goes when the picker
    does.
  • A layout change that cannot be saved as mirador quits is reported.
    A resize made just before q is written on the way out, and when that
    write failed nothing said so. mirador now prints why once the terminal is
    restored.
  • Clicks and the mouse wheel stay out of panels behind an open dialog.
    With the panel picker, the theme picker or the key map open, a click
    moved focus to the panel beneath it and selected a row nobody could see,
    and the wheel scrolled lists behind it. The dialogs take the mouse the
    way they take the keyboard.
  • "Every weekday" no longer meets at weekends. Thunderbird writes it as a
    daily rule naming Monday to Friday, and the agenda read the weekdays only
    on weekly rules, so it showed the meeting on Saturday and Sunday too. A
    daily rule now falls only on the days it names. A daily or weekly rule
    naming weekdays also shows the day it was set up, which the standard
    counts as the first occurrence whatever the rule names. A weekly rule that
    skips weeks groups its days by the week start it gives, so a fortnightly
    Sunday-and-Monday meeting from a calendar whose weeks start on Sunday
    lands on the right pair. A monthly or yearly rule
    naming weekdays, which means every such weekday of the month or year, is
    outside what the agenda reads and shows only its first occurrence, rather
    than one on the same date each month whatever day that was.
  • A repeating event's last day is no longer lost west of Greenwich. An
    end date written without a time, as Google writes the end of an all-day
    series, or a time without a zone, was read as UTC, so in the Americas the
    series ended the evening before its last day. Both are now read on the
    event's own clock and include that day, wherever you are. A broken time in
    the end date, which falls back to the date it carries, now keeps that day
    too.
  • A weekly event on several days keeps the early ones at the end of the
    agenda.
    A rule meeting on Mondays and Wednesdays that began on a
    Wednesday dropped the Monday on the agenda's last day.
  • A daily event set up more than about eleven years ago appears again,
    if it repeats without a set number of occurrences. Expanding a rule walked
    from its first occurrence and gave up after four thousand steps, so an old
    daily reminder never reached today and vanished without a word. Such a rule
    now starts just short of the days on show. One with a count still walks
    from the start, since it has to count, and four thousand steps remains its
    reach.
  • A repeating all-day event ends at midnight on the day the clocks
    change.
    Its length was measured in hours, so the occurrence on a 23-hour
    day ran to 01:00 the next morning, still in progress, and the one on a
    25-hour day ended at 23:00.
  • One enormous event title no longer slows the dashboard. A calendar's
    title and location are drawn on every frame, and line folding let either
    run to the whole 10MB the agenda reads; both are now kept to 400
    characters, far more than the panel shows.
  • The watch log no longer reports tomorrow's meetings as new every
    midnight.
    The agenda reads a window of days starting today, so each
    midnight brings in a day the previous read never looked at, and every
    event on it, including the next instance of every repeating meeting, was
    logged as having "appeared in your calendar". Only an event inside the
    previous window is reported now: one past its end was out of view, not
    missing.
  • The agenda's o shows the whole path. The calendar's path was drawn
    as one line and the terminal cut it at the panel's edge, so the default
    macOS path lost its filename at every width the panel takes, with no …
    to say so. It wraps now, and a panel too short for all of it ends the
    last row it has in ….
  • The meeting under way keeps its location. The ▸ beside it was
    counted as four cells, its size in bytes, rather than two, so that row
    cut its summary two cells early and dropped a location the rows around
    it kept.
  • The world clocks write zones.toml on the first run. The list from
    [clocks].zones was meant to be saved on the first run, but it was only
    saved after a clock was changed from the panel. Until then the file did
    not exist, o named a missing file, and the config was read again at
    every launch, though the shipped config says it is read once. After the
    first change, edits to the config stopped working with nothing to say
    why. The list is now saved when the panel first starts, as the stock
    watchlist is. If you have been editing [clocks].zones in your config,
    this release copies it into zones.toml on first start, and from then
    on you edit zones.toml or use the panel.
  • A clock table taller than its panel scrolls with the cursor. Clocks
    past the bottom of the panel were cut off with nothing to mark them, and
    the cursor could move onto one that was not drawn, where d would remove
    a clock you could not see. The table now shows the part that holds the
    selected clock. A message such as o's path has a row of its own,
    taken from spare room where there is any, instead of covering the last
    clock, and the big clock stays now appears when there is no table
    under the clock to show it with.
  • The ansi and high-contrast themes colour their graphs again. A
    gradient written with colour names was blended through a stand-in grey,
    so every graph, meter and gain or loss figure in both themes came out one
    true-colour grey whatever the load — sent as a 24-bit escape by the theme
    that exists to avoid them. A ramp of named or numbered colours now steps
    between the colours it names, so ansi runs green, yellow, red in your
    terminal's own palette, and hex ramps blend as before.
  • A fast disk's I/O graph is drawn at the right height. Above about two
    gigabytes a second the graph's scale was capped while the readings were
    not, so a 3 GB/s read on a disk peaking at 5 GB/s filled seventy per cent
    of the graph instead of thirty, and anything past 4.3 GB/s drew as a full
    column.
    The disk panel's combined graph reached this on an ordinary large copy
    from a modern SSD.
  • L no longer renames a weather panel that [weather] pins with
    coordinates.
    latitude and longitude win over the place name, so a
    name typed at L only retitled the panel over the old place's weather,
    and was remembered that way across restarts. With coordinates configured
    L now says to remove them to choose a place by name, and a place
    remembered before they were added no longer relabels them. A name you gave
    coordinates with L in an earlier version is dropped the same way; put it
    in [weather].location to keep it.
  • "City, Country" finds the city in that country. The text after the
    comma was matched against the region and the two-letter country code
    only, so London, Canada — the form the L prompt suggests — showed
    London, England. The country's name now counts too.
  • A failing weather refresh says why. Once a reading was on screen a
    failure showed only "refresh failing", so a misspelt place left the old
    place's weather up and never said the new one was not found. The reason
    now follows the reading's age, cut with … where the panel is narrow.
  • The weather panel bounds what the weather service sends it. A place
    name, region or observation time is drawn on every frame, and a broken or
    hostile response could make any of them megabytes long; a failure's
    reason could too, because the JSON parser quotes the value it rejects.
    Names and reasons are now cut at parse with …, far above anything real,
    and an observation time that is not a time is dropped.
  • An edited note's date can be read. The mark that says a note was
    edited took the date column one cell past its width, so at the shipped
    date format every edited note read ·25 J…. The column is a cell wider
    and the date is whole.
  • The notes panel marks what it cuts. A long title in the note pane,
    a long search term above the list, and an error in the note form were
    each drawn at their full length and cut wherever the panel's edge fell.
    Each now ends in … where it does not fit, and the form's keys drop
    whole rather than being cut in half, Esc cancel the last to go.
  • The task form shows its whole hint, its whole error and its way out.
    The row under the fields was one row high and drew only the first line of
    what it was given, so the Due hint lost or empty. and a date it could
    not read lost the list of forms it would take. The message now has the
    rows the form can spare, and ends in … when even those are not enough.
    The key row below it was cut to Esc can on the default dashboard; its
    keys now drop whole, Esc cancel the last to go.
  • The task filter offers whole tags. With nothing typed, the filter bar
    lists the tags in use, and on a narrow panel the last of them was cut to
    something like #mira, a tag that does not exist. Tags that do not fit
    are left off.
  • A prompt's help keeps Esc cancels. The help line under a prompt was
    cut at the dialog's edge, and two of them never fitted: the weather
    location's and the add-a-clock prompt's lost the end of Esc cancels at
    every terminal size. Both are shorter, and a help line that does not fit
    now drops whole parts, the way out last.
  • The calculator shows the end of a long sum as it is typed. An entry
    wider than its column was cut from the right, so the cursor and
    everything typed after the edge went out of sight. It now shows …, the
    last of the entry and the cursor.
  • The key map shows priority_previous whole. Its action column was
    two cells narrower than that name, which read priority_previ… — the one
    column whose job is to be copied into a key table.
  • The watch log's "since you were here" line fits a narrow panel. Below
    21 columns its label was drawn whole and cut by the terminal; it now ends
    in ….
  • Headlines keep their last letters once a story is selected. The list
    moves every line two cells right to make room for its ▸ as soon as a
    story is selected, and the headlines were still wrapped to the whole
    width, so from the first j, o, y or Enter any row the wrap filled
    lost its last two letters with no … — Krakatau drawn as Kraka. They
    are now wrapped to the width they are drawn at. A source and age too long
    for the panel drop the age, and then cut the name with …, instead of
    being cut mid-word. A headline taller than the panel, shown clipped, now
    ends its last row in … instead of stopping wherever the wrap left it.
  • The news panel says No stories. when there are none. A pass that read
    every feed and found nothing, or had no feeds to read, was never recorded,
    so the panel said Reading… for as long as it ran.
  • An Atom feed is reported instead of looking empty. mirador reads RSS
    2.0, and the README said Atom too. An Atom feed, or an address that is not
    a feed at all, came back as an empty feed with no error, so the panel
    could not tell it from a quiet one. It now counts as a feed that failed,
    and a panel with nothing to show names it under Cannot read the feeds
    with the reason: "an Atom feed; mirador reads RSS 2.0 only", or "not an
    RSS feed". An RSS feed that simply has nothing in it today is still a
    quiet feed. The README now says RSS 2.0.
  • A feed with one fault in it keeps the headlines before it. A single
    bare & anywhere in a feed, or one broken tag, threw away every headline
    in that feed. A bare & is now read as the ampersand it is, and any other
    fault keeps the headlines read before it while the feed is still reported
    as failing.
  • A bare & with a ; after it no longer deletes the text between.
    R&D spending rises; analysts wary was shown as R analysts wary, and a
    link whose address had both pointed somewhere else. A reference mirador
    cannot resolve — anything but the five XML entities, nbsp and a
    character number — is now shown as written, … included, which
    used to be dropped.
  • A rate-limited watchlist no longer asks for a source that does not
    exist.
    The HTTP 429 message ended by saying another [stocks].source
    was needed, and yahoo is the only one there is: any other value stops
    startup. It now says, first, that Yahoo is rate-limiting this address,
    that the panel will try again, and that a datacenter or VPN address is
    refused outright.
  • The quote service's error text is bounded. Yahoo's reason for
    refusing a symbol was kept whole and formatted into the markets panel on
    every frame, however long it was. It is now cut at 80 columns with …,
    twice anything real.
  • A temperature sensor whose chip name ends in a number keeps it. A
    Linux sensor with no label of its own is named after its driver, and the
    panel took every digit off the end in turn, so k10temp on older AMD
    processors, lm75 and jc42 on many boards, and it8728 came out as
    k, lm, jc and it in the table, the readout and the status-bar
    alert. Only the sensor's own number comes off now, and a number a
    separator introduces, like the _1 in the kernel's iwlwifi_1.
  • Two tasks or notes sharing an id in a hand-edited file stay two.
    Copying a [[task]] or [[note]] block is the natural way to add one by
    hand, and the copy kept its id. Every key acts by id, so deleting either
    deleted both, and editing the copy rewrote the original, with nothing said
    either time. A repeated id is now given a new one when the file is read,
    and the next save writes it down.
  • The task filter ignores case beyond A to Z. It folded only those,
    so übung did not find a task called Übung buchen, though the notes
    search found the same words in a note. Both fold case the same way now, a
    letter at a time, which mends the notes search for Greek typed in
    capitals too: a capital sigma ending what had been typed folded to ς,
    so a note called ΚΩΣΤΑΣ dropped out of the search at ΚΩΣ and came
    back at ΚΩΣΤ.
  • The calculator takes × and ÷ to the full length of an entry. The
    limit was counted in bytes where the panel counts characters, and those
    two signs are two bytes each, so a long entry using them was accepted as
    typed and then answered too long.
  • A clock named with no zone is refused for that. Tokyo = in the
    clocks panel's add or edit dialog, a label with nothing after the =,
    was answered "that clock is already on the panel". It now asks for a zone
    after the =.
  • Choosing another calendar with f no longer fills the watch log. The
    new calendar's first read was compared with the old one's events, so
    every event in it was logged as having "appeared in your calendar". It is
    now as quiet as the first read at startup, including when a read of the
    old file was already under way, and an event added to it later is still
    reported.
  • The w dialog scrolls on a short terminal. It drew every widget mirador
    has with no window over them, so a terminal shorter than the list cut the
    bottom rows, footer first, while the cursor went on moving into them: End
    then space switched a panel on or off with nothing on screen saying which.
    The list now scrolls to keep the cursor in view, and the status and footer
    under it stay drawn down to a five-row terminal, the blank line above them
    giving way first. With room for everything, every widget is drawn at once,
    as before.
  • The t dialog's footer names the key you set. It drew the keep key in
    capitals, so keep = "y" under [theme_picker.keys] was shown as Y,
    which is a different key and does nothing. Keys are now spelled as you
    wrote them, and both pickers spell Esc the way the key map does.
  • A plugin gets its 300 ms to clean up when it is closed. Mirador tells a
    plugin to shut down and allows it 300 ms before ending it, but anything the
    plugin sent in that time — most often the frame it was already drawing —
    was treated as a protocol error and the process was killed at once. A
    plugin that saves its state on the way out could be stopped halfway through
    the save. A well-formed message sent during the grace is now ignored; a
    malformed one still ends the process at once.
  • A failed plugin shows the end of its error output. The protocol
    promises the last few lines of a plugin's stderr in its panel, and the panel
    showed the first three — so a plugin that logged its startup and then
    crashed showed the startup, never the line saying what went wrong. It now
    shows the last three, giving up the earliest first when the panel is short,
    and a line the panel's height cuts ends in … rather than looking whole.
  • A pomodoro phase counts through sleep. The timer kept its deadline on a
    clock that stops while the machine sleeps on macOS and Linux, so a focus
    interval with ten minutes left when the lid closed still had ten minutes
    left an hour later, and no chime had sounded. It now
    reads the wall clock as well and ends by whichever says less is left: the
    phase that ran out ends on waking, once, and the next waits at its full
    length for a key — or with auto_start begins from the moment you come
    back — rather than the timer racing through the phases you were away for. A
    wall clock set back can neither end a phase nor lengthen it, and a paused
    timer is untouched.
  • A pomodoro phase set longer than 180 minutes keeps its length. + and
    - stop at 180 minutes, and the first press of either cut a longer phase
    from [pomodoro] down to it, took the difference off the time left —
    ending a phase with less than that to go — and remembered the 180 over the
    config. A longer phase now keeps its length: - takes a minute off, +
    puts it back as far as the config's length and no further, and a shortened
    length survives a restart.
  • The pomodoro's phase label says when it has been cut. In a panel
    narrower than the label, SHORT BREAK was cut by the terminal to a
    whole-looking SHORT BRE. It now ends in …, and so does the time where
    it falls back to plain text.

Install mirador 1.20.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jchultarsky/mirador/releases/download/v1.20.0/mirador-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://github.com/jchultarsky/mirador/releases/download/v1.20.0/mirador-installer.ps1 | iex"

Download mirador 1.20.0

File Platform Checksum
mirador-aarch64-apple-darwin.tar.gz Apple Silicon macOS checksum
mirador-x86_64-apple-darwin.tar.gz Intel macOS checksum
mirador-x86_64-pc-windows-msvc.zip x64 Windows checksum
mirador-aarch64-unknown-linux-gnu.tar.gz ARM64 Linux checksum
mirador-x86_64-unknown-linux-gnu.tar.gz x64 Linux checksum

Verifying GitHub Artifact Attestations

The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:

gh attestation verify <file-path of downloaded artifact> --repo jchultarsky/mirador

You can also download the attestation from GitHub and verify against that directly:

gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>

Don't miss a new mirador release

NewReleases is sending notifications on new releases.