Skip to content

Releases: vyncint/mossaic

v0.6.3

Choose a tag to compare

@github-actions github-actions released this 24 Aug 08:03
a992363

Added

  • Three more templates, so the catalogue is a catalogue: wave (one clean
    sine across the year), pulse (a heartbeat trace, a spike every few weeks)
    and invader (a space invader, centred). Each is 51 columns, which keeps
    every lit day clear of the two partial weeks at the ends of a year, and each
    is drawn in {0, 2, 4} — the only three shades no reader has to squint at.

  • Two gates on template art, which matter now that
    #57 invites contributions.
    every_template_reads_clearly measures the closest pair of shades in every
    template and fails anything that is not clear: a picture using 2 and 3
    looks two-toned in the .art file, because the digits differ, and reads as
    one flat colour on github.com. no_template_overhangs_the_year fails a
    template that loses a lit day off either end of any year. Both were checked
    against art planted to break them.

Changed

  • mossaic-art --help is grouped rather than a list of thirty flags. The
    options now sit under what to draw, where it goes, tracking, making
    the commits
    and output, with the usage and seven examples first. The
    colour-theory paragraph that lived inside --background moved to
    docs/ART.md, where there is room to say it properly. Shorter
    overall (93 lines against 113) and no flag was dropped.

  • Counts read as English. Every user-facing count said 1 template(s),
    6 cell(s), 59 day(s) — twenty-nine of them, on the first lines a new
    user reads and in the markdown the GitHub Action posts. They now agree with
    their number, verb included: 1 day is short, 2 days are short.

  • The description says what mossaic is. It never mentioned the terminal.

Fixed

  • The one shipped template no longer warns on every single year. Drawing
    --template dragon printed note: 6 cell(s) fell outside 2027 and were dropped for 2025 through 2029 — and all six were blank margin, so nothing
    was lost. Canvas::place counted every cell that fell outside the year,
    lit or not. It now counts only the cells that would have been drawn, so the
    note appears when a shade is genuinely lost and stays quiet otherwise.

    Found by running the flagship path and reading what it printed.

  • A missing --file in a script names the file, not the terminal. The
    chart claims the terminal before it reads the calendar, so
    mossaic --file typo.json in CI reported needs an interactive terminal
    true, and no help at all in finding the typo. With a terminal the behaviour
    is unchanged: the error is shown in the chart with r to retry, which is
    better than exiting.

  • --font no longer tells its readers to edit src/art.rs. It printed a
    note meant for contributors to everyone who ran it.

  • The two guidance messages line their columns up. could not detect a GitHub user put one of its three suggestions two columns out, and the
    --demo line — the one that needs no account at all — came last. The
    missing-file message tried to align against an interpolated path, which
    cannot be done, and repeated back a path the reader had just typed.

  • brew install vyncint/tap/mossaic. The formula is generated by the
    release itself from the checksums the archives produced, so it cannot
    describe an archive that was never built, and a hand-edit in the tap is
    overwritten by the next release rather than quietly kept.

    Verified by installing it: brew audit --strict is clean, brew test
    passes, and the three binaries plus a copy of the art templates land where
    the formula says.

Fixed

  • A release's archives are never replaced once published. The upload
    clobbered, so re-running the artifact build for an existing tag rebuilt and
    overwrote them — and Rust builds are not bit-for-bit reproducible, so the
    replacements had different checksums. Every checksum anyone had recorded
    became wrong, including the Homebrew formula's and including one somebody
    had written down to verify a download with.

    Found by re-running the workflow: the formula job reported "tap updated"
    where nothing should have changed, and the diff was four checksums.

    A re-run now fills in what is missing and leaves what is there alone, which
    is the case the workflow was built for. Replacing a genuinely corrupt asset
    means deleting it first, and that friction is deliberate.

  • The Intel-Mac binary is cross-compiled from Apple silicon rather than
    built on a macos-13 runner. Xcode's toolchain and SDK are universal, so an
    arm64 Mac produces an x86-64 binary with nothing but rustup target add
    no sysroot, no linker configuration.

    The Intel runners are being wound down and it shows: on the v0.6.2 release
    this leg sat queued for forty-five minutes while every other target finished
    inside ninety seconds. A target nobody can schedule is a target that
    silently stops shipping.

    The job now asks file what it actually built, and fails if the answer is
    not the architecture the target names — a cross-build that quietly produced
    the host's architecture would pass every other check here and fail on the
    first machine that downloaded it.

  • The README briefly promised Homebrew before the tap existed, and said
    so for an hour or two until it did. An install command that does not work
    is worse than one that is not offered; every route the README lists is now
    one that has been run.

  • The Windows binary is built. The 0.6.2 release shipped Linux and macOS
    archives and no .zip: the build step had no shell: bash, and Windows
    runners default to PowerShell, where "$TARGET" expands to the empty string
    and cargo build --target is handed nothing. Every step is bash now.

  • Building the artifacts is re-runnable, in binaries.yml rather than
    inside release.yml. Publishing to crates.io happens once and cannot be
    repeated; building an archive can fail for reasons that have nothing to do
    with the release, and when it does the fix must not be "cut another
    version". It fires on a published release, and by hand for any tag whose
    artifacts need rebuilding.

v0.6.2

Choose a tag to compare

@github-actions github-actions released this 23 Aug 06:19
f266be6

Added

  • Prebuilt binaries on every release, for Linux, macOS and Windows on both
    x86-64 and arm64 — so using mossaic no longer needs a Rust toolchain.

    The reason is not convenience. The pitch for contributing a template is that
    it needs no Rust, because a template is a text file — and that pitch is false
    if the only way to see what you drew is cargo install. Somebody who does
    not write Rust should not have to install a compiler to draw a picture.

    Linux builds are musl, so they are statically linked and run on any
    distribution whatever its glibc: a binary downloaded today still starts next
    year. Every target builds on its own architecture rather than
    cross-compiling.

    Each archive carries the three binaries, both licences, the changelog, and a
    templates/ directory to start from, with a .sha256 beside it. Assets are
    uploaded under two names: the versioned one, which cargo binstall and the
    Homebrew formula look for, and an unversioned alias, because
    releases/latest/download/<asset> resolves by exact file name and a URL that
    is stable across releases therefore cannot contain the version.

Changed

  • The termlens dev-dependency moves to 0.6.1, which carries an
    openpty retry that had been written, merged and left unreleased. Without
    it this suite fails on macOS at sixteen threads with Device not configured — a PTY device the kernel has not finished recycling — at
    roughly one shard in ten. It is a test-harness fix and touches nothing that
    ships.

  • Refreshed the lockfile. cc 1.4.3 → 1.4.4, darling 0.24.0 → 0.24.1
    (with _core and _macro), either 1.17.0 → 1.18.0, log 0.4.33 → 0.4.34,
    uuid 1.24.1 → 1.25.0. Four of those — cc, darling, either, log
    are genuinely compiled; uuid is in the resolution but not the build.

    This matters only to the binaries. cargo install mossaic --locked builds
    what this file names, so refreshing it is what puts those versions in an
    installed mossaic. A library consumer resolves its own dependencies and is
    unaffected either way.

    cargo deny check passes on the new lockfile: advisories, bans, licences and
    sources all clean.

  • generic-array stays at 0.14.7, and cannot move. cargo update reports
    0.14.9 as available, but crypto-common 0.1.7 requires it exactly:

    generic-array = "=0.14.7"
        required by crypto-common 0.1.7
        ... digest 0.10.7 -> sha2 0.10.9 -> termwiz 0.23.3
        ... -> ratatui-termwiz 0.1.2 -> ratatui 0.30.2 -> mossaic
    

    0.1.7 is the newest of the 0.1 line, and the relaxed requirement only arrives
    in crypto-common 0.2, which needs digest 0.11, which needs sha2 0.11,
    which needs a termwiz that depends on it. None of that is this crate's to
    move.

    It is also never compiled here. ratatui-termwiz is an optional backend and
    mossaic uses crossterm, so nothing in that subtree — generic-array, sha2,
    digest, termwiz — reaches a build: cargo tree does not list it, and a
    full build produces zero artefacts from it against ratatui's ninety. The
    entry is in the lockfile because a lockfile records the whole resolution,
    optional branches included.

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 23 Aug 05:03
544323d

Fixed

  • A day the picture does not cover is reported as free, not as one that must
    stay dark.
    Both a day the picture wants dark and a day outside it have a
    need of zero, and the tracker read the number rather than the intent — so
    every day after a picture ends was told to stay dark, when it is nobody's
    business. The advice for the two is opposite: one must be left alone or the
    drawing is damaged, the other is free.

    Only the human --track report was affected. The JSON and Markdown formats
    carry today.kind, which named the two apart correctly all along.

    The test written for this passed against the bug first time round: a day
    outside the picture with no contributions is not recorded at all, so it
    takes a different path and never reaches the branch. It needs a day that is
    both outside the picture and already contributed to, which is exactly the
    case that produced the report.

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 04:33
d1a8a1b

Added

Pixel art across the whole year. The font draws five rows of two shades on
Mon–Fri; a canvas is the general case — seven rows, up to 53 columns, any
of GitHub's five shades on any day, the weekend included.

  • --template NAME and --list-templates. A catalogue of whole-year
    pictures, shipped with the crate. --list-templates prints each one with a
    thumbnail, because a name and a sentence are not a picture and the point of
    choosing one is what it looks like. Contributing a template is dropping a
    .art file into art/templates/build.rs embeds every file it finds, so
    there is no list to remember to edit. Local templates in ./templates/ and
    ~/.config/mossaic/templates/ shadow the built-in ones, so a template can be
    tested under the name it will eventually ship with.

  • --matrix FILE. Draw a .art file: seven rows of 04, or of the
    blocks ░▒▓█, with # comments and a # name: / # author: /
    # description: header. A short row is padded with dark days, so an editor
    that strips trailing whitespace cannot corrupt a picture whose last column is
    dark.

  • --draw, an editor for the year itself. Arrows or hjkl to move, 04
    to paint, space to cycle a cell, the mouse to paint directly, c clear, i
    invert, u or ctrl-z undo, s save. The HUD shows the date under the
    cursor, the days at each shade, how well those shades will separate for a
    reader on any palette GitHub ships, and what the picture would cost in
    commits
    — the same arithmetic --write uses rather than an approximation
    of it. Days in the partial weeks at either end of the year are drawn as ·
    and cost nothing, because they are not days the year has.

  • --image FILE, with --invert and --dither. A PNG becomes a year:
    shrunk to the calendar keeping its aspect ratio, and quantised to five
    shades. A dark pixel becomes a busy day, the way ink reads on paper. Every
    source pixel that lands in a cell is averaged into it — the shrink is
    enormous, and sampling one pixel in twenty throws the picture away.
    --dither is Floyd–Steinberg, so a gradient reads as one rather than as four
    bands. The decoder is written here rather than pulled in: zlib is already
    present for the kitty protocol, so this costs no new dependency. PNG only,
    and it says so — a JPEG is named as a JPEG, with the one command that
    converts it.

  • art::Canvas, art::band, and plan::Plan::from_levels — the
    library half. A canvas tracks, saves, backfills and writes exactly as text
    does, and the GitHub Action reads the same JSON, because Day was already a
    band rather than a target and a level-2 day is that band with different
    numbers.

  • art::Canvas::closest_pair, and the palette rule it exists to enforce.
    GitHub's five greens are not evenly spaced: every adjacent pair is 9–20 ΔE
    apart in the worst palette it ships. {0, 2, 4} is the only three-shade set
    with no faint pair in it, and there is no clear set of four — proved by
    enumeration in the suite rather than asserted in prose.

  • The GitHub Action takes template, matrix, image, invert and
    dither.
    Exactly one of text, template, matrix or image says what
    is being drawn, and giving two is refused rather than guessed at. Every
    existing input and all twenty outputs are unchanged and work for a picture:
    a picture is the same plan underneath. matrix and image are paths in your
    repository, so a workflow without actions/checkout is told that rather than
    told the file is missing.

Changed

  • Contrast is reported for the shades that look most alike, not the
    darkest and brightest. The widest pair always flatters: a picture spanning
    levels 0 to 4 reads as ΔE 70 and can still hold two greens nobody can
    separate, because the ones sitting next to each other are 9 apart. This
    changes what --template, --matrix, --image and --draw print; a text
    plan is unaffected, since two shades have only one pair.

  • The reference template is drawn in three shades, not five. dragon.art
    used all five to demonstrate the format, which demonstrated the wrong thing:
    the rendered chart is what showed it up. It reads clearly now.

  • --top and --background are refused with a picture rather than
    silently ignored. They place letters on rows and shade the field behind
    them; a picture uses the whole week and names its own shade for every day.
    Only when typed, so the defaults nobody chose still work.

  • plan::Plan::holes counts any day past its ceiling, not only the ones
    inside the text's own block. A canvas has middle shades, and a level-2 day
    that has crept to level 4 is damage nothing takes back. This cannot change
    what a text plan reports: text gives a letter day no ceiling — brighter than
    brightest is still brightest — so there is nothing for one to pass.

  • plan::Spec carries an optional art field, the picture stored inline.
    A name would be a pointer to something that can change underneath a plan, and
    every other field here is resolved for exactly that reason. Plans written
    before this load unchanged, and a text plan is written byte-for-byte as it
    was — the field is skipped when absent.

    One direction does not work, and cannot be made to. A plan saved from a
    picture and then read by 0.5.0 or earlier draws the picture's name as
    text, because that version has no idea the art field exists and serde
    ignores what it does not know. Nothing added here can change what an already
    released binary does with a file. If you keep a canvas plan, keep a mossaic
    that understands it.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 15:09
b125a9b

Added

  • Shapes in the font — mossaic-art "I :heart: RUST". Nineteen of them:
    :star: :heart: :smile: :sad: :check: :circle: :square:
    :triangle: :diamond: :note: :sun: :moon: :bolt: :up: :down:
    :left: :right: :skull: :flower:, several with aliases (:cry: is
    :sad:, :zap: is :bolt:). A shape is written between colons so it can be
    typed on any keyboard; the symbol works where you can type one, and so does a
    pasted emoji — ⭐ draws :star:, 😢 draws :cry:, and the variation selector
    an emoji keyboard adds is dropped rather than refused. All three spellings
    become the same character before anything else sees them, which is what lets
    a shape survive being written to a plan, uppercased, and read back by
    --track every morning — including into the commit message --write leaves
    behind.
  • Punctuation: ! ? , ' " + = < > ( ) / \ * _
    @ & # %. The font is 77 glyphs where it was 39.
  • A picture of everything the font can draw,
    in the README and in docs/ART.md — every glyph rendered as contribution
    cells by the same rasteriser that draws the chart, in real Primer greens on a
    level-1 field. An image rather than a table of characters for the same reason
    the sextant chart is gone: half of these symbols are ones a browser may have
    no font for, and a picture of the cells has nothing to be missing.
  • mossaic-art --font --png PATH, which writes that sheet. The terminal
    view is still --font on its own.
  • graphics::sheet, the rasteriser generalised from a calendar's seven
    rows to any rectangle — so the sheet is drawn by the code that draws the
    chart rather than by a second one that would drift from it. graphics::grid
    is now a call to it and draws identical pixels.
  • A list of everything the font can draw in the README as text as well, and
    tests that check both against the font: every symbol and emoji in the table,
    and the image itself, regenerated and compared byte for byte. A table of
    glyphs is exactly the documentation that rots, and it is the only place most
    people will look.

Changed

  • : is the shape delimiter and has no glyph of its own, which is what
    keeps the grammar to one reading. A name nobody has drawn, or a colon that
    closes nothing, is refused with the list of shapes rather than guessed at.
  • mossaic-art --font names its shapes (:star:) instead of printing the
    symbol. Several of them are ambiguous-width, so a terminal may draw one in
    two columns and tear the aligned output apart.
  • The README no longer prints the sextant chart inline. It was 740
    block-sextant characters (U+1FB2B, U+1FB1B, Unicode 13) that too few
    systems have a font for: where one is missing the browser substitutes a font
    of a different width and shears every row of a 176-column chart apart, which
    is a worse advertisement for the fallback than no picture at all. The promise
    it was making stays, with the one command that shows it in a terminal, where
    the characters are guaranteed to work.

Tests

  • The pixel path is asserted as a picture, not as a byte count. termlens
    0.6 hands back the payloads an application transmitted and decodes them, so
    tests/pixels.rs now checks what the chart actually drew: that the year is
    pinned to the 106x7 character cells the layout reserved and placed on the
    grid's own origin, that it goes out as one image however many of kitty's
    4096-byte chunks it takes, that one moved cursor sends a single day-sized
    image rather than a cheap-looking year, and that the chart deletes its
    images before it exits instead of leaving them over whatever comes next.
    None of that changes a cell on screen, which is why none of it was
    assertable before.
  • The year drawn is checked against the year described. Every day's centre
    pixel is read out of the decoded image and counted: the number drawn in
    Primer's brightest green has to equal the number the footer calls active.
    The two halves of the chart — the picture and the text under it — had never
    been compared to each other, and nothing on screen can compare them.
  • The in-process sixel decoder rounds the way a terminal does. It
    converted a percentage back with p * 255 / 100, the exact inverse of the
    encoder's own rounding, so a systematic disagreement with real terminals
    would have round-tripped perfectly through a test whose stated purpose was
    to catch exactly that.

Changed

  • Esc no longer quits the chart. It cancels the username prompt and
    dismisses the help overlay, which is all the UI ever implied it did, and q
    is the only quit key. 0.4.0 took the other half of #34's suggestion and
    documented Esc instead, which left the complaint the issue opens with
    untouched: pressing Esc to dismiss a tooltip exited the program. Documenting
    a surprise is not the same as removing it. (#34, #44)

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 20 Aug 09:51
6863f6a

Added

  • --today DATE on the chart, not just the tracker. 0.3.0 made time an input
    for mossaic-art and left mossaic reading the clock, so the two disagreed
    about whether time is an input at all — and the still-to-come half of the
    rendering was reachable only from a live fetch, which is why the cursor could
    vanish there unnoticed. With --file it also makes a saved year show its future
    days, which is what a plan snapshot for next year actually is. (#38)
  • --palette auto can pick winter. Season::on matched only Halloween, so a
    scale that is fully implemented in all three appearances, measured among the nine
    palettes Shades::worst reads, and askable with --palette winter could never
    appear by itself. GitHub does not publish the windows; December 19–25 is what it
    has been observed using, and the code says so rather than claiming a
    specification. (#22)
  • The chart says when a protocol was asked for and could not be used.
    --graphics kitty without a cell size drew characters silently; --capabilities
    explained it and the chart did not. (#37)

Changed

  • Cells are the size github.com draws them. The 5% border was drawn first and
    the fill inset by it, so the coloured square measured 14px in a 20px pitch —
    0.700, where github.com's is 11 on 14, or 0.786. Every cell was about a ninth too
    small with a dark rim where a hairline belonged, --png included, which is where
    the README's own screenshots come from. The square is now drawn at full size with
    the hairline over its edge, and the offsets are rounded to whole pixels: at a 9x19
    cell the opaque extent was 12x11 where 12x12 was drawn, soft on one axis and crisp
    on the other. (#20)
  • A current streak is the run ending on a given day, so Calendar::stats takes
    one. Both loops compared positions in a list rather than dates, so any gap read as
    an unbroken run — ten non-adjacent Mondays reported a ten-day streak — and the
    "quiet today" tolerance was applied to the last elapsed day of any year, so a
    year that ended a decade ago reported a current streak of forty. (#18)
  • mossaic-glyphs uses the shared argument parser, so all three binaries agree
    about what a user notices: --color=never is honoured rather than ignored, an
    unknown option or a stray argument exits 2 with a message naming the binary, and a
    missing value says so instead of quietly defaulting. (#25)
  • Rgb::luminance is now Rgb::brightness. It applies the luminance
    coefficients to gamma-encoded sRGB with no linearisation, so it was not the
    quantity its name promised — mid grey came out 0.502 where relative luminance is
    0.216. The threshold it produces is the more useful one for choosing a terminal
    theme; only the name was wrong. (#35)
  • A flag that would do nothing is refused rather than ignored. --track with
    --snapshot, --write or --repo wrote nothing and said nothing; --demo with
    --file silently picked the demo. Distinct from --commits, which tracking has no
    use for but which is a tuning parameter rather than a side effect somebody asked
    for. (#26)
  • The legend says auto: while the chart is choosing. Cycling with d past the
    narrowest style lands back on Auto, which resolves to whatever fits — so the
    press looked like it had done nothing. (#34)

Fixed

  • Pixel images are bounded by columns as well as rows. Sixel clears the
    character cells it is about to cover by writing spaces, and did so with no idea
    how wide the terminal was: a 53-week grid wrote 106 of them from column 6, so on
    an 80-column terminal seven rows wrapped over the weekday gutter, the right border
    and the rows below — and stayed wrapped, because ratatui believed it had written
    those cells. The legend image was never bounded at all, and was clamped onto the
    frame's last row. (#17)
  • A shade GitHub names and this version does not is derived, not dropped.
    level_of mapped anything unrecognised to 0, and nothing downstream re-derives
    one — so a day with forty contributions was painted exactly like an empty one
    while the header reported a full year. The fallback is art::level, GitHub's own
    rule, which the art costing already used. (#19)
  • The keyboard cursor is visible on a day that has not happened. patch drew
    nothing at all without a level, the ring included, and filled_rows blanked a
    future day before consulting the mark — so it vanished in pixel, rounded, snug and
    squares cells while the five bordered styles showed it, and the detail line went on
    naming the day it was on. (#21)
  • The capability probe reads a reply that shares its buffer. Only the first
    CSI ? was tried, so a CSI ?2026;2$y — the DECRQM answer for synchronized
    update, the mode this program itself uses — left in the tty queue by whatever ran
    before us hid sixel and the sentinel, making every start-up pay the whole 250 ms
    deadline. OSC 11 now also accepts the #rrggbb form of an X colour spec, which
    was read as no answer at all, so a light terminal replying that way got the dark
    palette. (#23)
  • [ and ] stay inside the years --year accepts. The fallback for a year
    outside the contribution set stepped without a bound, one gh subprocess per
    keypress, and the header named years the CLI refuses to be given. (#24)
  • At 80x24 the chart still says how to get out. The footer was one 111-character
    line, hard-truncated, so neither q quit nor ? help was on screen; the note row
    was not counted in the chrome budget, so at one particular height it evicted the
    footer; and the help overlay truncated from the bottom, dropping what the terminal
    can draw and any key closes this with it. The footer now drops the guessable
    hints first and the overlay drops its cheapest lines. (#27)
  • A click that misses a day puts the tooltip away, instead of leaving it naming
    an unrelated date — which on a terminal that reports clicks but not motion nothing
    later corrected. And the mouse no longer acts behind the help overlay, where the
    wheel changed year and started a fetch for a year the reader could not see. (#28)
  • --png refuses an empty year rather than writing a zero-width PNG, which the
    format forbids and no viewer opens, and reporting success. (#29)
  • The help overlay is opaque on kitty. An image at z=-2 draws over a cell
    background rather than under it, and the panel sets one, so the chart showed
    through its text. The painter now stands down while it is up. (#30)
  • The advice line never points at a smaller style that does not exist. From
    Auto at its narrowest it said "press d for a smaller style", where d goes to
    pixels — three times wider. It now names the columns a year needs. (#31)
  • --capabilities honours --cell, so the one command whose job is explaining
    the pixel decision no longer reports the opposite of what the run would do. (#34)
  • A calendar naming a day twice is refused rather than keeping whichever record
    an unstable sort put last, and a GraphQL error without a message surfaces as an
    error rather than as "unexpected response from gh: missing field message".
    calendar::demo no longer panics on a year no calendar can hold. (#35)
  • The character cell is measured again on a resize, which is also when a font
    size changes — docs/DESIGN.md promises the geometry is scaled to whatever a cell
    measures, and it was measured once at startup. (#37)
  • Eight measured claims in the design notes, and five in the source. The
    tightest adjacent ΔE is 9.1 (light + halloween, levels 1 and 2), not the 10.8 that
    five places quoted for a different pair (src/art.rs among them); the sixel coarsening never triggers for a
    chart, which needs 20–27 of 256 registers; the kitty layering is two layers at
    z=-2 and z=-1, not one at z=-1; the probe asks five questions, not four, and
    has a window-size fallback; the two-level gap is warned about rather than enforced;
    the byte figures are ~5 KB and 44 KB at a 9x19 cell; the xterm-256 collision is
    between levels 0 and 1 rather than 1 and 2, and the dimmed ramp ties rather than
    inverts; and the layers block was missing four modules. The load-bearing claim —
    two levels apart never below ΔE 35.4 — holds, and is now pinned by a test. (#33)
  • Release hygiene. .claude/ no longer ships inside the crate,
    extract-changelog.sh runs from anywhere rather than only the repository root, and
    docs/RELEASING.md gained the two steps whose absence let three action@v0.2.0
    references go stale: bumping them, and repointing the changelog's own link
    definitions. (#32)

Documented

  • The advice line named a window size that would not have worked. It quoted the
    drawable area inside mossaic's own border, so a reader with a 17-row window was
    told it "has 15" — resizing to the 17 it asked for still did not fit. Both numbers
    are now the ones a terminal reports. The README's figures were wrong in the other
    direction ("about 115×26" for the bordered grid, against 166×27), and are now
    measured in a pty: 112×19 for pixel or square cells, 165 columns for rounded
    corners. (#31, #33)
  • The README is a quarter shorter and both its pictures are 2027. The headline
    claim is "2027 should read VYNCINT" and the screenshot was a 2026 calendar, so
    the first thing a reader saw disagreed with the first thing they read. Both
    images are now the same planned 2027 year — once in pixels, once in sextants —
    and the screenshot draws the letters on a level-1 field rather than on an
    empty graph, which is the option the guide recommends and the old picture never
    showed. Its caption is the two commands that reproduce it byte for byte.
    It was also rendered with the pre-#20 geometry, so the one image whose entire
    job is showing that mossaic draws github.com's cell size was showing cells a
    ninth too small; and t...
Read more

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 20 Aug 01:06
3002381

Added

  • tests/pixels.rs: the pixel path, end to end. Eight tests over the half of
    the program that was not merely unasserted but unreachable. mossaic decides
    whether to draw pixels by asking the terminal, so a harness that answered no to
    every question could only be driven down the text path — the suite worked around
    that with --graphics sixel --cell 10x20, which forces the protocol and hands
    over the cell size, skipping the probe, the fallbacks and the auto choice.
    termlens 0.5 states which terminal is being simulated, so the real decision runs.

    What that buys, beyond the probe itself: the byte budgets in docs/DESIGN.md §4
    are now checked against the wire (a year is ~5 KB over kitty against ~44 KB of
    sixel, and the test holds the order of magnitude the design decision rests on);
    the diffing in §5 is checked the same way, since one cursor move costs under a
    tenth of a year rather than re-sending it; the cell contract in §3 is asserted
    through a terminal for the first time, with all seven grid rows left to the
    painter while the month labels stay text; --graphics text is checked to send no
    image at all on a terminal that could draw one; Auto is checked never to place
    an image too wide to fit; and --capabilities, which had no test at all because
    no harness could answer the questions it asks, is checked to agree with what the
    chart then does.

  • Two tests for behaviour no content assertion can see. An idle chart repaints
    on an 80 ms timer so the loading spinner can turn, and every one of those frames
    shows exactly the right content — printable_chars is what shows that a settled
    chart writes nothing at all. And mossaic ignores keys it does not bind silently,
    which used to be the same screen as refusing them with a bell; bells() tells
    the two apart.

Changed

  • termlens 0.4.2 → 0.5.0 (dev-dependency). send, send_str and paste
    return Result rather than panicking, so every input call in the suite now
    propagates with ? — 24 call sites, which is the whole migration.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 12:56
23079ae

Added

  • --today DATE makes "today" an input. The tracker read the clock, so its
    answer to "what does today owe" was a fact about when it ran rather than about
    a calendar and a date. Now it can be told, which is what lets a report be
    reproduced, lets a day that has not arrived be asked what it will owe, and
    lets the tests assert on a report at all. (#2, #3)
  • --backfill commits what the days already past are short of, and nothing
    else.
    A plain --write puts the same flat count on every lit day, including
    the ones already bright — and adding to the busiest of those raises the year's
    peak, which is the very thing every letter day is measured against. A shortfall
    cannot do that: need is at most the peak already, so what every other day
    owes is unchanged afterwards and one pass of arithmetic is enough. Days still to
    come are deliberately left for an ordinary commit on the day; back-dating is
    only how you reach the ones that have gone. --today is what "past" means.
    Needs --repo, and --write to commit. (#7)
  • keep-dark, a day the report now warns about. A day inside the text block
    that is not part of a letter has to stay empty, and a contribution on it is
    the one loss nothing takes back. The Action gains today-kind and
    tomorrow-kind outputs, so do not commit tomorrow can arrive a day early.
    (#4)
  • A Windows job in CI. The cfg(not(unix)) probe was never compiled by any
    gate, and had drifted out of date in two ways that -D warnings would have
    caught. (#11)

Changed

  • fail-on: behind counts letter days only. It tested today.short, which
    is also non-zero for a short background day — so with background set, each
    of the year's ~290 field days failed the job, blaming a letter day for it.
    (#9)
  • A report says nothing about a day its own year does not hold. Tracking
    2024 while it is 2026 reported on a day in 2026 and called it outside, which
    is true of the wrong calendar; today and tomorrow are now null there.
    (#4)
  • hole names damage rather than position, in the JSON report and in the
    markdown one. A clean day inside the letters is keep-dark; hole is what it
    becomes once it is too bright. The old value was a position, and the markdown
    report read it as damage — so with a background drawn, a day inside the letters
    holding no contributions at all was reported as "inside the letters and
    already lit — a permanent hole". It now reads as the background day it is.
    (#4)
  • A failed fetch names whose year it was reading. Whose contributions to
    track can come from a saved plan, and "gh was not found" gave no hint which
    login it had resolved. The login is stripped of control characters where it is
    resolved rather than at each of the places that print it, which is the rule the
    calendar already followed — a plan file is a file someone may have sent you.

Fixed

  • A calendar has to sit somewhere a calendar can sit. github::parse bounded
    how wide a saved calendar was (400 days) but not where it was, and the grid
    reaches outside the days it is handed: back to the Sunday before the first, and
    a week forward from the cursor. A file whose first day was within three of the
    earliest date a NaiveDate can express panicked in release as well as debug —
    and in the chart that was the worst of it, because the fetch runs on its own
    thread: the thread died and the screen sat on "loading" for ever, with no error
    and no way to retry. Calendar::build degrades to an empty grid rather than
    panicking, for the same reason art::Grid::new returns None.
  • The cursor cannot be walked off the end of the calendar. move_cursor
    added to the date and clamped afterwards, so the clamp could not save it: with
    a calendar ending within a week of the last representable date, one arrow key
    ended the process.
  • A saved plan is validated when it is read. --plan PATH names a file, and
    a file need not have come from your own --save — so it was the way around
    every bound the command line enforces. A top of usize::MAX wrapped past
    place's guard (usize::MAX + 5 is 4, which is comfortably "inside" a
    seven-row week) and drew the letters on scrambled rows; a commits of
    u32::MAX quoted four billion commits a day; a year of -262143 panicked
    building the calendar, and one of 180000 drew a calendar cli::YEARS exists to
    refuse. Spec::validate applies the flags' own ranges, so a saved plan can
    never mean something a typed command could not.
  • art::Grid::new keeps the promise in its own doc comment. It said it
    returned None rather than panicking "because this is a library: a year
    arriving from a command line, a file or a caller is input" — and then stepped
    back to a Sunday with unchecked arithmetic, which panics in the first week a
    calendar can express. Two guards for one hole, because the second is what the
    library owes a caller that is not this CLI.
  • Three counts from a --merge calendar no longer wrap. The year's peak — the
    number the entire costing model rests on — was accumulated with +=. A
    calendar whose busiest day held four billion reported a peak of 8 and quoted a
    price to match it, in release builds; in debug it panicked. Everything around
    them already saturated or widened.
  • Numeric options are bounded where they are read. --year was
    range-checked because a binary once passed 999999 through to a panic; every
    other number took the unguarded path, and as is not a check. --commits -1
    came out as 4,294,967,295 — which --write would then try to build a
    fast-import stream for — --commits 0 drew bright letters for no commits at
    all, --top -1 wrapped past the guard meant to catch it, and
    --start-week -1 reached usize::MAX, where building a date from it panicked.
    A start column too far right is now refused with the last one that would have
    fitted, rather than silently drawing a plan of no days. (#5)
  • A saved plan's user is no longer discarded. It was read out of the file
    and then overwritten with the nothing a bare --track carries, so
    mossaic-art --track ignored the login the plan named — and with gh
    authenticated it tracked the wrong person rather than saying so. (#6)
  • A --merge calendar from another year says so. Every day of a 2026
    calendar falls outside a 2027 grid, so it filtered down to nothing and
    everything downstream was correct about an empty year: 9,527 contributions
    read as none, and a plan that cannot be drawn read as reachable at one commit
    a day. (#8)
  • --today outside the plan's year says so. The flag carries no year of its
    own, so --today 2027-06-01 against a plan whose year defaulted to this one
    asked about a calendar the plan does not cover — and the entire "what to do
    next" half of the report silently vanished. Still allowed, because every letter
    day of an ended year is overdue and that is a retrospective worth asking for.
  • Test scratch files no longer share a fixed path. Fourteen of them named a
    constant under the temp directory, so two test processes in one checkout — a
    stress loop beside an ordinary cargo test — raced and failed in ways that
    looked like the code. CONTRIBUTING.md §3 asks for hermetic tests, and a fixed
    global path is not one.
  • Two tests no longer assert on the wall clock. One claimed whatever day it
    ran on was a lit day inside the letters — true when it was written, false the
    morning after — and another that 2027 had not started. Both now pin --today,
    and CI runs the CLI suite a second time from a timezone that is already
    tomorrow. (#3)
  • The command --track suggests for back-dating now does what the sentence
    above it promises.
    It printed a plain --write with the text typed out,
    which ignored the saved plan, defaulted to 4 commits a day where the plan
    needed 110, and wrote to every lit day rather than the short ones. (#7)
  • Sixel uses all 256 colour registers. The capacity check bailed at a full
    palette rather than a full-plus-one, and it ran after an index had been handed
    out — one step later and seen.len() as u8 would have wrapped to 0 and
    aliased a colour. Restructured so no such index is ever constructed. (#14)
  • Rustdoc: term::probe carried doc comments on both sides of its
    #[cfg] and rendered them concatenated; calendar::demo's summary line
    belonged to Stats; the non-unix probe had no documentation at all. (#10)
  • calendar::demo no longer re-implements GitHub's shading. It called
    art::level's formula a second time, in u32 where the original widens to
    u64 — a second place to change when GitHub restyles the graph, in a crate
    whose whole point is agreeing with github.com. (#14)

Removed

  • y and Y no longer change year. They were undocumented, they duplicated
    [, ], PageUp and PageDown, and in a chart that binds h j k l a y
    that silently moves the year is the one binding with a real chance of
    surprising a vim-handed user, to whom it means yank. PageUp and PageDown
    are documented now, in the README and in the ? overlay. (#12)

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 09:52
babbd52

Changed

  • The GitHub Action installs the tracker from crates.io instead of building
    the checked-out repository, and gains a version input to say which release:
    the default, latest, follows crates.io, and a pinned number changes only
    when you change it. Nothing is cached — a fresh --locked install per run
    costs a minute or two and can never be stale, poisoned, or the wrong
    version. The ref you pin (@v0.2.0) now chooses only the action's own
    steps.

Fixed

  • Tracking is no longer refused because of a flag it does not use. Over a
    busy year, --track --merge --background 1 failed with "the letters would
    not show" — a check meant for runs that draw, where --commits is what
    each lit day gets. Tracking never writes a commit; it works out what a
    letter day needs from the year's real peak. The GitHub Action passes no
    --merge and so never hit it, which meant the Action and the CLI disagreed
    about the same plan. Found by running the Action against a real year and
    comparing.
  • A hole is called a hole again. With no background drawn, a lit day inside
    the letters reported as "background — 113 contributions, 113 too many for
    level 0", naming a shade the plan never asked for. The markdown report always
    got this right, so the text report was the one disagreeing with it.

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 19 Aug 09:09
4ce58b0

No functional changes — the crate is byte-for-byte the same tool as 0.1.0.
This release exists to prove the publishing pipeline works the way it is
supposed to.

Changed

  • Releases publish through crates.io Trusted Publishing. The workflow
    exchanges the job's OIDC token for a credential that lives for minutes, so
    no registry token is stored on the repository at all. 0.1.0 could not do
    this — Trusted Publishing is configured on a crate, and the crate did not
    exist until 0.1.0 published — so it went out with a stored token, which has
    since been deleted along with the zizmor exception that covered it.