diff --git a/app.py b/app.py index 7d0d491..d4e8f83 100644 --- a/app.py +++ b/app.py @@ -1089,10 +1089,27 @@ def _ranked(words, exclude: set[str], limit: int) -> list[dict]: out = [] for z, w in scored[:limit]: ph = phones_for(w) - out.append({"word": w, "syl": pronouncing.syllable_count(ph) if ph else 0}) + out.append({"word": w, "z": round(z, 1), + "syl": pronouncing.syllable_count(ph) if ph else 0}) return out +_homophone_index: dict[str, list[str]] | None = None + + +def get_homophones(w: str, phones: str) -> list[str]: + global _homophone_index + if _homophone_index is None: + pronouncing.init_cmu() + idx: dict[str, list[str]] = defaultdict(list) + for word, ph in pronouncing.pronunciations: + if word.isalpha() and zipf_frequency(word, "en") >= 3.0: + idx[DIGITS.sub("", _norm_r(ph))].append(word) + _homophone_index = idx + return [h for h in _homophone_index.get(DIGITS.sub("", phones), []) + if h != w][:6] + + @app.get("/api/word") def word_info(word: str): """Phonetic anatomy of a word: phones, syllables, stress, rime.""" @@ -1107,6 +1124,7 @@ def word_info(word: str): return {"word": w, "known": True, "phones": DIGITS.sub("", phones), "syl": len(stress), "stress": stress, "rime": rime, + "homophones": get_homophones(w, phones), "zipf": round(zipf_frequency(w, "en"), 1)} diff --git a/static/index.html b/static/index.html index 970e1f0..7ad7979 100644 --- a/static/index.html +++ b/static/index.html @@ -4,7 +4,7 @@ RhymePad — a scratchpad for poets & rappers - + @@ -198,18 +198,11 @@ border-radius: 10px; padding: 12px 14px; margin: 10px 0 4px; } .defhead { display: flex; align-items: baseline; gap: 8px; font-size: 14px; } - .defhead .phon { color: var(--ink-dim); font-size: 12px; } .defx { margin-left: auto; cursor: pointer; color: var(--ink-dim); font-size: 15px; } .defx:hover { color: var(--r6); } - .defs { margin: 8px 0 10px; padding-left: 18px; font-size: 13px; line-height: 1.55; } - .defs li { margin: 4px 0; } - .defs i { - color: var(--accent-2); font-style: normal; font-size: 11px; - text-transform: uppercase; letter-spacing: .06em; margin-right: 4px; - } .btn.small { padding: 5px 10px; font-size: 12px; } .def-actions { display: flex; gap: 8px; } - .defphon { font-size: 12px; margin: 0 0 10px; line-height: 1.6; } + .defphon { font-size: 12px; margin: 8px 0 10px; line-height: 1.6; } .defphon i { color: var(--accent-2); font-style: normal; } .chip { font-size: 13px; background: var(--panel-2); border: 1px solid var(--line); @@ -217,6 +210,8 @@ transition: all .12s; } .chip:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); } + .chip.common { font-weight: 600; } + .chip.rare { color: var(--ink-dim); opacity: .75; } .chip.near { border-style: dashed; color: var(--ink-dim); } .chip.near:hover { color: var(--accent); } .muted { color: var(--ink-dim); font-size: 13px; line-height: 1.6; } @@ -286,7 +281,7 @@ Double-click any word to look it up on the right.">