Last active
January 8, 2026 07:32
-
-
Save dpavlin/019de991fc7186390b786269c2f241cd to your computer and use it in GitHub Desktop.
load Google AI studio conversation.json from gdrive backup and display it as single html page
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Full Conversation Report</title> | |
| <!-- Using a lightweight and fast Markdown parser from a CDN --> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| --bg-color: #f8f9fa; | |
| --text-color: #212529; | |
| --border-color: #dee2e6; | |
| --card-bg: #ffffff; | |
| --header-bg: #e9ecef; | |
| --accent-color: #007bff; | |
| --code-bg: #e9ecef; | |
| --code-text: #333; | |
| --user-header: #d1e7dd; | |
| --model-header: #cff4fc; | |
| --thought-header: #fff3cd; | |
| } | |
| body.dark-mode { | |
| --bg-color: #212529; | |
| --text-color: #dee2e6; | |
| --border-color: #495057; | |
| --card-bg: #343a40; | |
| --header-bg: #495057; | |
| --accent-color: #0d6efd; | |
| --code-bg: #495057; | |
| --code-text: #f8f9fa; | |
| --user-header: #0f5132; | |
| --model-header: #055160; | |
| --thought-header: #664d03; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; | |
| margin: 0; | |
| background-color: var(--bg-color); | |
| color: var(--text-color); | |
| line-height: 1.6; | |
| } | |
| .page-header { | |
| padding: 1rem 2rem; | |
| background-color: var(--card-bg); | |
| border-bottom: 1px solid var(--border-color); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .page-header h1 { | |
| margin: 0; | |
| font-size: 1.5rem; | |
| } | |
| .controls button, .controls label { | |
| background-color: var(--accent-color); | |
| color: white; | |
| border: none; | |
| padding: 0.6rem 1.2rem; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-weight: bold; | |
| font-size: 0.9rem; | |
| margin-left: 0.5rem; | |
| } | |
| .controls input[type="file"] { | |
| display: none; | |
| } | |
| #report-container { | |
| padding: 2rem; | |
| max-width: 900px; | |
| margin: 0 auto; | |
| } | |
| .turn-card { | |
| background-color: var(--card-bg); | |
| border: 1px solid var(--border-color); | |
| border-radius: 8px; | |
| margin-bottom: 2rem; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.05); | |
| overflow: hidden; | |
| } | |
| .turn-header { | |
| padding: 0.8rem 1.5rem; | |
| font-weight: bold; | |
| font-size: 1.1rem; | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| .turn-header.user { background-color: var(--user-header); } | |
| .turn-header.model { background-color: var(--model-header); } | |
| .turn-header.thought { background-color: var(--thought-header); } | |
| .turn-body { | |
| padding: 0.5rem 1.5rem 1.5rem 1.5rem; | |
| } | |
| .turn-body h1, .turn-body h2, .turn-body h3 { | |
| border-bottom: 1px solid var(--border-color); | |
| padding-bottom: 0.3em; | |
| margin-top: 1.5em; | |
| } | |
| .turn-body table { border-collapse: collapse; width: 100%; margin: 1em 0; } | |
| .turn-body th, .turn-body td { border: 1px solid var(--border-color); padding: 0.6rem; text-align: left; } | |
| .turn-body th { background-color: var(--header-bg); } | |
| .turn-body code { background-color: var(--code-bg); color: var(--code-text); padding: 0.2em 0.4em; border-radius: 3px; font-size: 0.9em; } | |
| .turn-body pre { background-color: var(--code-bg); color: var(--code-text); padding: 1rem; border-radius: 5px; overflow-x: auto; } | |
| .turn-body pre code { padding: 0; background: none; } | |
| .metadata { | |
| font-size: 0.9em; | |
| padding: 1rem 1.5rem; | |
| border-top: 1px dashed var(--border-color); | |
| background: var(--header-bg); | |
| } | |
| .raw-json details { | |
| margin-top: 1rem; | |
| } | |
| .raw-json summary { | |
| cursor: pointer; | |
| font-weight: bold; | |
| color: var(--accent-color); | |
| } | |
| .placeholder { | |
| text-align: center; | |
| color: #6c757d; | |
| font-size: 1.2rem; | |
| padding: 4rem; | |
| } | |
| @media print { | |
| body { | |
| --bg-color: #ffffff; | |
| --text-color: #000000; | |
| --border-color: #cccccc; | |
| --card-bg: #ffffff; | |
| --user-header: #e9f5e9; | |
| --model-header: #e1f5fe; | |
| --thought-header: #fff8e1; | |
| } | |
| .page-header { | |
| display: none; | |
| } | |
| #report-container { | |
| padding: 0; | |
| max-width: 100%; | |
| box-shadow: none; | |
| } | |
| .turn-card { | |
| box-shadow: none; | |
| border: 1px solid #ccc; | |
| page-break-inside: avoid; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="page-header"> | |
| <h1>Full Conversation Report</h1> | |
| <div class="controls"> | |
| <label for="file-input">Load conversation.json</label> | |
| <input type="file" id="file-input" accept=".json"> | |
| <button id="print-button">Print Report</button> | |
| <button id="dark-mode-toggle">Toggle Dark Mode</button> | |
| </div> | |
| </header> | |
| <main id="report-container"> | |
| <div class="placeholder">Load a `conversation.json` file to generate the report.</div> | |
| </main> | |
| <!-- Template for a single conversation turn --> | |
| <template id="turn-template"> | |
| <div class="turn-card"> | |
| <div class="turn-header"></div> | |
| <div class="turn-body"> | |
| <div class="attachments"></div> | |
| <div class="main-text"></div> | |
| <div class="grounding"></div> | |
| <div class="raw-json"> | |
| <details> | |
| <summary>Show Raw JSON</summary> | |
| <pre><code></code></pre> | |
| </details> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const fileInput = document.getElementById('file-input'); | |
| const reportContainer = document.getElementById('report-container'); | |
| const turnTemplate = document.getElementById('turn-template'); | |
| document.getElementById('dark-mode-toggle').addEventListener('click', () => { | |
| document.body.classList.toggle('dark-mode'); | |
| }); | |
| document.getElementById('print-button').addEventListener('click', () => { | |
| window.print(); | |
| }); | |
| fileInput.addEventListener('change', (event) => { | |
| const file = event.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| try { | |
| const data = JSON.parse(e.target.result); | |
| generateReport(data); | |
| } catch (error) { | |
| alert(`Error reading or parsing file: ${error.message}`); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| }); | |
| function generateReport(data) { | |
| const chunks = data?.chunkedPrompt?.chunks; | |
| if (!chunks || !Array.isArray(chunks)) { | |
| reportContainer.innerHTML = '<div class="placeholder">Invalid JSON format. Expected a `chunkedPrompt.chunks` array.</div>'; | |
| return; | |
| } | |
| reportContainer.innerHTML = ''; // Clear placeholder | |
| chunks.forEach((chunk, index) => { | |
| const card = turnTemplate.content.cloneNode(true); | |
| const header = card.querySelector('.turn-header'); | |
| const mainText = card.querySelector('.main-text'); | |
| const attachmentsContainer = card.querySelector('.attachments'); | |
| const groundingContainer = card.querySelector('.grounding'); | |
| const rawJsonCode = card.querySelector('.raw-json pre code'); | |
| const role = chunk.role || 'unknown'; | |
| const isThought = chunk.isThought || false; | |
| // --- Header --- | |
| let headerText = `${index + 1}. ${role.toUpperCase()}`; | |
| header.classList.add(role); | |
| if (isThought) { | |
| headerText += ' (Thought)'; | |
| header.classList.add('thought'); | |
| } | |
| header.textContent = headerText; | |
| // --- Attachments --- | |
| let attachmentsHtml = ''; | |
| if (chunk.driveImage) { | |
| attachmentsHtml += `<p><strong>Attachment (Image):</strong> <code>${chunk.driveImage.id}</code></p>`; | |
| } | |
| if (chunk.driveDocument) { | |
| attachmentsHtml += `<p><strong>Attachment (Document):</strong> <code>${chunk.driveDocument.id}</code></p>`; | |
| } | |
| attachmentsContainer.innerHTML = attachmentsHtml; | |
| // --- Main Text --- | |
| if (chunk.text) { | |
| mainText.innerHTML = marked.parse(chunk.text); | |
| } | |
| // --- Grounding --- | |
| let groundingHtml = ''; | |
| if (chunk.grounding) { | |
| groundingHtml += '<h4>Grounding Details</h4>'; | |
| const g = chunk.grounding; | |
| if (g.webSearchQueries?.length) { | |
| groundingHtml += `<p><strong>Search Queries:</strong> ${g.webSearchQueries.map(q => `<code>${q}</code>`).join(', ')}</p>`; | |
| } | |
| if (g.groundingSources?.length) { | |
| groundingHtml += '<h5>Sources:</h5><ul>'; | |
| g.groundingSources.forEach(src => { | |
| groundingHtml += `<li><a href="${src.uri}" target="_blank" rel="noopener noreferrer">${src.title || src.uri}</a></li>`; | |
| }); | |
| groundingHtml += '</ul>'; | |
| } | |
| } | |
| groundingContainer.innerHTML = groundingHtml; | |
| if (!groundingHtml) groundingContainer.style.display = 'none'; | |
| // --- Raw JSON --- | |
| rawJsonCode.textContent = JSON.stringify(chunk, null, 2); | |
| reportContainer.appendChild(card); | |
| }); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment