Este tutorial crea el prototipo útil más pequeño posible para Manifest V3 de Chrome: un área de texto privada que aparece en las páginas de cada Pin, utiliza su identificador numérico como clave y se guarda en chrome.storage.local. Se trata deliberadamente de un prototipo, no de una réplica de las antiguas notas de Pinterest lista para producción.
El DOM de Pinterest puede cambiar sin previo aviso. Mantén aislados los selectores, no imites los elementos de marca protegidos de Pinterest y prueba todos los tipos de página e idiomas que pretendas admitir.
1. Crea la extensión con Manifest V3
Crea una carpeta con manifest.json y content.js. La documentación de Chrome sobre el manifiesto es la referencia oficial para saber qué claves puedes utilizar.
{
"manifest_version": 3,
"name": "Prototipo de contexto para Pines",
"version": "0.1.0",
"description": "Guarda una nota privada local para un Pin de Pinterest.",
"permissions": ["storage"],
"content_scripts": [{
"matches": ["https://*.pinterest.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}]
}
El patrón de URL es amplio porque Pinterest utiliza navegación en el cliente. Si tu función solo admite algunas rutas o dominios nacionales, limita el patrón y documenta ese alcance.
2. Obtén una clave estable a partir de la URL del Pin
No utilices como clave la posición de una tarjeta ni su título visible. Extrae el identificador numérico de una ruta canónica /pin/…/ y devuelve null en cualquier otro caso.
function getPinId(url = location.href) {
try {
const { pathname } = new URL(url);
const match = pathname.match(/\/pin\/(\d+)\/?/);
return match ? match[1] : null;
} catch {
return null;
}
}
Prueba URL válidas de detalle, parámetros de consulta, páginas que no correspondan a un Pin, URL mal formadas y todos los patrones regionales que afirmes admitir. Nunca asocies una nota sin avisar si no puedes determinar el identificador con certeza.
3. Guarda un registro con versión
Define el esquema de forma explícita para poder migrarlo en el futuro:
async function readNote(pinId) {
const key = 'pin-note:' + pinId;
const result = await chrome.storage.local.get(key);
return result[key] ?? { version: 1, pinId, text: '', updatedAt: null };
}
async function writeNote(pinId, text) {
const key = 'pin-note:' + pinId;
const record = {
version: 1,
pinId,
text,
updatedAt: new Date().toISOString()
};
await chrome.storage.local.set({ [key]: record });
return record;
}
Chrome documenta un límite predeterminado de 10 MB para storage.local, además del permiso independiente unlimitedStorage que permite aumentarlo. Chrome también advierte que el almacenamiento local de una extensión se borra al desinstalarla. Consulta la documentación vigente de la API Storage y crea una función de exportación antes de pedir a los usuarios que confíen datos importantes a la extensión.
4. Inserta un editor aislado y accesible
Un Shadow DOM reduce las colisiones accidentales de estilos. El ejemplo añade el editor al cuerpo de la página para no depender de un nombre de clase frágil de Pinterest:
async function mountEditor(pinId) {
if (document.querySelector('[data-pin-context-root]')) return;
const host = document.createElement('aside');
host.dataset.pinContextRoot = '';
host.setAttribute('aria-label', 'Nota privada del Pin');
document.body.append(host);
const root = host.attachShadow({ mode: 'open' });
const label = document.createElement('label');
label.textContent = 'Nota privada';
const editor = document.createElement('textarea');
editor.id = 'pin-note-editor';
label.htmlFor = editor.id;
editor.setAttribute('aria-label', 'Nota privada para este Pin');
const status = document.createElement('span');
status.setAttribute('role', 'status');
const saved = await readNote(pinId);
editor.value = saved.text;
root.append(label, editor, status);
bindAutosave(editor, status, pinId);
}
Crea el texto de la interfaz con textContent, no con un valor no fiable asignado a innerHTML. Añade estilos de foco visibles, contraste suficiente, una etiqueta real, acceso mediante teclado y mensajes de estado que no desplacen el foco.
5. Guarda sin escribir en cada pulsación
function bindAutosave(editor, status, pinId) {
let saveTimer;
editor.addEventListener('input', () => {
status.textContent = 'Cambios sin guardar';
clearTimeout(saveTimer);
saveTimer = setTimeout(async () => {
try {
await writeNote(pinId, editor.value);
status.textContent = 'Guardado en local';
} catch (error) {
console.error('No se pudo guardar la nota del Pin', error);
status.textContent = 'Error al guardar';
}
}, 400);
});
}
En producción, guarda también los cambios pendientes al pasar a otra página, impide que dos pestañas se sobrescriban entre sí, define un tamaño máximo para las notas y muestra los errores de cuota.
6. Gestiona la navegación de Pinterest en el cliente
Un controlador que solo se ejecuta al cargar la página no detecta los desplazamientos entre el feed y la vista de detalle. Utiliza una función de reconciliación idempotente y observa los cambios sin volver a montar la interfaz con cada mutación:
let scheduled = false;
async function reconcile() {
scheduled = false;
const pinId = getPinId();
const existing = document.querySelector('[data-pin-context-root]');
if (!pinId) {
existing?.remove();
return;
}
if (existing?.dataset.pinId === pinId) return;
existing?.remove();
await mountEditor(pinId);
const mounted = document.querySelector('[data-pin-context-root]');
if (mounted) mounted.dataset.pinId = pinId;
}
new MutationObserver(() => {
if (scheduled) return;
scheduled = true;
requestAnimationFrame(reconcile);
}).observe(document.documentElement, { childList: true, subtree: true });
reconcile();
Una versión para producción también debe observar directamente el estado de navegación cuando sea posible y desconectar los observadores que ya no necesite. Mide el rendimiento del código en feeds largos: una función correcta que ralentiza la página sigue siendo una mala extensión.
7. Respeta las restricciones de seguridad de Manifest V3
Manifest V3 prohíbe el código ejecutable alojado de forma remota. Incluye el JavaScript ejecutable dentro de la extensión, conserva la política de seguridad de contenido predeterminada salvo que exista un motivo justificado para cambiarla, evita la ejecución dinámica de código y solicita solo los permisos imprescindibles. La introducción de Chrome a Manifest V3 explica el modelo de la plataforma.
8. Añade las funciones que necesita un producto real
- exportación e importación JSON con validación del esquema y resolución de conflictos;
- una exportación legible para recuperar los datos;
- búsqueda y etiquetas opcionales;
- información sobre el espacio utilizado y la cuota disponible;
- borrado individual de notas y borrado de todos los datos;
- una política de privacidad que coincida con el comportamiento observable;
- pruebas de migración entre versiones del esquema;
- avisos claros sobre la desinstalación y la ausencia de sincronización;
- telemetría de errores solo si minimizas los datos cuidadosamente e informas de su recogida.
9. Prueba también las rutas de fallo
- Carga la extensión descomprimida desde
chrome://extensions. - Visita un feed, un tablero, la página de detalle de un Pin y una ruta que no pertenezca a ningún Pin.
- Pasa de un Pin a otro sin recargar la página y confirma que sus notas permanecen aisladas.
- Abre el mismo Pin en dos pestañas y comprueba cómo se resuelven los conflictos.
- Desactiva el almacenamiento o simula que se ha agotado la cuota y comprueba el aviso.
- Exporta un registro de prueba, bórralo y restáuralo.
- Prueba la navegación mediante teclado, el zoom, las etiquetas para lectores de pantalla y la interfaz sobre páginas claras y oscuras.
La lección de ingeniería
El área de texto es la parte fácil. Una extensión de Pinterest fiable necesita una identidad estable, una gestión resistente del ciclo de vida, una interfaz accesible, permisos limitados, un almacenamiento documentado y una vía de salida probada. Resuelve todo eso antes de ampliar la lista de funciones.
Sigue mejorando tu sistema de trabajo en Pinterest
Continúa con la guía que corresponda a tu siguiente paso:
Prueba la extensión Notestopin para Chrome
Añade notas privadas a cualquier Pin, organízalas con etiquetas y encuentra después todo lo que guardaste.
Añadir a Chrome