diff --git a/app.py b/app.py index 5f17d8b..eb48fdf 100644 --- a/app.py +++ b/app.py @@ -547,6 +547,31 @@ def analyze(draft: Draft): raw_groups[gi]["toks"].append(p) grouped.add(id(p)) + # fuse groups that carry the same vowel family — a perfect subgroup + # (shoulder/older/colder) shouldn't split colors with the slant family + # it lives inside (soldier/holster/coaster). Perfect members keep the + # strong styling; the slant side keeps per-token slant marks. + by_family: dict[str, dict] = {} + fused: list[dict] = [] + for g in raw_groups: + mk = founding_projections(g["key"]).get("multi") + tgt = by_family.get(mk) if mk else None + if tgt is None: + if mk: + by_family[mk] = g + fused.append(g) + continue + if g["slant"]: + for t in g["toks"]: + t["slant"] = True + elif tgt["slant"]: + for t in tgt["toks"]: + t["slant"] = True + tgt["slant"] = False + tgt["key"] = g["key"] + tgt["toks"].extend(g["toks"]) + raw_groups = fused + # stable colors: order groups by first appearance raw_groups.sort(key=lambda g: min((t["line"], t["start"]) for t in g["toks"])) groups_out = [] diff --git a/tests/test_rhymes.py b/tests/test_rhymes.py index 6c4bd23..0a478e6 100644 --- a/tests/test_rhymes.py +++ b/tests/test_rhymes.py @@ -281,3 +281,19 @@ def test_annotation_lines_ignored(): (st,) = res["stanzas"] assert st["lines"] == [1, 2, 5, 6] assert st["scheme"] == "aabb" + + +def test_perfect_subgroup_fuses_with_slant_family(): + # shoulder/older/colder (perfect, OW L D ER) live inside the bigger + # OW-schwa family — one color for the whole column + text = ("Skunk, bug, soldier\n" + "Tongue, shrub, shoulder\n" + "One month older\n" + "Sponge, mob, colder\n" + "Nun, rug, holster\n" + "Lug nut, coaster\n" + "Lung, jug, roaster\n" + "Young Thug poster\n" + "Unplugged toaster") + group_with(text, "soldier", "shoulder", "older", "colder", "holster", + "coaster", "roaster", "poster", "toaster")