1
Fork 0
dictionary-lookup/app.js
Leonardo Devai 1c7f139569 Review and modernize all 42 projects to the updated standard
Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24,
Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root
runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone
line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login,
self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the
blog series, safe markdown links in the frontends, and many smaller bugs — every
project was built, run and exercised end to end.

Adds scripts/publish.sh + a CI publish job that splits every folder into its own
repo at git.devai.io/templates/<folder>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G
2026-09-27 21:10:38 +02:00

105 lines
3.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const form = document.getElementById("form");
const input = document.getElementById("word");
const entry = document.getElementById("entry");
const statusEl = document.getElementById("status");
const meaningsEl = document.getElementById("meanings");
async function lookup(word) {
statusEl.textContent = "Loading…";
entry.hidden = true;
try {
// Free Dictionary API: English Wiktionary as JSON, no key needed.
const res = await fetch(
`https://freedictionaryapi.com/api/v1/entries/en/${encodeURIComponent(word)}`,
);
if (res.status === 429) {
statusEl.textContent = "Too many lookups this hour — try again later.";
return;
}
if (!res.ok) throw new Error(`Dictionary API returned ${res.status}`);
const data = await res.json();
// An unknown word still answers 200 — just with an empty entries array.
if (data.entries.length === 0) {
statusEl.textContent = `No definition found for "${word}".`;
return;
}
render(data);
statusEl.textContent = "";
} catch (err) {
statusEl.textContent = "Something went wrong. Try again in a moment.";
console.error(err);
}
}
// The answer is NESTED: arrays inside objects inside arrays. Three levels deep.
//
// data = {
// word: "curious",
// entries: [ // level 1: one per part of speech
// {
// partOfSpeech: "adjective",
// pronunciations: [{ type: "ipa", text: "/ˈkjʊə.ɹi.əs/" }],
// senses: [ // level 2: one per meaning
// {
// definition: "Tending to ask questions…",
// examples: ["Young children are naturally curious…"], // level 3
// },
// ],
// },
// ],
// }
//
// To reach the data, walk down one loop per level.
function render(data) {
document.getElementById("word-title").textContent = data.word;
// Pronunciations are optional: take the first IPA one from any entry, if any.
const ipa = data.entries
.flatMap((e) => e.pronunciations || [])
.find((p) => p.type === "ipa");
document.getElementById("phonetic").textContent = ipa ? ipa.text : "";
meaningsEl.replaceChildren();
for (const item of data.entries) {
const section = document.createElement("section");
section.className = "meaning";
const pos = document.createElement("h3");
pos.className = "pos";
pos.textContent = item.partOfSpeech;
section.appendChild(pos);
const list = document.createElement("ol");
list.className = "definitions";
for (const sense of item.senses) {
const li = document.createElement("li");
li.textContent = sense.definition;
// examples is optional too — show the first one when there is one.
const example = sense.examples?.[0];
if (example) {
const ex = document.createElement("p");
ex.className = "example";
ex.textContent = `“${example}”`;
li.appendChild(ex);
}
list.appendChild(li);
}
section.appendChild(list);
meaningsEl.appendChild(section);
}
entry.hidden = false;
}
form.addEventListener("submit", (event) => {
event.preventDefault();
const word = input.value.trim();
if (word) lookup(word);
});
lookup("curious");