mirror of
https://github.com/kennethreitz/rhymepad.org.git
synced 2026-06-11 17:08:33 +00:00
Mosaic triple rhymes + UI truncation
Three-word vowel mosaics (mean to it / seen do it / theme music) now group, gated hard: the mosaic must draw a full vowel beyond its anchor word, and all-phrase buckets mirroring the same two word groups stay suppressed. Scheme readout truncates at 16 letters; draft tabs cap at 170px with ellipsis. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -448,6 +448,40 @@ def analyze(draft: Draft):
|
||||
or b["word"].lower() in STOPWORDS),
|
||||
})
|
||||
|
||||
# mosaic triples: three-word runs whose vowel run is the rhyme —
|
||||
# "mean to it" / "seen do it" / "theme music" (IY UW x). Anchor must
|
||||
# carry content; the tail words may be anything pronounceable.
|
||||
for toks in line_toks.values():
|
||||
for a, b, c in zip(toks, toks[1:], toks[2:]):
|
||||
if a["word"].lower() in STOPWORDS:
|
||||
continue
|
||||
pa, pb, pc = (phones_for(a["word"]), phones_for(b["word"]),
|
||||
phones_for(c["word"]))
|
||||
if not (pa and pb and pc):
|
||||
continue
|
||||
tail_vowels = _all_vowels(pb) + _all_vowels(pc)
|
||||
if not any(v not in REDUCED for v in tail_vowels):
|
||||
continue # the mosaic must span words: "mean TO it" does,
|
||||
# "methamphetamine with the" is just its anchor
|
||||
vowels = _tail_vowels(pa) + tail_vowels
|
||||
if len(vowels) < 3:
|
||||
continue
|
||||
pl = pa.split()
|
||||
start = 0
|
||||
for i in range(len(pl) - 1, -1, -1):
|
||||
if pl[i][-1] in "12":
|
||||
start = i
|
||||
break
|
||||
phrases.append({
|
||||
"line": a["line"], "start": a["start"], "end": c["end"],
|
||||
"word": " ".join(w["word"].lower() for w in (a, b, c)),
|
||||
"is_end": c["is_end"], "sid": a["sid"], "gid": None,
|
||||
"slant": False, "vowels": vowels,
|
||||
"rime": DIGITS.sub(
|
||||
"", " ".join(pl[start:] + pb.split() + pc.split())),
|
||||
"weak": False,
|
||||
})
|
||||
|
||||
# pass 1: perfect rhymes (shared rime), anywhere in a line — this is
|
||||
# what catches internal rhymes. Phrases compete too, so "stir up"
|
||||
# perfect-rhymes "syrup" even while its "up" rhymes with "cup".
|
||||
@@ -573,11 +607,17 @@ def analyze(draft: Draft):
|
||||
key = _m2_key(p["rime"].split())
|
||||
if not key:
|
||||
continue
|
||||
if p["word"].count(" ") == 2:
|
||||
# mosaic triples must carry at least two full vowels —
|
||||
# anchor + schwa-tails ("Sleep is the") prove nothing
|
||||
if sum(v != "x" for v in key[2:].split()) < 2:
|
||||
continue
|
||||
spans = grouped_spans[p["line"]]
|
||||
a_gi = next((gi for s, e, gi in spans if s <= p["start"] < e), None)
|
||||
b_gi = next((gi for s, e, gi in spans if s < p["end"] <= e), None)
|
||||
p["halves"] = (a_gi, b_gi)
|
||||
if a_gi is not None and b_gi is not None:
|
||||
# both halves already rhyme — the phrase only matters if it
|
||||
# both ends already rhyme — the phrase only matters if it
|
||||
# ties into a THIRD family (four-inch joining the orange/
|
||||
# storage clan while four sits with door and inch with hinge)
|
||||
gi = group_by_multi.get((p["sid"], key))
|
||||
@@ -585,6 +625,10 @@ def analyze(draft: Draft):
|
||||
raw_groups[gi]["toks"].append(p)
|
||||
p["slant"] = True
|
||||
grouped.add(id(p))
|
||||
else:
|
||||
# or if it can seed a family with non-mirror siblings
|
||||
# (mean to it / seen do it / theme music)
|
||||
by_multi[(p["sid"], key)].append(p)
|
||||
continue
|
||||
attach_or_collect(p, key, by_multi, group_by_multi)
|
||||
|
||||
@@ -594,9 +638,17 @@ def analyze(draft: Draft):
|
||||
for (sid, key), toks in sorted(by_multi.items(),
|
||||
key=lambda kv: (-len(kv[1]), kv[0][1])):
|
||||
toks = [t for t in toks if id(t) not in grouped]
|
||||
if len(toks) >= 2 and len({t["word"].split()[0] for t in toks}) >= 2:
|
||||
raw_groups.append({"toks": toks, "slant": True, "key": key})
|
||||
grouped.update(id(t) for t in toks)
|
||||
if len(toks) < 2 or len({t["word"].split()[0] for t in toks}) < 2:
|
||||
continue
|
||||
# an all-phrase bucket whose members mirror the same two word
|
||||
# groups is pure redundancy (oh my / go rhyme over oh+go, my+rhyme)
|
||||
halves = {t.get("halves") for t in toks}
|
||||
if (len(halves) == 1
|
||||
and None not in halves
|
||||
and None not in next(iter(halves))):
|
||||
continue
|
||||
raw_groups.append({"toks": toks, "slant": True, "key": key})
|
||||
grouped.update(id(t) for t in toks)
|
||||
|
||||
# pass 4: consonance-aware slant anywhere in a line — last stressed
|
||||
# vowel + first coda consonant, so bliss / whisps / exist (IH S) group
|
||||
|
||||
+8
-3
@@ -65,11 +65,12 @@
|
||||
.drafts-bar { display: flex; gap: 6px; overflow-x: auto; flex: none; padding-bottom: 2px; }
|
||||
.dtab {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 12px; padding: 6px 12px;
|
||||
font-size: 12px; padding: 6px 12px; max-width: 170px;
|
||||
background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
|
||||
color: var(--ink-dim); cursor: pointer; white-space: nowrap;
|
||||
transition: all .15s; user-select: none;
|
||||
transition: all .15s; user-select: none; flex: none;
|
||||
}
|
||||
.dtab .dtitle { overflow: hidden; text-overflow: ellipsis; }
|
||||
.dtab:hover { color: var(--ink); }
|
||||
.dtab.active { color: var(--ink); border-color: var(--accent); background: rgba(232,129,74,0.08); }
|
||||
.dtab .x { color: var(--ink-dim); font-size: 13px; line-height: 1; }
|
||||
@@ -511,7 +512,11 @@ function buildReadout(){
|
||||
parts.push(p);
|
||||
}
|
||||
const st = caretStanza();
|
||||
if(st && st.scheme) parts.push(`scheme <b>${st.scheme.toUpperCase().split('').join(' ')}</b>`);
|
||||
if(st && st.scheme){
|
||||
const sch = st.scheme.toUpperCase();
|
||||
const shown = sch.slice(0, 16).split('').join(' ') + (sch.length > 16 ? ' …' : '');
|
||||
parts.push(`scheme <b>${shown}</b>`);
|
||||
}
|
||||
schemeReadout.innerHTML = parts.join(' ');
|
||||
}
|
||||
|
||||
|
||||
@@ -366,3 +366,12 @@ def test_the_full_orange_verse():
|
||||
"four-inch", "door hinge")
|
||||
group_with(text, "four", "door", "george")
|
||||
group_with(text, "inch", "hinge")
|
||||
|
||||
|
||||
def test_mosaic_triples_kanye_power():
|
||||
text = ("I'm living in that 21st century\n"
|
||||
"Doing something mean to it\n"
|
||||
"Do it better than anybody you ever seen do it\n"
|
||||
"Screams from the haters, got a nice ring to it\n"
|
||||
"I guess every superhero need his theme music")
|
||||
group_with(text, "mean to it", "seen do it", "theme music")
|
||||
|
||||
Reference in New Issue
Block a user