Skip to content

Release v1.1.1 - #12

Merged
JamesM92 merged 18 commits into
mainfrom
dev
Aug 9, 2026
Merged

JamesM92 merged 18 commits into
mainfrom
dev

Conversation

@JamesM92

@JamesM92 JamesM92 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Promotes dev to main for v1.1.1.

Summary

Small patch release restoring the `FT<6hex> / `BT<6hex> 24-bit color parser that NomadNet's reference implementation supports.

What's in

  • feat: restore FT<6hex> / BT<6hex> 24-bit color parser (f275e8c) — the parser now handles NomadNet's exact-color escape form. Invalid or too-short input still consumes T + 6 chars so garbage doesn't leak. Docstring explicitly notes the MeshChat portability trade-off.
  • v1.1.1 (a69fdb7) — version bump + CHANGELOG.

Test plan

  • 97 pytest cases pass (4 new/modified in TestColors)
  • CI green on this PR

JamesM92 and others added 18 commits May 15, 2026 22:33
Previous positions 15/38/62/85 had:
  - intra-cell row spacing: 23-24% per gap
  - inter-cell row spacing: (100-85) + 15 = 30% per gap

That 6-7% disproportion produced a visible horizontal stripe between
adjacent rows of Braille — the column of Braille cells read as
discrete rows instead of a continuous grid.

New positions 12.5/37.5/62.5/87.5 give uniform 25% spacing both
within a cell and across cells, so a column of full-dot Braille cells
flows as a single uniform grid with no visible row break.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous radius (0.07em) gave a 14% dot diameter relative to the 1em
cell height, leaving an 11% visible gap between adjacent dots
vertically (with the new 25% uniform center-to-center spacing from
the prior commit).

Bumped radius to 0.125em (= 25% diameter, exactly the center-to-
center spacing) so adjacent dots just touch vertically. The 0.01em
soft-transition zone is preserved (transparent transition at 0.135em).

Visual effect: a column of full-dot Braille cells now reads as a
solid vertical strip of touching dots, with no visible row-break
stripe. Horizontal spacing between left and right column dots is
unchanged (still 50% center-to-center), so dots in a row remain
visually distinct as paired left/right columns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revert the radius bump from a485679 (user wants normal-sized dots,
not bigger ones) and instead shift dot positions further apart
vertically — pushing the top and bottom rows close to cell edges.

New positions: y = 5%, 35%, 65%, 95%
  - intra-cell row spacing: 30% per gap
  - inter-cell row spacing: (100-95) + 5 = 10% per gap

Intra-cell is now wider but each cell still has dots. Inter-cell is
much narrower so the "empty stripe" between rows of cells visually
shrinks. Spacing is no longer perfectly uniform, but the visible row-
break gap is reduced from 25% → 10%, which is the actual perceived
issue ("the inter-cell area looks emptier than the intra-cell area").

Dot radius stays at 0.07em (no bigger dots).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous edge-near positions (5/35/65/95) reduced the inter-cell gap
to ~1.66px but still left a visible thin stripe between rows because
the painted dots didn't quite reach the cell boundary.

Pinning the top and bottom rows at y=0% and y=100% places the dot
centres exactly on the cell edges. The radial-gradient only paints
within the inline-block's bounds, so a y=0% dot shows just its bottom
half, and a y=100% dot shows just its top half. When two cells stack
vertically, the top-half from cell N+1's top dot and the bottom-half
from cell N's bottom dot share the cell boundary line and together
form one complete dot straddling the join — no visible gap between
rows by construction.

Intra-cell spacing grows from 30% to 33.33% per gap; visually the
in-cell dot grid is now perfectly evenly spaced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cells previously had uniform 50% horizontal dot spacing both within
and between cells, making adjacent Braille glyphs blur together
horizontally. Moving the dot columns inward to x=27%/73% makes:

  - intra-cell horizontal spacing: 46%
  - inter-cell horizontal spacing: 54%

The 8% asymmetry produces a faintly perceptible cell-boundary so
adjacent Braille characters can be distinguished without obvious gaps
that would break the contiguous-grid feel.

Cell width stays at 1ch so Braille remains aligned with surrounding
text. Only the dot positions within each cell change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_parse_header_color() accepted both 3-hex and 6-hex values for
#!bg=/#!fg= headers, inconsistent with the 3-hex-only inline
`Fxxx`/`Bxxx` colour tokens. Also removes README and example
references to the `FTxxxxxx`/`BTxxxxxx` 24-bit inline extension,
which was actually dropped from the parser back in v1.0.2.
Dev's tip was going unverified between pushes and promotion to
main since ci.yml only triggered on main push/PR. security.yml
stays main+PR-only per the dev-branch workflow's guidance that
heavier scans can wait for the promotion gate.
Fetched and read NomadNet's own Guide.py (in-app spec) and
MicronParser.py (reference implementation) directly from the
markqvist/NomadNet repo to re-audit every "MeshChat parity" claim in
the codebase against the real target: rendering Micron the way actual
NomadNet renders it, not just matching one third-party client.

Behavior fixes (both narrow, matching NomadNet exactly):
- Dividers only get a custom repeat-character/double-rule treatment
  when the line is exactly "-" + one more char, matching
  MicronParser.py's `len(line) == 2` check. `---`/`-==` now correctly
  fall back to the default rule instead of being misread as styled.
- Page-level #!bg=/#!fg= headers accept both 3-hex and 6-hex again
  (Guide.py doesn't restrict header length the way it restricts the
  inline `Fxxx`/`Bxxx` tags) — reverts an over-correction from earlier
  this session that assumed headers should match inline's 3-hex-only
  rule.

Docs corrections (README, CHANGELOG, code comments):
- Renamed "Dynamic include" to "Partials" (NomadNet's own term) and
  documented what's actually discarded (refresh/fields/pid) instead
  of implying they're used.
- Documented pipe-separated link field-specs as the real primary
  syntax; the existing backtick-joining leniency is MeshChat/
  Micron2HTML-only and kept but no longer presented as canonical.
- Documented NomadNet's own canonical checkbox/radio style (empty
  field label + trailing text) alongside the already-documented
  embedded-label form.
- Fixed broken checkbox/radio examples in README and
  examples/showcase.mu (missing the mandatory backtick separator).
- Fixed inline colour doubling example (was missing the command
  letter: "F40" -> "`FF40").
- Fixed incorrect file:// wording in the Security section (the code
  blocks a `/file/` path segment, not the file:// URI scheme).
- Fixed stale "pure Python 3.9+" claim (>=3.10 since v1.0.8).
- Added README "Known limitations" section: tables and anchors are
  unimplemented; alignment-anywhere and inline literal-toggle are
  documented as intentional supersets of NomadNet's line-scoped
  versions.
- Documented to_text()/--format text, previously untested-in-docs.

Test coverage:
- 2 new divider tests locking in the length-2 rule.
- 1 new header-color test confirming 6-hex support.
- Renamed TestDynamicInclude -> TestPartials; added a test confirming
  fields/pid are discarded, matching corrected docs.
- Relabeled "MeshChat parity" comments to "NomadNet" throughout
  converter.py and tests where the behavior is independently verified
  against MicronParser.py, without changing behavior.

57 tests passing (was 54).
Reverts the header 6-hex acceptance added earlier this session.
NomadNet's own docs would technically permit 6-hex there, but with
no marker distinguishing 3-hex from 6-hex (unlike the inline tags,
which would need something like NomadNet's `T` prefix), allowing
both means a value's meaning silently depends on its length. One
fixed width, applied consistently to every colour value in this
converter, beats matching every length NomadNet's own code happens
to tolerate.
…ences

Full NomadNet parity pass: implements the two previously-unimplemented
real NomadNet features (tables, anchors) and reverses several
Micron2HTML-only leniencies/extensions that had no equivalent in real
NomadNet, so a page renders the same way in both directions.

New features:
- Tables (`t): rendered as box-drawing-character ASCII art (matching
  NomadNet's actual visual output, not a semantic <table>), ported from
  RNS's MarkdownToMicron.format_table_raw algorithm. Supports header/
  alignment-separator/data rows, per-column alignment, min column width,
  optional `tc30-style whole-table alignment + max-width, escaped pipes.
- Anchors: heading auto-anchors (slugified text -> id=), explicit `:name
  declarations (zero-width, first-wins shared namespace), `[label`#name]
  named jumps, and bare `[label`#] "jump to next heading" links (resolved
  via a one-time forward/backward pre-pass in convert()).

Behavior changes (reversing MeshChat-only leniencies, matching NomadNet
exactly):
- Removed the inline mid-line `=...`= literal toggle - NomadNet only
  recognizes `= as a whole-line block toggle. This was an undocumented
  Micron2HTML extension with 2 tests exercising it; both rewritten.
- Link field-specs with more than 3 backtick-separated segments now
  render nothing at all, reversing a v1.0.3 fix that was solving a
  MeshChat-specific quirk rather than real NomadNet behavior. The
  correct pipe-separated form (`[label`url`a=1|b=2]) is unaffected.
- Empty heading lines now emit nothing (no row), matching NomadNet's
  parse_line() returning None - reverses a deliberate deviation made
  earlier this session, superseded by the broader parity goal.

Partials (`{url`refresh`fields}) now expose refresh/fields/pid as
data-refresh/data-fields/data-pid attributes instead of discarding them,
so a consuming app can wire up its own live-refresh behavior - still no
JS shipped by this library.

Also corrected a factually wrong README claim: "alignment tags must
appear at line start" was never an enforced NomadNet parser rule, just
Guide.py's style advice to authors - Micron2HTML already matched real
NomadNet's permissive behavior here.

Test suite: 57 -> 90 (33 new/rewritten tests). README gains Tables and
Anchors sections and an updated Known Limitations list (wcwidth and
table-shrink-formula approximations, partials live-refresh gap, a rare
anchor-collision edge case in the bare-hash pre-pass).
_close_innermost()/_pop_tag() found and removed a tag from the middle
of the open-tag stack when it wasn't actually innermost, but still
emitted the close tag at the current output position — HTML closes are
LIFO, so that close landed on whatever tag genuinely was innermost
instead, desyncing the bookkeeping from the real DOM for the rest of
the line (e.g. `B777 X`f `F975`b <> left the background open until
end-of-line instead of closing at `b).

Fixes by unwinding: close every tag above the target (innermost-first),
close the target, then reopen the unwound tags as fresh elements. The
tag stack now carries each entry's open HTML alongside its close HTML
so the reopens can be replayed. `!/`_/`* now route through
_close_innermost() like `b/`f always did, instead of inlining their
own close+pop; _pop_tag() is removed as dead code.

Reported against a downstream consumer (vscode-mu-preview) as
"background color not resetting after `b token" — confirmed the bug
was entirely upstream here. 4 new tests in TestTagNesting.
NomadNet's reference MicronParser.py accepts an FT<6hex> /
BT<6hex> form for exact 24-bit color. Micron2HTML dropped this
in an earlier version citing MeshChat compatibility — MeshChat's
MicronParser.js doesn't render it, so pages relying on the T
escape look wrong there. Re-added now for feature parity with
NomadNet's own parser; portability trade-off is documented in
the docstring and CHANGELOG so authors know when it's safe to
use vs when to stick to `Fxxx / `Bxxx.

Invalid or too-short input consumes T + 6 chars so garbage
doesn't leak as text, matching the 3-hex parser's
"always consume, apply only when valid" convention. T-prefix
branch checks BEFORE the 3-hex branch so FTrrggbb isn't
mis-consumed as FT? + garbage.

Test: existing test_24bit_T_format_not_supported flipped to
test_24bit_T_format_fg (now asserts the color IS applied);
added tests for the BG form, invalid-hex consumption, and the
too-short (`FTab at EOF) edge case. All 97 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
NomadNet's reference MicronParser.py accepts this form; Micron2HTML
had dropped it in an earlier version citing MeshChat compat. Users
authoring for Micron2HTML-based clients can now use exact colors;
docstring + CHANGELOG note the portability trade-off explicitly
so authors know when it's safe.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same phantom-conflict pattern the NomadPortal release cycle hits
(see [[rebase-merge-phantom-conflicts]] memory). main had the
squashed v1.1.0 commit but dev still carried its individual
pre-1.1.0 history. Merged with -X ours so dev's content wins on
the phantom-conflict files; CHANGELOG needed a manual dedupe pass
to drop the duplicated [1.1.0] section the merge produced.
@JamesM92
JamesM92 merged commit 2be0d4e into main Aug 9, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant