Creé un bookmarklet para crear la tabla de contenido para las publicaciones del foro

Tabla de contenido:

Creé un bookmarklet para generar una tabla de contenido (ToC) plegable como la que se muestra arriba.
¡Espero que ayude a los miembros ávidos de la comunidad que escriben textos largos!

Resumen

A veces escribo temas/publicaciones largas y necesitaba una ToC para facilitar la lectura.
Encontré algunos trabajos existentes, como DiscoTOC - automatic table of contents, pero necesitaba una herramienta solo para mí, sin necesidad de instalar algo en toda la comunidad.

Después de publicar una publicación estructurada, haz clic en el bookmarklet y la ToC se copiará a tu portapapeles. ¡Edita la publicación y pega la ToC en la parte superior!

Cómo usar

Instalación

  1. Guarda una página como marcador.
  2. Edita el nombre, por ejemplo, “:clipboard: Copiar ToC del foro al portapapeles”.
  3. Edita la URL y pega el siguiente código. Personaliza el código si es necesario; consulta los dos elementos “Opcional” a continuación.
javascript:(function() {
	const copyForumTocToClipboard = function() {
		const urlMatch = window.location.href.match(/\/t\/[^\/]*\/\d+\/?(\d*)/);
		if (!urlMatch) return;

		const postIndex = urlMatch ? urlMatch[1] : 1;
		const anchors = document.querySelectorAll('#post_' + postIndex + ' div.cooked h6 a.anchor,h5 a.anchor,h4 a.anchor,h3 a.anchor,h2 a.anchor,h1 a.anchor');
		if (!anchors) return;

		let toc = '';
		anchors.forEach(anchor => {
			toc +=
				' '.repeat((anchor.parentNode.nodeName[1] - 1) * 4) +
				`<a href="${anchor.href}">${anchor.parentNode.textContent}</a><br>\n`;
		});
		if (!toc) return;

		navigator.clipboard.writeText('<details open><summary>Table of contents: </summary><ul>\n' + toc + '</ul></details>');
	};

	copyForumTocToClipboard();
})();

Qué hace el código

  1. Comprueba si la URL parece ser una publicación de la comunidad: https://{dominio}/t/{título}/{ID_del_tema}(/{índice_de_publicación}).
  2. Comprueba si se incluyen anclajes :link: (encabezados como <h1> y # ) en la publicación.
  3. Genera código HTML para la ToC.
  4. Copia el código al portapapeles.

Generación de ToC

  1. Publica una entrada con estructura (HTML <h1>, <h2>, … y Markdown # , ## , …). Discourse asignará anclajes a cada encabezado.
  2. Asegúrate de que tu publicación esté seleccionada mirando la barra de progreso (por ejemplo, 1/22) o la URL (por ejemplo, /1).
  3. Haz clic en el bookmarklet en la barra de marcadores.
  4. La ToC se copia a tu portapapeles.

Uso de ToC

  1. Haz clic en el icono del lápiz para editar la publicación.
  2. Pega el código en la parte superior.
  3. Comprueba si los elementos de la ToC se muestran correctamente (problema conocido: el bookmarklet omite algunos emojis).
  4. (Opcional) Cambia/traduce “Table of contents”.
  5. (Opcional) Elimina open si deseas contraer la ToC por defecto.
  6. Haz clic en “Guardar cambios”. Si lo haces unos minutos después de publicar la entrada, el icono del lápiz de “editado” no se añadirá a tu publicación.

Notas técnicas para gente técnica

  • También puedes copiar y pegar el código de JavaScript en la consola del desarrollador.
    • Envolví el código en una función porque return; no funciona cuando se usa fuera de las funciones.
  • El enfoque de '\u0026nbsp;'.repeat() parece desordenado al previsualizar el tema, pero en mi opinión, se ve mejor en la publicación real (en comparación con el uso de <li></li>) en mi opinión.
  • Cuando probé querySelectorAll, de alguna manera no se encontró el primer elemento en h6 a.anchor,h5 a.anchor, .... Puse h6 al principio porque es probablemente lo que se usa con menos frecuencia.
  • El bookmarklet podría dejar de funcionar si Discourse cambia su interfaz/DOM. Por favor, respóndeme si encuentras errores.

Capturas de pantalla

Como nuevo usuario en meta.discourse.org, no puedo agregar varias imágenes, así que agrupo todas las capturas de pantalla en una sola imagen:

18 Me gusta

Ohh genial, ¿se podría extender para crear una tabla de contenidos para toda la publicación con todas las respuestas?

3 Me gusta

Las publicaciones generalmente se cargan unas 20 a la vez, supongo que sería difícil crear una tabla de contenido de todas las publicaciones a la vez.

4 Me gusta

¡Muy genial! ¡Gracias por compartir @ShunS!

Me encanta cómo planteaste el problema que estás resolviendo con esta herramienta y cómo está diseñada para satisfacer las necesidades de usuarios importantes en sitios que, ellos mismos, pueden no tener permisos para cambiar las cosas en el sitio tal como existen hoy.

10 Me gusta

Gracias por las respuestas :slight_smile:

Como dice @Lhc_fl, el enfoque DOM (HTML/JavaScript) tiene un número limitado de elementos que podemos administrar. La API de Discourse podría ayudar para tales acciones.
Otro problema al crear una tabla de contenido para las respuestas es que no es fácil definir cuál sería el título del encabezado para cada respuesta. Podríamos usar información como el autor y la fecha, pero no estoy seguro de que sea más útil que la barra de progreso de desplazamiento existente.

¡Gracias @mcwumbly!

6 Me gusta

@ShunS esto es muy bueno, gracias por compartir. Además, bienvenido a Meta :wave: :slight_smile:

5 Me gusta

Esto es fabuloso y exactamente lo que estaba buscando. Parece resolver este problema

¿Por qué no enviar esto a Discourse para incluirlo como un complemento o función oficial? Podría incluirse en la barra de herramientas del compositor/editor para insertar automáticamente una tabla de contenido en la publicación.

6 Me gusta

Tengo una sugerencia: ¿es posible añadir un punto al principio de la línea de la tabla de contenidos? En mi caso, cada encabezado es una línea larga, por lo que el punto ayuda a diferenciar una entrada de la siguiente.

1 me gusta

Hola @RBoy, ¡gracias por tus comentarios y sugerencias!

Un plugin de editor sería genial, pero requeriría mucho esfuerzo leer el código fuente de Discourse para comprender la lógica de manejo de emojis y la definición del texto/ancla del encabezado, y crear un repositorio del plugin.

Un plugin (aparentemente) simple como Spoiler Alert es un repositorio grande y no tengo el ancho de banda para comprometerme completamente con el desarrollo. Por lo tanto, espero que Discourse pueda priorizar solicitudes de funciones como Automatic Table of Contents generation y desarrollar una función nativa mientras tanto :folded_hands:


A continuación se muestra la versión con los puntos de viñeta. Los espacios entre <ul> y </ul> son bastante grandes, por lo que preferí la versión original sin viñetas.

Captura de pantalla:

Código:

javascript:(function() {
	const copyForumTocToClipboard = function() {
		const urlMatch = window.location.href.match(/\/t\/[^\/]*\/\d+\/?(\d*)/);
		if (!urlMatch) return;

		const postIndex = 1;
		const anchors = document.querySelectorAll('#post_' + postIndex + ' div.cooked h6 a.anchor,h5 a.anchor,h4 a.anchor,h3 a.anchor,h2 a.anchor,h1 a.anchor');

		let toc = '';
		let currentLevel = 1;
		anchors.forEach(anchor => {
			newLevel = anchor.parentNode.nodeName[1];
			levelChange = newLevel - currentLevel;
			toc +=
				((levelChange >= 0) ? '<ul>'.repeat(levelChange) : '</ul>'.repeat(levelChange * -1)) +
				`<li><a href="${anchor.href}">${anchor.parentNode.textContent}</a></li>`;
			currentLevel = newLevel;
		});
		if (newLevel > 1) toc += '</ul>'.repeat(newLevel - 1);
		toc = '<details open><summary>Table of contents: </summary><ul>\n' + toc + '</ul></details>';

		navigator.clipboard.writeText(toc);
	};

	copyForumTocToClipboard();
})();
5 Me gusta

Ya tenemos un plugin de ToC y no necesita mantener el ToC en el cuerpo de la publicación.

Definitivamente recomendaría ese si está instalado, ya que no puede desincronizarse.

Sin embargo, esta herramienta parece genial para cuando DiscoToC no está disponible. ¡Buen trabajo! :+1:

4 Me gusta

Esto se puede hacer usando un componente temático. Puedes echar un vistazo al método decorateCookedElement de la api, debería ser útil.

4 Me gusta

HI @supermathie, gracias por la respuesta.

cuando DiscoToC no está disponible

Sí, esa es la diferenciación. Creé el bookmarklet porque no estoy en posición de decidir qué “componente temático” instalar en el foro en el que suelo estar, y solo unas pocas personas escriben textos largos que necesitan un TOC.


¡Gracias @Lhc_fl, eso es muy útil!

Busqué en el repositorio de GitHub y encontré ese método. Consideraré desarrollarlo (solo) cuando tenga buen ancho de banda y vea mucha demanda para la función.

Sin embargo, si se puede agregar un componente temático a la comunidad, ya existe DiscoTOC como dijo @supermathie :slight_smile:

4 Me gusta

Lamentablemente, ese complemento es totalmente inutilizable cuando el encabezado tiene más de unas pocas palabras, ya que arruina por completo las cosas. Si los encabezados tienen una o dos líneas de largo (por ejemplo, una página de preguntas frecuentes), entonces el complemento Disco TOC arruina por completo la página, razón por la cual planteé esta solicitud de una tabla de contenido en línea (que esta proporciona) y es perfecta para tales páginas

Con la cantidad de genio/talento entre los equipos que construyen Discourse, no debería ser tan difícil incluir esta increíble función como una alternativa a DiscoTOC para darle un rango de uso mucho más amplio.