From 71c39db5db5007a3a9e0bcd5de3134620761b55c Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 8 Sep 2026 23:03:30 +0200 Subject: [PATCH] El glosario del taller, con su globo en el texto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lo que se pidió: «cada vez que aparezca un término del glosario en el texto, estará marcado para visualizarse con un popup con la explicación». Veintinueve términos con acepciones numeradas —«clave» es el patrón rítmico y el instrumento de percusión, y con una sola definición una de las dos sería falsa—, más las formas con las que cada palabra aparece de verdad: plurales, femeninos, con tilde y sin ella. El marcado va sobre el HTML ya compuesto y no sobre el Markdown: antes habría que esquivar la sintaxis, y un término dentro de un enlace o de un bloque de código es justo donde un reemplazo de texto se equivoca en silencio. Se marca una vez por término y por apartado, no en cada aparición: nueve «compás» subrayados no hay quien los lea. La pieza tiene más casos raros que cualquier otra del sitio y ninguno se ve mirando la página —el fallo es que un globo no salga, y nadie echa en falta lo que no sabe que existía—, así que va separada de la base y con doce pruebas. Dos las escribí creyendo que ya pasaban: - Marcaba «sinalefas» del final del párrafo en vez de la «sinalefa» de la primera línea, porque probaba forma por forma en orden de longitud. - Con las formas sin ordenar marcaba «rima» y no «rima asonante». Salía bien solo porque la consulta las devolvía ordenadas; ahora el desempate está en la función, que no puede depender de cómo se la llame. El globo es un botón y no un `title`: un tooltip nativo no se abre con teclado y en un móvil no existe. Se cierra con Escape devolviendo el foco, y se coloca debajo del término o encima si no cabe, porque tapar lo que se acaba de pulsar es lo peor que podría hacer. El glosario entero tiene además su página, donde sí cabe la ampliación que en un globo taparía el texto. Co-Authored-By: Claude Opus 5 (1M context) --- e2e/taller.e2e.ts | 81 +++++ package.json | 1 + scripts/db/sembrar-glosario.mjs | 102 ++++++ scripts/db/semilla-glosario.json | 376 +++++++++++++++++++++ src/app.css | 28 ++ src/lib/components/GlobosDeGlosario.svelte | 234 +++++++++++++ src/lib/components/IndiceTaller.svelte | 18 + src/lib/server/glosario.spec.ts | 108 ++++++ src/lib/server/glosario.ts | 220 ++++++++++++ src/lib/server/taller.ts | 22 +- src/routes/taller/[slug]/+page.svelte | 12 +- src/routes/taller/glosario/+page.server.ts | 5 + src/routes/taller/glosario/+page.svelte | 189 +++++++++++ 13 files changed, 1394 insertions(+), 2 deletions(-) create mode 100644 scripts/db/sembrar-glosario.mjs create mode 100644 scripts/db/semilla-glosario.json create mode 100644 src/lib/components/GlobosDeGlosario.svelte create mode 100644 src/lib/server/glosario.spec.ts create mode 100644 src/lib/server/glosario.ts create mode 100644 src/routes/taller/glosario/+page.server.ts create mode 100644 src/routes/taller/glosario/+page.svelte diff --git a/e2e/taller.e2e.ts b/e2e/taller.e2e.ts index 9442c8e..c2b7faa 100644 --- a/e2e/taller.e2e.ts +++ b/e2e/taller.e2e.ts @@ -271,6 +271,87 @@ test.describe('Taller de letras', () => { await expect(aviso).toContainText('versos'); }); + /* --- El glosario ------------------------------------------------------- */ + + test('los términos del glosario salen marcados en el texto', async ({ page }) => { + // Lo que se pidió: «cada vez que aparezca un término del glosario en el + // texto, estará marcado para visualizarse con un popup». + await page.goto('/taller/son-cubano'); + + const marcados = page.locator('.texto .termino'); + await expect(marcados.first()).toBeVisible(); + + // Cada término una sola vez por apartado, no en cada aparición: un texto + // con nueve «compás» subrayados no hay quien lo lea. + const slugs = await marcados.evaluateAll((bs) => + bs.map((b) => (b as HTMLElement).dataset.termino) + ); + expect(new Set(slugs).size).toBeGreaterThan(1); + }); + + test('al pulsar un término se abre su definición', async ({ page }) => { + await page.goto('/taller/son-cubano'); + + const montuno = page.locator('.texto .termino[data-termino="montuno"]').first(); + await montuno.click(); + + const globo = page.getByRole('dialog', { name: /Definición de montuno/ }); + await expect(globo).toBeVisible(); + await expect(globo).toContainText('llamada y respuesta'); + + // Y se cierra con Escape, devolviendo el foco a donde estaba. + await page.keyboard.press('Escape'); + await expect(globo).toBeHidden(); + await expect(montuno).toBeFocused(); + }); + + test('el globo no tapa el término que se acaba de pulsar', async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 900 }); + await page.goto('/taller/son-cubano'); + + const termino = page.locator('.texto .termino').first(); + await termino.click(); + + const suyo = (await termino.boundingBox())!; + const globo = (await page.locator('.globo').boundingBox())!; + + const seSolapan = globo.y < suyo.y + suyo.height && suyo.y < globo.y + globo.height; + expect(seSolapan).toBe(false); + }); + + test('no se marca dentro de un encabezado', async ({ page }) => { + // Ensuciaría el índice del capítulo, que sale de los encabezados. + await page.goto('/taller/son-cubano'); + await expect(page.locator('.texto h2 .termino')).toHaveCount(0); + }); + + test('el glosario tiene su propia página, con las acepciones numeradas', async ({ page }) => { + await page.goto('/taller/glosario'); + + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Glosario'); + await expect(page.getByRole('heading', { level: 2, name: 'sinalefa' })).toBeVisible(); + + /* + * «Clave» tiene dos: patrón rítmico e instrumento. Con una sola + * definición por palabra, una de las dos sería falsa, y por eso las + * acepciones van numeradas. + */ + const clave = page.locator('#clave'); + await expect(clave.locator('.acepciones > li')).toHaveCount(2); + await expect(clave).toContainText('patrón rítmico'); + await expect(clave).toContainText('percusión'); + }); + + test('se llega al glosario desde el índice del taller', async ({ page }) => { + await page.goto('/taller/bolero'); + await page + .getByRole('navigation', { name: 'Contenido del taller' }) + .getByRole('link', { name: 'Glosario' }) + .click(); + + await expect(page).toHaveURL('/taller/glosario'); + }); + test('el taller está en la navegación principal', async ({ page }) => { await page.goto('/'); await page diff --git a/package.json b/package.json index e1f6cf2..c564d60 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "db:medios": "node --env-file=.env scripts/db/volcar-medios.mjs", "db:generos": "node --env-file=.env scripts/db/sembrar-generos.mjs", "db:versos": "node --env-file=.env scripts/db/sembrar-versos.mjs", + "db:glosario": "node --env-file=.env scripts/db/sembrar-glosario.mjs", "db:completar": "node --env-file=.env scripts/db/completar-catalogo.mjs", "db:comparar": "node --experimental-strip-types --import ./scripts/db/resolver-lib.mjs --env-file=.env scripts/db/comparar-catalogo.mjs" }, diff --git a/scripts/db/sembrar-glosario.mjs b/scripts/db/sembrar-glosario.mjs new file mode 100644 index 0000000..79dc49d --- /dev/null +++ b/scripts/db/sembrar-glosario.mjs @@ -0,0 +1,102 @@ +/** + * Carga el glosario del taller. + * + * npm run db:glosario + * + * Veintiocho términos con sus acepciones numeradas y las formas con las que + * aparecen de verdad en el texto. Es lo que hace posible el marcado: sin las + * variantes, «sinalefas» en plural no se reconocería y nadie echaría en falta + * el globo que no salió. + * + * Idempotente por slug: reimportar una definición corregida actualiza. Las + * acepciones se rehacen enteras —se borran y se vuelven a poner— porque su + * número es su identidad y renumerar a mano dejaría huecos. + */ + +import fs from 'node:fs'; +import pg from 'pg'; + +const SEMILLA = JSON.parse(fs.readFileSync('scripts/db/semilla-glosario.json', 'utf8')); + +const slugificar = (t) => + t + .toLowerCase() + .normalize('NFD') + .replace(/[̀-ͯ]/g, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + +const c = new pg.Client({ connectionString: process.env.DATABASE_URL, query_timeout: 60_000 }); +await c.connect(); +await c.query('BEGIN'); + +const contado = { termino: 0, acepcion: 0, variante: 0 }; +const avisos = []; + +try { + const vistos = new Set(); + + for (const t of SEMILLA.terminos) { + const slug = slugificar(t.termino); + if (vistos.has(slug)) { + avisos.push(`«${t.termino}» sale dos veces en la semilla`); + continue; + } + vistos.add(slug); + + const { rows } = await c.query( + `INSERT INTO termino (id, slug, termino) VALUES ($1,$2,$3) + ON CONFLICT (slug) DO UPDATE SET termino = EXCLUDED.termino + RETURNING id`, + [crypto.randomUUID(), slug, t.termino] + ); + const terminoId = rows[0].id; + contado.termino++; + + await c.query('DELETE FROM acepcion WHERE termino_id = $1', [terminoId]); + for (const [i, a] of t.acepciones.entries()) { + await c.query( + `INSERT INTO acepcion (id, termino_id, numero, categoria, definicion, ampliacion, ejemplo) + VALUES ($1,$2,$3,$4,$5,$6,$7)`, + [ + crypto.randomUUID(), + terminoId, + i + 1, + a.categoria, + a.definicion, + a.ampliacion ?? null, + a.ejemplo ?? null + ] + ); + contado.acepcion++; + } + + /* + * El propio término también es una forma buscable, así no hay que + * repetirlo en la lista de variantes de los veintiocho. + */ + await c.query('DELETE FROM termino_variante WHERE termino_id = $1', [terminoId]); + for (const forma of new Set([t.termino, ...(t.variantes ?? [])])) { + await c.query( + 'INSERT INTO termino_variante (termino_id, forma) VALUES ($1,$2) ON CONFLICT DO NOTHING', + [terminoId, forma.toLowerCase()] + ); + contado.variante++; + } + } + + await c.query('COMMIT'); +} catch (fallo) { + await c.query('ROLLBACK').catch(() => {}); + console.error('Falló, no se ha escrito nada:'); + console.error(' ' + fallo.message); + await c.end(); + process.exit(1); +} + +await c.end(); + +for (const [que, n] of Object.entries(contado)) console.log(` ${que.padEnd(10)} ${n}`); +for (const aviso of avisos) console.log(` aviso: ${aviso}`); +console.log(''); +console.log('Glosario cargado.'); diff --git a/scripts/db/semilla-glosario.json b/scripts/db/semilla-glosario.json new file mode 100644 index 0000000..d889e86 --- /dev/null +++ b/scripts/db/semilla-glosario.json @@ -0,0 +1,376 @@ +{ + "_": [ + "El glosario del taller.", + "", + "Las 'variantes' son las formas con las que la palabra aparece de verdad en", + "el texto: plurales, femeninos, la forma con tilde y sin ella. El marcado", + "busca por ellas, así que una variante que falte es un término que no se", + "marca y nadie echa de menos.", + "", + "Las acepciones van numeradas porque la misma palabra significa cosas", + "distintas: 'clave' es el instrumento de percusión, el patrón rítmico y la", + "armadura de tonalidad. Con una sola definición, dos de las tres serían", + "falsas.", + "", + "'definicion' es lo que cabe en un globo sin tapar el texto. 'ampliacion' es", + "lo que se lee en la página del glosario, y 'ejemplo' un verso donde se vea:", + "un término de métrica sin ejemplo no se entiende." + ], + + "terminos": [ + { + "termino": "sinalefa", + "variantes": ["sinalefas"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Dos vocales que se juntan al cantar y cuentan como una sola sílaba: la última de una palabra y la primera de la siguiente.", + "ampliacion": "Es la regla que más cuesta interiorizar y la que más versos arregla. No es opcional: al cantar se produce sola, y contar sin ella da un verso más largo de lo que suena. Cuando molesta —porque junta dos vocales que quieren oírse aparte— se rompe metiendo una consonante o cambiando la palabra.", + "ejemplo": "«Dejaste la taza a medias» son ocho sílabas, no nueve: «za·a» va en una." + } + ] + }, + { + "termino": "hiato", + "variantes": ["hiatos"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Lo contrario de la sinalefa: dos vocales seguidas que se pronuncian en sílabas separadas.", + "ampliacion": "Se usa a propósito cuando hace falta una sílaba más o cuando la sinalefa atropella una palabra que importa. Forzarlo demasiado suena a verso mal medido, no a licencia.", + "ejemplo": "«que reías con el otro»: «re·í·as», tres sílabas, porque la tilde rompe el diptongo." + } + ] + }, + { + "termino": "octosílabo", + "variantes": ["octosílabos", "octosilabo", "octosilabos"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Verso de ocho sílabas. Es la medida de la copla popular en español y la más usada en la canción.", + "ampliacion": "Suena a lengua hablada porque el castellano cae solo en grupos de ocho. Por eso lo usan la ranchera, el son, la cumbia y el bolero clásico: no hay que forzar nada para que quepa una frase entera.", + "ejemplo": "«Si me llaman por el nombre»" + } + ] + }, + { + "termino": "endecasílabo", + "variantes": ["endecasílabos", "endecasilabo", "endecasilabos"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Verso de once sílabas, con acento obligatorio en la décima. Es el verso culto del español.", + "ampliacion": "Da sitio a subordinadas, que es lo que permite tener sintaxis de prosa sin dejar de cantarse. El tango vive de él; el bolero lo alterna con el octosílabo.", + "ejemplo": "«Volví a la esquina donde me esperabas»" + } + ] + }, + { + "termino": "heptasílabo", + "variantes": ["heptasílabos", "heptasilabo", "heptasilabos"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Verso de siete sílabas. Suele alternar con el endecasílabo para dar respiro.", + "ampliacion": "Es medida corta y por eso corta la frase: obliga a decir una cosa por verso. Alternado con versos largos, marca el pulso de una estrofa sin que se note.", + "ejemplo": "«y el humo por la casa»" + } + ] + }, + { + "termino": "métrica", + "variantes": ["metrica"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "La medida de los versos: cuántas sílabas tiene cada uno y cómo se cuentan.", + "ampliacion": "No es un adorno académico: la métrica es lo que hace que una letra se pueda cantar sobre una melodía sin atropellar palabras. Un verso de más sobra, y se nota antes en el oído que en el papel.", + "ejemplo": null + } + ] + }, + { + "termino": "escansión", + "variantes": ["escansion", "escandir", "escandida", "escandido"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Contar las sílabas de un verso aplicando las reglas: sinalefas, hiatos y la ley del acento final.", + "ampliacion": "Escandir es lo que separa medir de contar letras. Se hace con la voz, no con los ojos: si al decirlo en alto la cuenta no sale, la escansión está mal.", + "ejemplo": null + } + ] + }, + { + "termino": "verso agudo", + "variantes": ["versos agudos", "aguda", "agudas"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Verso que acaba en palabra aguda. Suma una sílaba a la cuenta.", + "ampliacion": "Es la mitad de la ley del acento final, y la que más despista: «yo respondo con el son» tiene siete casillas y es un octosílabo.", + "ejemplo": "«yo respondo con el son» — siete sílabas, mide ocho." + } + ] + }, + { + "termino": "rima consonante", + "variantes": ["consonante", "rima perfecta"], + "acepciones": [ + { + "categoria": "rima", + "definicion": "Riman todos los sonidos desde la última sílaba tónica: vocales y consonantes.", + "ampliacion": "Es la rima cerrada, la que suena a rima. Pide mucho —hay pocas palabras que rimen así— y por eso empuja a decir lo que rima en vez de lo que se quería decir. Úsala cuando el estilo la exija.", + "ejemplo": "techo / lecho, encendido / ruido" + } + ] + }, + { + "termino": "rima asonante", + "variantes": ["asonante", "asonantes"], + "acepciones": [ + { + "categoria": "rima", + "definicion": "Riman solo las vocales desde la última tónica; las consonantes dan igual.", + "ampliacion": "Es la rima de la copla popular. Pide mucho menos que la consonante y por eso deja decir lo que hay que decir: en una letra cantada casi nadie nota la diferencia, y el margen que da se agradece en cada verso.", + "ejemplo": "Jalisco / dicho — las dos en i-o." + } + ] + }, + { + "termino": "estrofa", + "variantes": ["estrofas"], + "acepciones": [ + { + "categoria": "estructura", + "definicion": "Grupo de versos que funciona como unidad: la parte que cuenta algo antes de volver al estribillo.", + "ampliacion": "En una canción la estrofa hace avanzar; el estribillo se queda. Una letra en la que las estrofas no avanzan es una letra que repite el estribillo tres veces con otras palabras.", + "ejemplo": null + } + ] + }, + { + "termino": "estribillo", + "variantes": ["estribillos"], + "acepciones": [ + { + "categoria": "estructura", + "definicion": "La parte que vuelve, igual, cada vez. Es lo que se recuerda y lo que da nombre a la canción.", + "ampliacion": "Tiene que poder cantarse sin haber entendido la estrofa. Si necesita contexto para significar algo, no es estribillo: es otra estrofa con la misma melodía.", + "ejemplo": null + } + ] + }, + { + "termino": "puente", + "variantes": ["puentes"], + "acepciones": [ + { + "categoria": "estructura", + "definicion": "La parte que rompe: distinta de la estrofa y del estribillo, y suele aparecer una sola vez.", + "ampliacion": "Sirve para que el último estribillo signifique algo distinto del primero. Sin puente, una canción de tres minutos dice lo mismo tres veces.", + "ejemplo": null + } + ] + }, + { + "termino": "colchón", + "variantes": ["colchon", "colchones"], + "acepciones": [ + { + "categoria": "musica", + "definicion": "El fondo sostenido —cuerdas, teclado, coros— sobre el que se apoya la voz sin competir con ella.", + "ampliacion": "Al escribir importa porque un colchón admite versos largos y frases alargadas: no hay que dejar hueco. Sobre un acompañamiento marcado, en cambio, cada sílaba cae en un sitio.", + "ejemplo": null + } + ] + }, + { + "termino": "compás", + "variantes": ["compas", "compases"], + "acepciones": [ + { + "categoria": "musica", + "definicion": "Cómo se agrupan los pulsos: de dos en dos, de tres en tres, de cuatro en cuatro.", + "ampliacion": "Es lo primero que hay que saber para escribir en un estilo. Un 3/4 pide frases que respiren de tres en tres; un 4/4 marcado admite versos más apretados. Escribir contra el compás se oye enseguida.", + "ejemplo": "El vals criollo va en 3/4; el son cubano, en 4/4." + } + ] + }, + { + "termino": "clave", + "variantes": ["claves"], + "acepciones": [ + { + "categoria": "musica", + "definicion": "El patrón rítmico de cinco golpes sobre el que se organiza la música cubana. Va en 3-2 o en 2-3.", + "ampliacion": "No es un adorno de la percusión: es la rejilla a la que se ajusta todo lo demás, la letra incluida. Una frase que cae «al revés de la clave» suena mal aunque las sílabas cuadren.", + "ejemplo": null + }, + { + "categoria": "instrumento", + "definicion": "El instrumento de percusión: dos palos de madera que se golpean uno contra otro.", + "ampliacion": "Es lo que marca el patrón que lleva su nombre. Suena por encima de todo sin necesitar volumen.", + "ejemplo": null + } + ] + }, + { + "termino": "cinquillo", + "variantes": ["cinquillos"], + "acepciones": [ + { + "categoria": "musica", + "definicion": "Célula rítmica cubana de cinco golpes en un compás. Es el pulso del bolero y del danzón.", + "ampliacion": "Condiciona dónde caen los acentos de la letra: los versos se apoyan en él, y por eso un bolero escrito sin tenerlo en cuenta se canta a trompicones.", + "ejemplo": null + } + ] + }, + { + "termino": "montuno", + "variantes": ["montunos"], + "acepciones": [ + { + "categoria": "estructura", + "definicion": "La segunda parte del son, con llamada y respuesta entre el solista y el coro.", + "ampliacion": "Es donde la letra deja de contar y empieza a insistir. Las frases se acortan, se repiten y admiten improvisación encima: escribir un montuno es escribir algo que aguante veinte vueltas.", + "ejemplo": null + } + ] + }, + { + "termino": "lunfardo", + "variantes": [], + "acepciones": [ + { + "categoria": "interpretacion", + "definicion": "El habla popular de Buenos Aires y Montevideo. En el tango no es color local: identifica de dónde viene quien habla.", + "ampliacion": "Usado de fuera suena a disfraz. Si no es tu lengua, escribe el tango en la tuya: la estructura y la mirada son lo que hacen tango a un tango, no el diccionario.", + "ejemplo": "«Dejá el escabio, piba, y escuchá»" + } + ] + }, + { + "termino": "melisma", + "variantes": ["melismas", "melismático", "melismatico"], + "acepciones": [ + { + "categoria": "interpretacion", + "definicion": "Cantar varias notas sobre una sola sílaba, alargándola.", + "ampliacion": "Por eso en un bolero cabe menos texto del que parece: cada sílaba se estira. Un verso apretado obliga a atropellar, y el melisma deja de poder hacerse.", + "ejemplo": null + } + ] + }, + { + "termino": "requinto", + "variantes": ["requintos"], + "acepciones": [ + { + "categoria": "instrumento", + "definicion": "Guitarra pequeña afinada más aguda, que hace la introducción y los adornos entre versos del bolero.", + "ampliacion": "Es lo que deja huecos entre frase y frase, y por eso una letra de bolero no debe llenarlos: los versos van con aire porque el requinto va a hablar en medio.", + "ejemplo": null + } + ] + }, + { + "termino": "bandoneón", + "variantes": ["bandoneon", "bandoneones"], + "acepciones": [ + { + "categoria": "instrumento", + "definicion": "El fuelle del tango. Su respiración marca el rubato, esa forma de adelantar y retrasar el tiempo.", + "ampliacion": "La letra del tango se canta con esa elasticidad, así que admite versos largos sin sonar apretados: el bandoneón espera.", + "ejemplo": null + } + ] + }, + { + "termino": "rubato", + "variantes": [], + "acepciones": [ + { + "categoria": "interpretacion", + "definicion": "Estirar y encoger el tiempo al cantar, sin ceñirse al pulso exacto.", + "ampliacion": "Es lo que permite decir un verso como quien habla. También es lo que hace que la métrica importe menos de lo que parece en algunos estilos, y muchísimo en los que no lo admiten.", + "ejemplo": null + } + ] + }, + { + "termino": "copla", + "variantes": ["coplas"], + "acepciones": [ + { + "categoria": "estructura", + "definicion": "Estrofa de cuatro versos octosílabos con rima en los pares. Es la forma popular por excelencia en español.", + "ampliacion": "Los impares van libres, y ese es su truco: dejan sitio para decir algo concreto sin pelearse con la rima. Media canción hispanoamericana está escrita en coplas.", + "ejemplo": null + } + ] + }, + { + "termino": "tónica", + "variantes": ["tonica", "tónicas", "tonicas", "sílaba tónica", "silaba tonica"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "La sílaba en la que cae el acento de una palabra o de un verso.", + "ampliacion": "El octosílabo lleva siempre acento en la séptima; el endecasílabo, en la décima. Eso es lo que hace que un verso suene a lo que es, aunque cambien todas las palabras.", + "ejemplo": null + } + ] + }, + { + "termino": "ley del acento final", + "variantes": ["acento final"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Un verso se mide hasta su última sílaba tónica más una: el agudo suma una sílaba, el esdrújulo resta una.", + "ampliacion": "Es la primera regla que hay que tener automatizada. Sin ella, media escansión sale mal y no se entiende por qué.", + "ejemplo": "«yo respondo con el son»: siete casillas, ocho sílabas." + } + ] + }, + { + "termino": "encabalgamiento", + "variantes": ["encabalgamientos", "encabalgado", "encabalgada"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Cuando la frase no acaba con el verso y continúa en el siguiente.", + "ampliacion": "En una letra cantada es arriesgado: la melodía suele cerrar donde cierra el verso, y una frase que sigue queda partida. Usado a propósito, es lo que da sensación de habla.", + "ejemplo": null + } + ] + }, + { + "termino": "hemistiquio", + "variantes": ["hemistiquios"], + "acepciones": [ + { + "categoria": "metrica", + "definicion": "Cada una de las dos mitades en que una pausa parte un verso largo.", + "ampliacion": "En el alejandrino la pausa cae en el centro y las dos mitades se miden aparte, como si fueran dos versos.", + "ejemplo": null + } + ] + }, + { + "termino": "voseo", + "variantes": [], + "acepciones": [ + { + "categoria": "interpretacion", + "definicion": "Tratar de «vos» en vez de «tú», con su acento al final del verbo: dejá, escuchá, mirá.", + "ampliacion": "Cambia la cuenta del verso, porque convierte en agudas palabras que en el tuteo son llanas. En el tango no es un detalle regional: es la voz.", + "ejemplo": "«y escuchá» hace agudo el verso y le suma una sílaba." + } + ] + } + ] +} diff --git a/src/app.css b/src/app.css index a102913..85f750c 100644 --- a/src/app.css +++ b/src/app.css @@ -777,3 +777,31 @@ a.chip:focus-visible { transform: none; } } + +/* --- términos del glosario ------------------------------------------------ + * + * El subrayado punteado es la convención de «esto tiene definición» desde + * mucho antes que la web, y se distingue de un enlace de verdad: un enlace + * lleva a otra página, esto abre un globo y te deja donde estabas. + * + * Va aquí y no en un componente porque el botón lo escribe el servidor dentro + * del HTML del capítulo, donde el ámbito de Svelte no llega. + */ +.termino { + border: 0; + padding: 0; + background: none; + font: inherit; + color: inherit; + cursor: help; + text-decoration: underline dotted; + text-decoration-color: color-mix(in oklab, var(--c-accent) 60%, transparent); + text-underline-offset: 0.2em; + text-decoration-thickness: 1px; +} + +.termino:hover, +.termino:focus-visible { + color: var(--c-accent); + text-decoration-color: var(--c-accent); +} diff --git a/src/lib/components/GlobosDeGlosario.svelte b/src/lib/components/GlobosDeGlosario.svelte new file mode 100644 index 0000000..7ac368f --- /dev/null +++ b/src/lib/components/GlobosDeGlosario.svelte @@ -0,0 +1,234 @@ + + + e.key === 'Escape' && abierto && cerrar()} + onresize={() => abierto && colocar()} + onscroll={() => abierto && colocar()} +/> + +{#if abierto} + + + + +{/if} + + diff --git a/src/lib/components/IndiceTaller.svelte b/src/lib/components/IndiceTaller.svelte index ea67abc..077266b 100644 --- a/src/lib/components/IndiceTaller.svelte +++ b/src/lib/components/IndiceTaller.svelte @@ -66,6 +66,24 @@ {/each} + + +
+

Consulta

+ +