Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ae9c07e
Move Braille dots to uniform 1/8 vertical intervals
JamesM92 May 16, 2026
a485679
Increase Braille dot radius to close vertical gap between rows
JamesM92 May 16, 2026
8183d3a
Move Braille dot positions to cell edges (5/35/65/95)
JamesM92 May 16, 2026
93cc8b6
Pin Braille dots at cell edges (0/33.33/66.67/100) to eliminate gap
JamesM92 May 16, 2026
3a4e548
Revert "Pin Braille dots at cell edges (0/33.33/66.67/100) to elimina…
JamesM92 May 16, 2026
ef9144a
Inset Braille dots horizontally (25/75 → 27/73) for subtle char boundary
JamesM92 May 16, 2026
628c506
fix: restrict page-header colours to 3-hex, drop stale 6-hex docs
JamesM92 Jul 28, 2026
b91906c
Merge remote-tracking branch 'origin/main' into dev
JamesM92 Jul 28, 2026
77230f6
ci: run test/build workflow on dev pushes too
JamesM92 Jul 28, 2026
9e87697
docs+fix: correct project goal from MeshChat parity to NomadNet parity
JamesM92 Jul 28, 2026
2b67fd1
fix: keep page-header colours 3-hex-only, don't chase 6-hex
JamesM92 Jul 28, 2026
37ff3e3
feat: implement tables and anchors, tighten remaining NomadNet diverg…
JamesM92 Jul 28, 2026
bf765b3
fix: correct out-of-order tag closes producing mismatched HTML nesting
JamesM92 Jul 30, 2026
932f8b6
v1.1.0: full NomadNet parity pass, tables/anchors, tag-nesting fix
JamesM92 Jul 30, 2026
8feadd7
Merge remote-tracking branch 'origin/main' into dev
JamesM92 Jul 30, 2026
f275e8c
feat: restore FT<6hex> / BT<6hex> 24-bit color parser
JamesM92 Aug 9, 2026
a69fdb7
v1.1.1: restore FT<6hex> / BT<6hex> 24-bit color parser
JamesM92 Aug 9, 2026
2830fc9
merge: sync Micron2HTML dev with squashed main to unblock v1.1.1 PR
JamesM92 Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@
All notable changes to Micron2HTML are documented in this file.
The format follows [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [1.1.1] - 2026-08-09

### Added

- **`` `FT<6hex> `` / `` `BT<6hex> `` — 24-bit exact-color escape from
NomadNet's reference parser** (e.g. `` `FT8b4513 `` for saddle-brown
foreground). Re-added after being dropped in earlier versions. Not
portable — MeshChat's `MicronParser.js` doesn't render this form —
so pages targeting cross-client compatibility should still use the
standard `` `Fxxx `` 3-hex shorthand. Intended for authors who need
exact colors on Micron2HTML-based renderers and are willing to
accept the portability cost.

Invalid or too-short input consumes the full `` T + 6 `` chars so
garbage doesn't leak as text, matching the 3-hex parser's
"always consume, apply only when valid" convention. 3-hex remains
the default and always-portable form; the T-prefix is checked
first so `` `FTrrggbb `` isn't mis-consumed as `` `FT?? ``.

## [1.1.0] - 2026-07-30

### Changed — goal correction: MeshChat parity → NomadNet parity
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .converter import MicronConverter, default_url_resolver, UrlResolver, slugify_micron

__all__ = ["MicronConverter", "default_url_resolver", "UrlResolver", "slugify_micron"]
__version__ = "1.1.0"
__version__ = "1.1.1"
44 changes: 29 additions & 15 deletions converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,26 +750,40 @@ def _parse_inline(self, text: str, node_hash: str, base_path: str,
def _parse_color(self, text: str, i: int, n: int):
"""Parse a Micron color token after the F/B prefix.

Format: 3 hex chars — each nibble doubled (f→ff, 8→88, 0→00).
Two forms:

1. **3 hex chars** — each nibble doubled (f→ff, 8→88, 0→00).
This is the canonical form and the one MeshChat +
every mainstream .mu page uses.
2. **``T`` + 6 hex chars** — a 24-bit exact-color extension
from NomadNet's reference parser (e.g. ``FTrrggbb``).
Not portable — MeshChat doesn't render it; pages relying
on it will look wrong on MeshChat. Supported here for
feature parity with NomadNet's parser and for authors who
need exact colors on Micron2HTML-based clients, but
actively discouraged in documentation.

NomadNet parity notes:
- Always consume the next 3 chars after F/B (if available),
regardless of whether they're valid hex. Both MicronParser.py
(`line[i+1:i+4]` + `skip = 3`) and MeshChat's MicronParser.js
(`line.substr(i+1,3)` + `skip = 3`) do this unconditionally.
- If the 3 chars aren't valid hex, no colour is applied (the
colour-state holds the invalid string; rendering ignores it),
but the 3 chars are still consumed so they don't leak as text.
- NomadNet's *reference parser* also accepts a `T<6hex>` 24-bit
form (`FTrrggbb`) — but its own Guide.py never teaches this to
page authors, and MeshChat doesn't implement it either. Since
the point of this converter is to render what real `.mu` pages
actually contain, not every corner of one client's source, we
deliberately stay 3-hex-only: any real-world page relying on
`FT<6hex>` wouldn't render correctly in MeshChat anyway.
- Always consume the next 3 (or ``T`` + 6) chars after F/B
if available, regardless of whether they're valid hex.
Both NomadNet's MicronParser.py and MeshChat's
MicronParser.js do this unconditionally so garbage
doesn't leak into rendered text.
- If the chars aren't valid hex, no colour is applied
(the colour-state holds the invalid string; rendering
ignores it).

Returns (css_color_str | None, new_index).
"""
# T<6hex> exact-color extension — check BEFORE the 3-hex path
# so a `FTrrggbb` isn't mis-consumed as `T` + garbage.
if i < n and text[i] == "T" and i + 7 <= n:
h6 = text[i + 1:i + 7]
if all(c in _HEX for c in h6):
return "#" + h6.lower(), i + 7
# Invalid 6-hex — still consume ``T`` + 6 chars so they
# don't leak into rendered text.
return None, i + 7
if i + 3 <= n:
h3 = text[i:i + 3]
if all(c in _HEX for c in h3):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "Micron2HTML"
version = "1.1.0"
version = "1.1.1"
description = "Convert Micron markup (NomadNet) to HTML"
readme = "README.md"
license = { text = "MIT" }
Expand Down
40 changes: 32 additions & 8 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,16 +242,40 @@ def test_invalid_hex_consumes_three_chars(self, conv):
assert "xxx" not in out
assert "hello" in out

def test_24bit_T_format_not_supported(self, conv):
# NomadNet's own reference parser DOES accept `FT<6hex>`, but its
# Guide.py never teaches it and MeshChat doesn't implement it — we
# stay 3-hex-only to match what real page authors actually write.
# The `T` plus 2 next chars are consumed as a (failed) 3-char hex,
# leaving the tail as visible text.
def test_24bit_T_format_fg(self, conv):
# NomadNet's reference parser accepts `FT<6hex>` for exact
# 24-bit color. Re-added for feature parity — MeshChat still
# doesn't render this form, so it's actively discouraged in
# authoring docs, but the parser handles it correctly.
out = conv.convert("`FT8b4513 brown`f")
assert "color:#8b4513" not in out
assert "color:#" not in out # no colour applied
assert "color:#8b4513" in out
assert "brown" in out
# `T + 6 hex` (7 chars) must be fully consumed — nothing leaks
assert "8b4513" not in out.split(">")[-1] # no leak into text

def test_24bit_T_format_bg(self, conv):
# Same behaviour on the background side.
out = conv.convert("`BT2c3e50 dark`b")
assert "background-color:#2c3e50" in out
assert "dark" in out

def test_24bit_T_format_invalid_hex_consumes_all_seven(self, conv):
# Invalid 6-hex after `T` still consumes all 7 chars (the T
# plus the six that follow) so garbage doesn't leak as text.
# Matches the 3-hex parser's "always consume, apply only
# when valid" convention.
out = conv.convert("`FTzzzzzz visible`f")
assert "color:#" not in out
assert "zzzzzz" not in out
assert "visible" in out

def test_24bit_T_format_falls_back_when_too_short(self, conv):
# `FT` with fewer than 6 chars after is treated as a normal
# 3-hex attempt on `T??` — invalid, consumes 3 chars.
# Guards against a `FTab` string near end-of-input crashing
# or being interpreted as a partial exact-color escape.
out = conv.convert("`FTab")
assert "color:#" not in out

def test_header_fg_bg_3digit(self, conv):
out = conv.convert("#!bg=333\n#!fg=aaa\nhello")
Expand Down