Guarda una conversación entera de Gemini como documento, tus preguntas y todas las respuestas, con fórmulas, código y tablas. Gratis y sin extensiones.
Abre la conversación que quieras en gemini.google.com y ejecuta allí el script.
// Responsive Muse · Gemini Conversation Exporter · v1.0 · 2026-10-22
// Saves the Gemini conversation you have open as Markdown, Word (.doc) or PDF, with code
// blocks, lists and tables kept. Runs only on gemini.google.com. Gemini loads older messages
// as you scroll up, so the script scrolls from the top and collects every message on the way.
// It makes no network requests of its own and never reads cookies, passwords or tokens.
(async () => {
const TOOL = 'Exportar chat de Gemini';
const NAMES = { user: 'Tú', ai: 'Gemini' }; // headings used in the file
const DELAY = 1000; // pause between scrolls, in milliseconds
if (location.hostname !== 'gemini.google.com' || !/^\/(app|gem)\//.test(location.pathname)) {
alert('Abre primero la conversación de Gemini que quieres exportar (gemini.google.com/app/…).');
return;
}
// A small box on the page shows progress and asks before changing anything,
// so you never need to read or edit the console.
const many = (n, word) => `${n} ${n === 1 ? word : ({ canal: 'canales', 'canción': 'canciones', 'publicación': 'publicaciones', 'elemento guardado': 'elementos guardados' })[word] || word + 's'}`; // 1 vídeo, 2 vídeos
const panel = (() => {
const box = document.createElement('div');
box.style.cssText = 'display:block!important;position:fixed;right:16px;bottom:16px;z-index:2147483647;width:340px;max-width:calc(100vw - 32px);' +
'background:#111318;color:#e6e8ee;font:14px/1.5 system-ui,sans-serif;border-radius:12px;padding:16px;' +
'box-shadow:0 8px 30px rgba(0,0,0,.4);text-align:left';
const head = document.createElement('strong');
head.textContent = TOOL;
const msg = document.createElement('p');
msg.style.cssText = 'margin:8px 0 12px;white-space:pre-line';
const row = document.createElement('div');
row.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end';
box.append(head, msg, row);
document.body.append(box);
const button = (label, primary, onClick) => {
const b = document.createElement('button');
b.textContent = label;
b.style.cssText = 'font:600 14px system-ui,sans-serif;padding:8px 14px;border-radius:8px;cursor:pointer;' +
(primary ? 'background:#0f766e;color:#fff;border:1px solid #0f766e' : 'background:none;color:#e6e8ee;border:1px solid #3a3f4b');
b.onclick = onClick;
row.append(b);
};
return {
text(t) { msg.textContent = t; },
ask(t, yes) {
msg.textContent = t;
row.replaceChildren();
return new Promise(r => {
button('Cancelar', false, () => { row.replaceChildren(); r(false); });
button(yes, true, () => { row.replaceChildren(); r(true); });
});
},
stoppable(onStop) { row.replaceChildren(); button('Parar', false, onStop); },
actions(t, list) {
msg.textContent = t;
row.replaceChildren();
list.forEach(([label, primary, run]) => button(label, primary, run));
},
finish(t) { this.actions(t, [['Cerrar', true, () => box.remove()]]); },
close() { box.remove(); },
};
})();
// Turn a rendered message (HTML) back into Markdown: headings, lists, code, tables, links
const toMarkdown = root => {
const skip = el => el.matches('button, svg, script, style, [aria-hidden="true"], .sr-only, [data-markdown-copy="exclude"], ' +
'.code-block-decoration, [class*="code-block-header"]');
const walk = (node, depth) => {
if (node.nodeType === 3) return node.textContent;
if (node.nodeType !== 1 || skip(node)) return '';
const kids = () => [...node.childNodes].map(n => walk(n, depth)).join('');
const tag = node.tagName.toLowerCase();
// Math (KaTeX) is on the page three times; keep only its TeX source
if (node.classList.contains('katex')) {
const tex = node.querySelector('annotation[encoding="application/x-tex"]')?.textContent.trim();
if (tex) return node.closest('.katex-display') ? `\n\n$$${tex}$$\n\n` : `$${tex}$`;
}
if (/^h[1-6]$/.test(tag)) return `\n\n${'#'.repeat(+tag[1])} ${kids().trim()}\n\n`;
if (tag === 'p') return `\n\n${kids().trim()}\n\n`;
if (tag === 'br') return '\n';
if (tag === 'hr') return '\n\n---\n\n';
if (tag === 'strong' || tag === 'b') return `**${kids()}**`;
if (tag === 'em' || tag === 'i') return `*${kids()}*`;
if (tag === 'a') return `[${kids()}](${node.href})`;
if (tag === 'img') return node.alt ? `` : '';
if (tag === 'pre') {
const code = node.querySelector('code') || node;
const lang = ((code.className || '').match(/language-([\w+-]+)/) || [])[1] || '';
return `\n\n\`\`\`${lang}\n${code.textContent.replace(/\n$/, '')}\n\`\`\`\n\n`;
}
if (tag === 'code') return '`' + node.textContent + '`';
if (tag === 'blockquote') return '\n\n' + kids().trim().split('\n').map(l => '> ' + l).join('\n') + '\n\n';
if (tag === 'ul' || tag === 'ol') {
const items = [...node.children].filter(li => li.tagName === 'LI').map((li, i) => {
const body = [...li.childNodes].map(n => walk(n, depth + 1)).join('').trim().replace(/\n{2,}/g, '\n');
return ' '.repeat(depth) + (tag === 'ol' ? `${i + 1}. ` : '- ') + body;
});
return '\n' + items.join('\n') + '\n';
}
if (tag === 'table') {
const rows = [...node.querySelectorAll('tr')].map(tr =>
'| ' + [...tr.children].map(c => walk(c, depth).trim().replace(/\|/g, '\\|').replace(/\n/g, ' ')).join(' | ') + ' |');
if (rows.length) rows.splice(1, 0, '|' + ' --- |'.repeat(rows[0].split(' | ').length));
return '\n\n' + rows.join('\n') + '\n\n';
}
return kids();
};
return walk(root, 0).replace(/\n{3,}/g, '\n\n').trim();
};
// Offer the chat as Markdown, Word or PDF (all built here, in your browser)
const deliverChat = (title, messages, names, note = '') => {
const date = new Date().toISOString().slice(0, 10);
const md = `# ${title}\n\n_Exportado el ${date} desde ${location.href}_\n\n` + messages.map(m =>
`## ${m.role === 'user' ? names.user : names.ai}\n\n${m.markdown}`).join('\n\n---\n\n') + '\n';
const esc = s => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
const html = '<!doctype html><html><head><meta charset="utf-8"><title>' + esc(title) + '</title><style>' +
'body{font:15px/1.6 Georgia,serif;max-width:760px;margin:40px auto;padding:0 20px;color:#111}' +
'h1{font:700 26px system-ui,sans-serif}h2{font:600 15px system-ui,sans-serif;text-transform:uppercase;letter-spacing:.05em;color:#555;margin-top:32px}' +
'.msg{white-space:pre-wrap}.user{background:#f3f4f6;padding:12px 16px;border-radius:8px}' +
'pre,code{font:13px Consolas,monospace}hr{border:0;border-top:1px solid #ddd}</style></head><body>' +
`<h1>${esc(title)}</h1><p><em>Exportado el ${date}</em></p>` + messages.map(m =>
`<h2>${esc(m.role === 'user' ? names.user : names.ai)}</h2><div class="msg ${m.role}">${esc(m.markdown)}</div>`).join('<hr>') +
'</body></html>';
const file = (title || 'chat').replace(/[\\/:*?"<>|]+/g, '_').slice(0, 80);
const save = (body, ext, type) => {
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([body], { type }));
a.download = `${file}.${ext}`;
a.click();
setTimeout(() => URL.revokeObjectURL(a.href), 5000);
panel.text(`Guardado como ${file}.${ext} (mira tu carpeta de Descargas).`);
};
const pdf = () => {
const w = window.open('', '_blank');
if (!w) { panel.text('El navegador ha bloqueado la ventana nueva. Permite las ventanas emergentes en esta web y vuelve a probar.'); return; }
w.document.write(html);
w.document.close();
w.focus();
w.print();
panel.text('Elige «Guardar como PDF» como impresora en la ventana que se ha abierto.');
};
const copy = () => navigator.clipboard.writeText(md)
.then(() => panel.text('Copiado en Markdown. Pégalo en Notion, Obsidian o cualquier editor.'))
.catch(() => panel.text('El navegador ha bloqueado el portapapeles: usa Markdown.'));
panel.actions(`Listo: ${many(messages.length, 'mensaje')}.${note ? '\n' + note : ''}`, [
['Copiar', false, copy],
['Markdown', false, () => save(md, 'md', 'text/markdown;charset=utf-8')],
['Word', false, () => save('\ufeff' + html, 'doc', 'application/msword')],
['PDF', true, pdf],
]);
};
const sleep = ms => new Promise(r => setTimeout(r, ms));
const turns = () => [...document.querySelectorAll('.conversation-container')];
const first = turns()[0];
let box = first;
while (box && !(box.scrollHeight > box.clientHeight + 50 && /(auto|scroll)/.test(getComputedStyle(box).overflowY))) box = box.parentElement;
if (!first) {
panel.finish('No se han encontrado mensajes. Puede que Gemini haya cambiado su diseño: avísanos en responsive-muse.com.');
return 0;
}
box = box || document.scrollingElement;
// 1. Scroll to the very top until no older messages load
let last = -1, idle = 0;
while (idle < 3) {
box.scrollTop = 0;
await sleep(DELAY);
const n = turns().length;
idle = n === last ? idle + 1 : 0;
last = n;
panel.text('Cargando el principio de la conversación…');
}
// 2. Scroll down, collecting each turn (your prompt, then Gemini's answer) once
const seen = new Map();
const collect = () => {
turns().forEach((t, i) => {
const user = t.querySelector('user-query .query-text, user-query');
const ai = t.querySelector('model-response message-content .markdown, model-response .markdown');
const key = t.id || (user?.textContent || '').slice(0, 200) + '|' + i;
if (seen.has(key)) return;
const parts = [];
if (user) parts.push({ role: 'user', markdown: user.textContent.replace(/^(You said|Has dicho)\s*/i, '').trim() });
if (ai) parts.push({ role: 'ai', markdown: toMarkdown(ai) });
if (parts.length) seen.set(key, parts);
});
};
idle = 0;
while (idle < 3) {
const before = seen.size;
collect();
const atEnd = box.scrollTop + box.clientHeight >= box.scrollHeight - 5;
box.scrollTop += box.clientHeight * 0.8;
await sleep(DELAY);
idle = seen.size === before && atEnd ? idle + 1 : 0;
panel.text(`Leyendo mensajes… ${many([...seen.values()].flat().length, 'mensaje')}`);
}
const messages = [...seen.values()].flat();
if (!messages.length) {
panel.finish('No se han encontrado mensajes. Puede que Gemini haya cambiado su diseño: avísanos en responsive-muse.com.');
return 0;
}
deliverChat(document.title.replace(/\s*-\s*Google Gemini$/, '') || 'Conversación de Gemini', messages, NAMES);
return messages.length;
})();
🛡️ ¿Chrome te dice «No pegues código…»? Es normal. Escribe allow pasting en la consola (o permitir pegar si tu Chrome está en español), pulsa Intro y vuelve a pegar. Solo hace falta una vez.
¿Por qué avisa Chrome?
Chrome lo muestra la primera vez que alguien pega algo en la consola, para proteger de estafas en las que te piden pegar código desconocido. Hace bien en avisar, y por eso este script está entero a la vista: 201 líneas comentadas que no envían nada a ningún sitio ni tocan tus contraseñas o cookies. Por qué es seguro.
¿Prefieres no usar la consola? Arrastra el botón Exportar chat de Gemini a tu barra de marcadores y haz clic en él estando en gemini.google.com. Así no sale el aviso.
Marcador: arrastra el botón del medio a tu barra de marcadores, abre gemini.google.com y pulsa el marcador. Ejecuta el mismo script con las opciones de arriba.
- No envía nada a ningún sitio. Sin fetch, XHR ni scripts externos. La única actividad de red es la de gemini.google.com cargando tu lista.
- No toca tu sesión. No lee cookies, contraseñas, tokens ni el almacenamiento del navegador.
- Solo funciona en gemini.google.com. En cualquier otra web se detiene antes de hacer nada.
- Corto y legible. 201 líneas comentadas, sin minificar ni ofuscar.
- Muestra su progreso en la página. Un pequeño recuadro te dice qué está haciendo y al final te ofrece Descargar o Copiar.
- El mismo código para todos. SHA-256 de la versión por defecto:
c238e78fa2d2a1bd…
Compruébalo tú mismo en 30 segundos
- Léelo: todas las líneas están arriba, con comentarios.
- Busca en el código
fetch,XMLHttpRequest,cookie,localStorageoeval. No aparece ninguna. - Abre la pestaña Red (Network) antes de ejecutarlo: solo verás peticiones a gemini.google.com.
Cómo usarlo
- Abre la conversación en gemini.google.com.
- Abre la consola: F12 o Ctrl+Mayús+J (Cmd+Opción+J en Mac). Si Chrome te lo pide, escribe
permitir pegar(oallow pastingsi lo tienes en inglés) y pulsa Enter. - Copia el script, pégalo en la consola y pulsa Enter. Aparece un recuadro: pulsa Markdown, Word o PDF (o Copiar para pegarlo en Notion u Obsidian).
Toda la conversación, no una respuesta
Gemini solo deja enviar a Google Docs las respuestas de una en una. Este script guarda la conversación completa en un único archivo.
Preguntas frecuentes
¿No puedo exportar a Google Docs desde Gemini?
Gemini puede enviar una sola respuesta a Docs, de una en una. Este script guarda la conversación entera, tus preguntas y todas las respuestas, en un solo archivo.
¿Se conservan el código, las tablas y las fórmulas?
Sí. El código mantiene su lenguaje, las tablas pasan a tablas Markdown y las fórmulas se exportan en TeX ($…$), que Notion, Obsidian y la mayoría de editores Markdown muestran como fórmulas.
¿Cómo saco un PDF?
Pulsa PDF en el recuadro: se abre una vista de impresión limpia. Elige «Guardar como PDF» como impresora. No se sube nada a ningún sitio.
¿Funciona con chats muy largos?
Sí. La web solo mantiene en pantalla parte de un chat largo, así que el script se desplaza desde el principio y recoge cada mensaje por el camino. Deja la pestaña abierta hasta que el recuadro muestre los botones.
