Volver al blog
La nueva pestaña de GraphMarks: los marcadores dispuestos como constelaciones alrededor de sus etiquetas —#kubernetes, #docs, #github, #ia, #domotica—, con un cúmulo aparte de veintinueve sin etiquetar y la barra de filtros arriba.

GraphMarks: Marcadores de Chrome en un grafo

LaboratorioExtensionesTypeScriptGrafo de conocimiento

Tengo cientos de marcadores y no uso ninguno. La carpeta «Leer luego» lleva años sin abrirse y dentro hay enlaces de tres trabajos atrás. El problema no es la cantidad: es que un árbol de carpetas obliga a decidir, en el momento de guardar, el único sitio donde ese enlace va a existir. Y esa decisión casi siempre se toma mal, porque cuando guardas todavía no sabes para qué lo vas a querer.

GraphMarks es el experimento que salió de ahí: una extensión de Chrome que sustituye la nueva pestaña por un grafo de tus marcadores. La hipótesis es sencilla y no sé todavía si es cierta: si el enlace puede estar en varios sitios a la vez, dejas de decidir al guardar y empiezas a encontrar por vecindad.

Las restricciones, que son la mitad del diseño

Una extensión no es una aplicación web con menos pantalla: es un entorno con límites duros que deciden por ti. Tres marcaron todo lo demás.

  • Sin servidor. Los marcadores de alguien son un mapa de su vida: dónde compra, qué lee, con quién trabaja. Subir eso a una API mía convierte un juguete en una responsabilidad. Todo se queda en el navegador.
  • La cuota de sincronización. Si quieres que las etiquetas viajen entre equipos sin infraestructura propia, el sitio es el almacén sincronizado de Chrome. Y ese almacén tiene números pequeños: 100 KB en total, 8 KB por elemento, 512 elementos y 1.800 escrituras por hora.
  • Una pestaña nueva se abre cien veces al día. Si tarda en pintar, la desinstalas a la semana. Eso descartó traer un framework: son 34 ficheros de TypeScript, unas 4.000 líneas, un canvas 2D y los módulos de d3 que hacen falta para la simulación de fuerzas.

Dos formas de guardar, porque se escriben distinto

Aquí está la decisión que más me ha enseñado del proyecto, y no tiene nada que ver con grafos. Hay dos cosas que guardar —las etiquetas y las sesiones de pestañas— y el instinto dice tratarlas igual. Se escriben de forma opuesta.

Etiquetar es un gesto pequeño y muy repetido: una etiqueta aquí, otra allá, decenas de veces en una tarde. Si cada una reescribe el mapa entero de etiquetas, dos cosas se rompen a la vez: el elemento crece hasta pasarse de los 8 KB, y el goteo de escrituras agota la cuota horaria. La salida es repartir por hash.

/**
 * Bucket de chrome.storage.sync para una URL. El límite es de 8 KB por item,
 * así que las etiquetas se reparten por hash entre TAG_BUCKETS claves.
 */
export function tagBucket(url: string): string {
  return `tags_${strHash(url) % TAG_BUCKETS}`
}

Doce cubos. Etiquetar una URL reescribe solo el cubo que le toca, así que el elemento se mantiene pequeño y cada gesto cuesta una escritura en vez de arrastrar todo lo demás.

Las sesiones son lo contrario: se guardan de tarde en tarde, pueden ocupar bastante y nunca se tocan a medias. Ahí no compensa la sofisticación, así que se trocea el JSON completo en fragmentos que quepan y se recompone al leer. La misma cuota, dos respuestas distintas, porque lo que cambia no es el tamaño del dato sino cómo se escribe.

Y las dos escriben antes el espejo local. Si la cuota falla, el dato sobrevive en el equipo y el aviso sale por pantalla en vez de perderse en la consola. Los anclajes de posición del grafo, en cambio, no se sincronizan a propósito: dependen del tamaño de pantalla, y traerse los de otro equipo desordena el mapa en lugar de conservarlo.

Degradar en cadena en vez de exigir permisos

Una extensión se instala con un diálogo de permisos que asusta. Cuantos más pidas de golpe, menos gente llega al otro lado. Así que ninguna capacidad es obligatoria: el almacén sincronizado cae al local y el local a la memoria del navegador; sin permiso para grupos de pestañas se agrupa igual, solo que sin sus metadatos; y fuera de la extensión, en la vista de previsualización, el grafo se pinta con datos de ejemplo. El permiso de grupos, además, se pide en caliente cuando lo usas, no al instalar.

Lo que Chrome no deja hacer

Guardar una sesión de trabajo debería incluir las vistas divididas. No se puede: el identificador de división es de solo lectura y no existe forma de recrearla desde una extensión. Está reportado en el repositorio de estándares de extensiones y sigue abierto. La decisión fue guardar el dato igualmente y, al restaurar, dejar el par seleccionado; el código comprueba si existe la API que hoy falta, así que el día que llegue funcionará sin tocar nada.

El otro tropiezo fue más tonto y más caro: los módulos ES no cargan bajo el protocolo de fichero, así que la previsualización suelta no arrancaba. De ahí que el empaquetado produzca funciones autoejecutables en vez de módulos.

Qué se testea y qué no

Catorce tests, en tres ficheros, y todos sobre la misma carpeta: la de funciones puras. Se prueba el reparto en cubos, la normalización de etiquetas, el troceado que garantiza que ningún fragmento pase de 8 KB. No se prueba el pintado del canvas ni las APIs del navegador, que ya tienen quien las pruebe. La regla que sigo es que un test vale lo que vale la pregunta que contesta, y «¿se dibuja el círculo?» no es una pregunta interesante.

34 / 4.027ficheros TypeScript y líneas
12cubos de etiquetas
14tests, solo de lógica pura
0servidores propios

Qué no sé todavía

  • Si la hipótesis se sostiene con el tiempo. Lo uso yo, y a las dos semanas encuentro cosas que antes no encontraba. Eso no es una medición: es el sesgo de quien lo ha escrito. La prueba de verdad es si alguien que no lo ha construido sigue usándolo al mes.
  • Cuánto aguanta el grafo. Con unos cientos de marcadores va suelto. No he medido dónde empieza a arrastrarse la simulación de fuerzas, y ese número lo tendría que saber antes de recomendárselo a nadie con miles.
  • No está publicada. Versión 0.4.0, con el paquete preparado para la tienda y la ficha traducida a dos idiomas, pero sin subir. Es un laboratorio, no un producto: si lo pruebas, hazlo sabiendo eso. El código está en el repositorio público.

Lo que me llevo para el trabajo de verdad

Dos cosas, y ninguna es sobre marcadores. La primera es que la forma de guardar la dicta el patrón de escritura, no el tamaño del dato: el mismo almacén pide dos diseños distintos según si escribes mucho y poco cada vez, o poco y mucho de golpe. Esa pregunta —¿cómo se escribe esto, no cuánto ocupa?— es la que hago ahora en cualquier sistema con una cuota delante, y la que decide entre una cola que va sobrada y otra que se atasca en producción.

La segunda es que degradar en cadena sale más barato que exigir. Cada permiso opcional y cada respaldo local es una línea más de código y un usuario menos perdido en el diálogo de instalación. En una extensión eso se ve en el primer minuto; en una plataforma de empresa tarda meses en verse, pero el cálculo es idéntico.

Zetesis-Labs/graphmarks

Nueva pestaña de Chrome que muestra tus marcadores como un grafo interactivo.

TypeScript

Más del laboratorio