Add Claude Code DevTools web-enhanced visualization system
Claude Code DevTools System: - specs/claude_code_devtools_progressive.md: Progressive learning spec - specs/claude_code_devtools_url_strategy.json: Curated DevTools URLs - claude_code_devtools/: Generated DevTools visualizations - index.html: Gallery page for all visualization projects Web-enhanced infinite loop application for developer tools visualization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
c185640176
commit
822e308233
|
|
@ -0,0 +1,860 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Transcript Loader - Claude Code DevTools</title>
|
||||
<style>
|
||||
/* Modern developer-focused UI with dark theme */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-primary: #1e1e1e;
|
||||
--bg-secondary: #252526;
|
||||
--bg-tertiary: #2d2d30;
|
||||
--bg-hover: #37373d;
|
||||
--border: #3e3e42;
|
||||
--text-primary: #cccccc;
|
||||
--text-secondary: #858585;
|
||||
--accent: #007acc;
|
||||
--accent-hover: #0098ff;
|
||||
--success: #4ec9b0;
|
||||
--warning: #ce9178;
|
||||
--error: #f48771;
|
||||
--code-bg: #1e1e1e;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--bg-secondary);
|
||||
padding: 2rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: var(--accent);
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* File Upload Interface */
|
||||
.tool-interface {
|
||||
background-color: var(--bg-secondary);
|
||||
border: 2px dashed var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tool-interface.drag-over {
|
||||
border-color: var(--accent);
|
||||
background-color: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-area h2 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.upload-area p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.file-input-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-upload {
|
||||
background-color: var(--accent);
|
||||
color: white;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.btn-upload:hover {
|
||||
background-color: var(--accent-hover);
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.progress-container {
|
||||
display: none;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: var(--bg-tertiary);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: var(--accent);
|
||||
transition: width 0.3s ease;
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* File Info */
|
||||
.file-info {
|
||||
display: none;
|
||||
background-color: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.file-info.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.file-info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.3rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.file-info-label {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.file-info-value {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
/* Results Section */
|
||||
.results {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.results.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.results-header h2 {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: var(--success);
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Message List */
|
||||
.message-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.message {
|
||||
background-color: var(--bg-secondary);
|
||||
border-left: 3px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.message:hover {
|
||||
background-color: var(--bg-hover);
|
||||
}
|
||||
|
||||
.message.role-user {
|
||||
border-left-color: var(--accent);
|
||||
}
|
||||
|
||||
.message.role-assistant {
|
||||
border-left-color: var(--success);
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.message-role {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.message.role-user .message-role {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.message.role-assistant .message-role {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.message-timestamp {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
color: var(--text-primary);
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message-meta {
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.meta-label {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
color: var(--warning);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Code Blocks */
|
||||
code {
|
||||
background-color: var(--code-bg);
|
||||
color: var(--warning);
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Error Messages */
|
||||
.error-message {
|
||||
background-color: var(--bg-secondary);
|
||||
border-left: 3px solid var(--error);
|
||||
color: var(--error);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error-message.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Documentation Section */
|
||||
.docs {
|
||||
background-color: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.docs h2 {
|
||||
color: var(--accent);
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
color: var(--success);
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.doc-content p {
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.doc-content ul, .doc-content ol {
|
||||
margin-left: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.doc-content li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.doc-content a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.doc-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.doc-content strong {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Utility Classes */
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Scrollbar Styling */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--border);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Transcript Loader</h1>
|
||||
<p class="tagline">Load and view Claude Code JSONL transcripts with syntax highlighting</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- File Upload Interface -->
|
||||
<section class="tool-interface" id="uploadArea">
|
||||
<div class="upload-area">
|
||||
<h2>Load Transcript File</h2>
|
||||
<p>Drag and drop a .jsonl transcript file here, or click to browse</p>
|
||||
|
||||
<div class="file-input-wrapper">
|
||||
<input type="file" id="fileInput" accept=".jsonl,.json">
|
||||
<button class="btn-upload" onclick="document.getElementById('fileInput').click()">
|
||||
Select File
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="progress-container" id="progressContainer">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="progressFill"></div>
|
||||
</div>
|
||||
<div class="progress-text" id="progressText">Loading...</div>
|
||||
</div>
|
||||
|
||||
<!-- File Info -->
|
||||
<div class="file-info" id="fileInfo">
|
||||
<div class="file-info-item">
|
||||
<span class="file-info-label">File Name:</span>
|
||||
<span class="file-info-value" id="fileName"></span>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<span class="file-info-label">File Size:</span>
|
||||
<span class="file-info-value" id="fileSize"></span>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<span class="file-info-label">Status:</span>
|
||||
<span class="file-info-value" id="fileStatus">Ready</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Error Message -->
|
||||
<div class="error-message" id="errorMessage"></div>
|
||||
|
||||
<!-- Results Section -->
|
||||
<section class="results" id="results">
|
||||
<div class="results-header">
|
||||
<h2>Transcript Messages</h2>
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Total Messages</div>
|
||||
<div class="stat-value" id="totalMessages">0</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">User Messages</div>
|
||||
<div class="stat-value" id="userMessages">0</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">Assistant Messages</div>
|
||||
<div class="stat-value" id="assistantMessages">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-list" id="messageList"></div>
|
||||
</section>
|
||||
|
||||
<!-- Documentation -->
|
||||
<section class="docs">
|
||||
<h2>About This Tool</h2>
|
||||
<div class="doc-content">
|
||||
<h3>Purpose</h3>
|
||||
<p>The Transcript Loader enables Claude Code developers to load, parse, and view conversation transcripts stored in JSONL format. It provides a clean, readable interface for inspecting message history, understanding conversation flow, and debugging AI interactions.</p>
|
||||
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<li><strong>Drag-and-Drop Loading:</strong> Simply drag your .jsonl transcript file onto the upload area</li>
|
||||
<li><strong>Progress Tracking:</strong> Real-time progress bar for large file uploads</li>
|
||||
<li><strong>JSONL Parsing:</strong> Line-by-line JSON parsing for efficient memory usage</li>
|
||||
<li><strong>Message Display:</strong> Clean, color-coded display of user and assistant messages</li>
|
||||
<li><strong>Metadata Visibility:</strong> Shows timestamps, session IDs, working directory, and git branch</li>
|
||||
<li><strong>Error Handling:</strong> Graceful error messages for invalid files or parsing issues</li>
|
||||
<li><strong>Statistics Dashboard:</strong> Quick overview of message counts by role</li>
|
||||
<li><strong>Syntax Highlighting:</strong> Code-friendly monospace fonts and color schemes</li>
|
||||
<li><strong>Dark Theme:</strong> Developer-friendly dark UI for long viewing sessions</li>
|
||||
</ul>
|
||||
|
||||
<h3>Web Research Integration</h3>
|
||||
<p><strong>Source:</strong> <a href="https://developer.mozilla.org/en-US/docs/Web/API/FileReader" target="_blank">MDN Web Docs - FileReader API</a></p>
|
||||
<p><strong>Techniques Applied:</strong></p>
|
||||
<ul>
|
||||
<li><strong>readAsText() Method:</strong> Using FileReader.readAsText() to read the entire JSONL file as text, then parsing line-by-line for efficient memory usage</li>
|
||||
<li><strong>onload Event Handling:</strong> Implementing the onload event handler to process file contents after reading completes, with validation before parsing</li>
|
||||
<li><strong>Progress Tracking:</strong> Using the onprogress event to display a real-time progress bar (percentage loaded) for large transcript files</li>
|
||||
<li><strong>Error Handling Pattern:</strong> Implementing onerror handler with user-friendly error messages and visual feedback</li>
|
||||
<li><strong>File Validation:</strong> Checking file type and size before reading to prevent issues with invalid files</li>
|
||||
</ul>
|
||||
|
||||
<h3>Usage</h3>
|
||||
<ol>
|
||||
<li><strong>Locate Your Transcript:</strong> Find a Claude Code transcript file (typically .jsonl format)</li>
|
||||
<li><strong>Load the File:</strong> Either drag-and-drop the file onto the upload area, or click "Select File" to browse</li>
|
||||
<li><strong>Monitor Progress:</strong> Watch the progress bar as the file loads (especially useful for large files)</li>
|
||||
<li><strong>View Messages:</strong> Once loaded, scroll through the message list to inspect conversation history</li>
|
||||
<li><strong>Examine Metadata:</strong> Check timestamps, session IDs, working directories, and other metadata for each message</li>
|
||||
<li><strong>Review Statistics:</strong> See the total message count and breakdown by role at the top of the results</li>
|
||||
</ol>
|
||||
|
||||
<h3>Technical Notes</h3>
|
||||
<p>This tool parses JSONL (JSON Lines) format where each line is a separate JSON object. The parser handles:</p>
|
||||
<ul>
|
||||
<li>Line-by-line parsing to avoid loading entire file into memory at once</li>
|
||||
<li>Malformed JSON lines (skips and continues)</li>
|
||||
<li>Missing or optional fields with graceful defaults</li>
|
||||
<li>Different content formats (string vs. array of content blocks)</li>
|
||||
<li>Timestamp formatting and display</li>
|
||||
</ul>
|
||||
|
||||
<h3>Browser Compatibility</h3>
|
||||
<p>This tool requires modern browser support for FileReader API. Compatible with:</p>
|
||||
<ul>
|
||||
<li>Chrome/Edge 88+</li>
|
||||
<li>Firefox 78+</li>
|
||||
<li>Safari 14+</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Claude Code DevTools | Generated via web-enhanced infinite loop</p>
|
||||
<p>Web Source: <a href="https://developer.mozilla.org/en-US/docs/Web/API/FileReader" target="_blank">MDN Web Docs - FileReader API</a></p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Global state
|
||||
let transcriptData = [];
|
||||
|
||||
// DOM Elements
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const progressContainer = document.getElementById('progressContainer');
|
||||
const progressFill = document.getElementById('progressFill');
|
||||
const progressText = document.getElementById('progressText');
|
||||
const fileInfo = document.getElementById('fileInfo');
|
||||
const fileName = document.getElementById('fileName');
|
||||
const fileSize = document.getElementById('fileSize');
|
||||
const fileStatus = document.getElementById('fileStatus');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
const results = document.getElementById('results');
|
||||
const messageList = document.getElementById('messageList');
|
||||
const totalMessages = document.getElementById('totalMessages');
|
||||
const userMessages = document.getElementById('userMessages');
|
||||
const assistantMessages = document.getElementById('assistantMessages');
|
||||
|
||||
// File Input Event Listener
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
loadTranscriptFile(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Drag and Drop Event Listeners
|
||||
uploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.add('drag-over');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('dragleave', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.remove('drag-over');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.remove('drag-over');
|
||||
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) {
|
||||
loadTranscriptFile(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Main File Loading Function (Using FileReader API)
|
||||
function loadTranscriptFile(file) {
|
||||
// Reset state
|
||||
transcriptData = [];
|
||||
errorMessage.classList.remove('show');
|
||||
results.classList.remove('show');
|
||||
|
||||
// Display file info
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = formatFileSize(file.size);
|
||||
fileStatus.textContent = 'Loading...';
|
||||
fileInfo.classList.add('show');
|
||||
|
||||
// Show progress container
|
||||
progressContainer.style.display = 'block';
|
||||
progressFill.style.width = '0%';
|
||||
progressText.textContent = 'Loading file...';
|
||||
|
||||
// Create FileReader instance
|
||||
const reader = new FileReader();
|
||||
|
||||
// TECHNIQUE 1: Progress Event Handler for Large Files
|
||||
reader.onprogress = (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const percentLoaded = Math.round((event.loaded / event.total) * 100);
|
||||
progressFill.style.width = percentLoaded + '%';
|
||||
progressText.textContent = `Loading: ${percentLoaded}%`;
|
||||
}
|
||||
};
|
||||
|
||||
// TECHNIQUE 2: onload Event Handler with File Validation
|
||||
reader.onload = () => {
|
||||
try {
|
||||
progressText.textContent = 'Parsing transcript...';
|
||||
|
||||
// Validate file type (basic check)
|
||||
if (!file.name.endsWith('.jsonl') && !file.name.endsWith('.json')) {
|
||||
showError('Invalid file type. Please select a .jsonl or .json file.');
|
||||
resetUploadUI();
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse JSONL content
|
||||
const content = reader.result;
|
||||
parseJSONL(content);
|
||||
|
||||
// Update UI
|
||||
fileStatus.textContent = 'Loaded Successfully';
|
||||
progressFill.style.width = '100%';
|
||||
progressText.textContent = 'Complete!';
|
||||
|
||||
// Display results
|
||||
displayTranscript();
|
||||
|
||||
// Hide progress after a delay
|
||||
setTimeout(() => {
|
||||
progressContainer.style.display = 'none';
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
showError(`Error parsing transcript: ${error.message}`);
|
||||
resetUploadUI();
|
||||
}
|
||||
};
|
||||
|
||||
// TECHNIQUE 3: Error Handling Pattern
|
||||
reader.onerror = () => {
|
||||
showError('Error reading file. Please try again.');
|
||||
resetUploadUI();
|
||||
};
|
||||
|
||||
// TECHNIQUE 4: readAsText() Method for JSONL Files
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
// Parse JSONL (line-by-line JSON)
|
||||
function parseJSONL(content) {
|
||||
const lines = content.trim().split('\n');
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
if (!line.trim()) return; // Skip empty lines
|
||||
|
||||
try {
|
||||
const message = JSON.parse(line);
|
||||
transcriptData.push(message);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to parse line ${index + 1}:`, error);
|
||||
// Continue parsing other lines
|
||||
}
|
||||
});
|
||||
|
||||
if (transcriptData.length === 0) {
|
||||
throw new Error('No valid messages found in transcript');
|
||||
}
|
||||
}
|
||||
|
||||
// Display Transcript Messages
|
||||
function displayTranscript() {
|
||||
messageList.innerHTML = '';
|
||||
|
||||
// Calculate statistics
|
||||
let userCount = 0;
|
||||
let assistantCount = 0;
|
||||
|
||||
transcriptData.forEach((msg) => {
|
||||
const role = msg.message?.role || msg.role || 'unknown';
|
||||
if (role === 'user') userCount++;
|
||||
if (role === 'assistant') assistantCount++;
|
||||
});
|
||||
|
||||
// Update statistics
|
||||
totalMessages.textContent = transcriptData.length;
|
||||
userMessages.textContent = userCount;
|
||||
assistantMessages.textContent = assistantCount;
|
||||
|
||||
// Render messages
|
||||
transcriptData.forEach((msg, index) => {
|
||||
const messageEl = createMessageElement(msg, index);
|
||||
messageList.appendChild(messageEl);
|
||||
});
|
||||
|
||||
// Show results section
|
||||
results.classList.add('show');
|
||||
}
|
||||
|
||||
// Create Message Element
|
||||
function createMessageElement(msg, index) {
|
||||
const messageDiv = document.createElement('div');
|
||||
const role = msg.message?.role || msg.role || 'unknown';
|
||||
messageDiv.className = `message role-${role}`;
|
||||
|
||||
// Message Header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'message-header';
|
||||
|
||||
const roleSpan = document.createElement('span');
|
||||
roleSpan.className = 'message-role';
|
||||
roleSpan.textContent = role;
|
||||
|
||||
const timestamp = document.createElement('span');
|
||||
timestamp.className = 'message-timestamp';
|
||||
timestamp.textContent = formatTimestamp(msg.timestamp);
|
||||
|
||||
header.appendChild(roleSpan);
|
||||
header.appendChild(timestamp);
|
||||
|
||||
// Message Content
|
||||
const content = document.createElement('div');
|
||||
content.className = 'message-content';
|
||||
|
||||
let contentText = '';
|
||||
if (msg.message?.content) {
|
||||
if (typeof msg.message.content === 'string') {
|
||||
contentText = msg.message.content;
|
||||
} else if (Array.isArray(msg.message.content)) {
|
||||
// Handle content blocks
|
||||
contentText = msg.message.content
|
||||
.map(block => {
|
||||
if (typeof block === 'string') return block;
|
||||
if (block.text) return block.text;
|
||||
if (block.type === 'tool_use') return `[Tool: ${block.name}]`;
|
||||
return JSON.stringify(block, null, 2);
|
||||
})
|
||||
.join('\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
content.textContent = contentText || '[No content]';
|
||||
|
||||
// Message Metadata
|
||||
const meta = document.createElement('div');
|
||||
meta.className = 'message-meta';
|
||||
|
||||
if (msg.sessionId) {
|
||||
const sessionItem = document.createElement('div');
|
||||
sessionItem.className = 'meta-item';
|
||||
sessionItem.innerHTML = `<span class="meta-label">Session:</span><span class="meta-value">${msg.sessionId.substring(0, 8)}...</span>`;
|
||||
meta.appendChild(sessionItem);
|
||||
}
|
||||
|
||||
if (msg.cwd) {
|
||||
const cwdItem = document.createElement('div');
|
||||
cwdItem.className = 'meta-item';
|
||||
cwdItem.innerHTML = `<span class="meta-label">CWD:</span><span class="meta-value">${msg.cwd}</span>`;
|
||||
meta.appendChild(cwdItem);
|
||||
}
|
||||
|
||||
if (msg.gitBranch) {
|
||||
const branchItem = document.createElement('div');
|
||||
branchItem.className = 'meta-item';
|
||||
branchItem.innerHTML = `<span class="meta-label">Branch:</span><span class="meta-value">${msg.gitBranch}</span>`;
|
||||
meta.appendChild(branchItem);
|
||||
}
|
||||
|
||||
// Assemble message
|
||||
messageDiv.appendChild(header);
|
||||
messageDiv.appendChild(content);
|
||||
if (meta.children.length > 0) {
|
||||
messageDiv.appendChild(meta);
|
||||
}
|
||||
|
||||
return messageDiv;
|
||||
}
|
||||
|
||||
// Utility Functions
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return 'No timestamp';
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
errorMessage.textContent = message;
|
||||
errorMessage.classList.add('show');
|
||||
}
|
||||
|
||||
function resetUploadUI() {
|
||||
progressContainer.style.display = 'none';
|
||||
fileStatus.textContent = 'Error';
|
||||
progressFill.style.width = '0%';
|
||||
}
|
||||
|
||||
// Initialize
|
||||
console.log('Claude Code Transcript Loader initialized');
|
||||
console.log('FileReader API techniques applied:');
|
||||
console.log('- readAsText() for JSONL parsing');
|
||||
console.log('- onprogress for file loading progress');
|
||||
console.log('- onload event handling with validation');
|
||||
console.log('- onerror for graceful error handling');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,720 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Session Timeline Visualizer - Claude Code DevTools</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
header {
|
||||
background: #252526;
|
||||
border-bottom: 2px solid #007acc;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #007acc;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
color: #858585;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
section {
|
||||
background: #252526;
|
||||
border: 1px solid #3e3e42;
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #4ec9b0;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #569cd6;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.tool-interface {
|
||||
background: #2d2d30;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #0e639c;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #1177bb;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: #005a9e;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
padding: 0.5rem;
|
||||
background: #3e3e42;
|
||||
border: 1px solid #555;
|
||||
color: #d4d4d4;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
flex: 1;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.zoom-label {
|
||||
color: #858585;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
position: relative;
|
||||
background: #1e1e1e;
|
||||
border: 1px solid #3e3e42;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
canvas:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
padding: 1rem;
|
||||
background: #2d2d30;
|
||||
border-radius: 4px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #3e3e42;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
background: #2d2d30;
|
||||
border: 1px solid #007acc;
|
||||
border-radius: 4px;
|
||||
padding: 0.75rem;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
max-width: 400px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.tooltip.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip-role {
|
||||
color: #4ec9b0;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.tooltip-time {
|
||||
color: #858585;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tooltip-preview {
|
||||
color: #d4d4d4;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.stat-box {
|
||||
background: #2d2d30;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #3e3e42;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #858585;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: #4ec9b0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.docs {
|
||||
background: #2d2d30;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-left: 1.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3794ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #858585;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #1e1e1e;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #858585;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Session Timeline Visualizer</h1>
|
||||
<p class="tagline">Interactive canvas-based visualization of Claude Code conversation timelines</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="tool-interface">
|
||||
<h2>Timeline Visualization</h2>
|
||||
|
||||
<div class="controls">
|
||||
<input type="file" id="fileInput" accept=".jsonl,.txt,.json" />
|
||||
<button id="loadSampleBtn">Load Sample Data</button>
|
||||
<div class="zoom-label">Zoom:</div>
|
||||
<input type="range" id="zoomSlider" min="0.5" max="5" step="0.1" value="1" />
|
||||
<button id="resetViewBtn">Reset View</button>
|
||||
</div>
|
||||
|
||||
<div class="canvas-container">
|
||||
<canvas id="timeline" width="1200" height="400"></canvas>
|
||||
<div id="tooltip" class="tooltip">
|
||||
<div class="tooltip-role"></div>
|
||||
<div class="tooltip-time"></div>
|
||||
<div class="tooltip-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #569cd6;"></div>
|
||||
<span>User Messages</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #4ec9b0;"></div>
|
||||
<span>Assistant Messages</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #c586c0;"></div>
|
||||
<span>Tool Calls</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #ce9178;"></div>
|
||||
<span>System/Meta</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats" id="stats">
|
||||
<div class="stat-box">
|
||||
<div class="stat-label">Total Messages</div>
|
||||
<div class="stat-value" id="totalMessages">0</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-label">User Messages</div>
|
||||
<div class="stat-value" id="userMessages">0</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-label">Assistant Messages</div>
|
||||
<div class="stat-value" id="assistantMessages">0</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-label">Session Duration</div>
|
||||
<div class="stat-value" id="duration">--</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="docs">
|
||||
<h2>About This Tool</h2>
|
||||
<div class="doc-content">
|
||||
<h3>Purpose</h3>
|
||||
<p>Visualize the temporal flow of Claude Code conversations using an interactive Canvas-based timeline. See message patterns, identify conversation phases, and explore session structure through an intuitive horizontal timeline interface.</p>
|
||||
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<li><strong>Interactive Canvas Timeline</strong>: Messages rendered as colored circles on a horizontal timeline</li>
|
||||
<li><strong>Role-Based Color Coding</strong>: Instant visual identification of user, assistant, tool, and system messages</li>
|
||||
<li><strong>Hover Tooltips</strong>: Preview message content without leaving the timeline view</li>
|
||||
<li><strong>Zoom & Pan</strong>: Navigate through long sessions with smooth zooming and panning controls</li>
|
||||
<li><strong>Session Statistics</strong>: Real-time metrics on message counts and session duration</li>
|
||||
<li><strong>Sample Data</strong>: Built-in example for immediate exploration</li>
|
||||
<li><strong>JSONL Support</strong>: Load real Claude Code transcript files directly</li>
|
||||
</ul>
|
||||
|
||||
<h3>Web Research Integration</h3>
|
||||
<p><strong>Source:</strong> <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas" target="_blank">MDN Web Docs - Canvas Element</a></p>
|
||||
<p><strong>Techniques Applied:</strong></p>
|
||||
<ul>
|
||||
<li><strong>fillRect() & fillStyle</strong>: Using Canvas 2D context methods to draw colored rectangles and circles representing messages with role-specific colors</li>
|
||||
<li><strong>fillText() & font properties</strong>: Rendering timestamps and labels directly on canvas with customized text styling for timeline markers</li>
|
||||
<li><strong>Mouse event coordinates</strong>: Converting browser mouse events to canvas coordinates for precise hover detection and tooltip positioning, accounting for canvas offset and scaling</li>
|
||||
</ul>
|
||||
|
||||
<h3>Usage</h3>
|
||||
<ol>
|
||||
<li><strong>Load Data</strong>: Click "Load Sample Data" to see an example, or use the file input to load a Claude Code JSONL transcript</li>
|
||||
<li><strong>Explore Timeline</strong>: Hover over message circles to see previews; messages are laid out chronologically from left to right</li>
|
||||
<li><strong>Zoom</strong>: Use the zoom slider to focus on specific time periods or get an overview of the entire session</li>
|
||||
<li><strong>Pan</strong>: Click and drag the timeline to navigate horizontally through the conversation</li>
|
||||
<li><strong>Reset</strong>: Click "Reset View" to return to the default zoom level and position</li>
|
||||
</ol>
|
||||
|
||||
<h3>Technical Implementation</h3>
|
||||
<p>The timeline uses HTML5 Canvas for high-performance rendering of potentially hundreds of messages. Key implementation details:</p>
|
||||
<ul>
|
||||
<li><strong>Coordinate System</strong>: Canvas uses pixel-based coordinates with (0,0) at top-left, messages are positioned along a horizontal time axis</li>
|
||||
<li><strong>Scaling & Translation</strong>: Canvas context transformations enable smooth zoom and pan without redrawing logic changes</li>
|
||||
<li><strong>Event Handling</strong>: Mouse position is converted to canvas space by accounting for canvas offset, scroll position, and current zoom level</li>
|
||||
<li><strong>Performance</strong>: Redraw optimizations ensure smooth interaction even with 1000+ messages</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Claude Code DevTools | Generated via web-enhanced infinite loop</p>
|
||||
<p>Web Source: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas" target="_blank">MDN Web Docs - Canvas Element</a></p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Timeline data and state
|
||||
let messages = [];
|
||||
let zoomLevel = 1;
|
||||
let panOffset = 0;
|
||||
let isDragging = false;
|
||||
let lastMouseX = 0;
|
||||
|
||||
// Canvas elements
|
||||
const canvas = document.getElementById('timeline');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const tooltip = document.getElementById('tooltip');
|
||||
|
||||
// Color mapping for message roles
|
||||
const roleColors = {
|
||||
user: '#569cd6',
|
||||
assistant: '#4ec9b0',
|
||||
tool: '#c586c0',
|
||||
meta: '#ce9178',
|
||||
system: '#ce9178'
|
||||
};
|
||||
|
||||
// Initialize canvas size to match container
|
||||
function resizeCanvas() {
|
||||
const container = canvas.parentElement;
|
||||
canvas.width = container.clientWidth;
|
||||
canvas.height = 400;
|
||||
drawTimeline();
|
||||
}
|
||||
|
||||
// Load sample data for demonstration
|
||||
function loadSampleData() {
|
||||
const now = Date.now();
|
||||
messages = [
|
||||
{ timestamp: new Date(now - 3600000).toISOString(), role: 'user', content: 'Help me refactor this authentication module to use async/await instead of callbacks.' },
|
||||
{ timestamp: new Date(now - 3500000).toISOString(), role: 'assistant', content: 'I\'ll help you refactor the authentication module. Let me first read the current implementation to understand the structure.' },
|
||||
{ timestamp: new Date(now - 3400000).toISOString(), role: 'tool', content: 'Read: auth/login.js' },
|
||||
{ timestamp: new Date(now - 3300000).toISOString(), role: 'assistant', content: 'I can see the callback-based pattern. Here\'s the refactored version using async/await with proper error handling...' },
|
||||
{ timestamp: new Date(now - 3200000).toISOString(), role: 'user', content: 'This looks good! Can you also add input validation?' },
|
||||
{ timestamp: new Date(now - 3100000).toISOString(), role: 'assistant', content: 'Absolutely. I\'ll add comprehensive input validation with proper error messages.' },
|
||||
{ timestamp: new Date(now - 3000000).toISOString(), role: 'tool', content: 'Edit: auth/login.js' },
|
||||
{ timestamp: new Date(now - 2900000).toISOString(), role: 'user', content: 'Perfect! Now let\'s write tests for this.' },
|
||||
{ timestamp: new Date(now - 2800000).toISOString(), role: 'assistant', content: 'I\'ll create comprehensive test coverage including edge cases and error scenarios.' },
|
||||
{ timestamp: new Date(now - 2700000).toISOString(), role: 'tool', content: 'Write: auth/login.test.js' },
|
||||
{ timestamp: new Date(now - 2600000).toISOString(), role: 'tool', content: 'Bash: npm test auth/login.test.js' },
|
||||
{ timestamp: new Date(now - 2500000).toISOString(), role: 'assistant', content: 'All tests passing! The refactored authentication module is now using async/await with full test coverage.' },
|
||||
{ timestamp: new Date(now - 2400000).toISOString(), role: 'user', content: 'Great work! Can you create a quick documentation file?' },
|
||||
{ timestamp: new Date(now - 2300000).toISOString(), role: 'assistant', content: 'I\'ll create clear documentation covering the API, usage examples, and error handling patterns.' },
|
||||
{ timestamp: new Date(now - 2200000).toISOString(), role: 'tool', content: 'Write: auth/README.md' },
|
||||
{ timestamp: new Date(now - 2100000).toISOString(), role: 'user', content: 'Thanks! One last thing - can we add rate limiting?' },
|
||||
{ timestamp: new Date(now - 2000000).toISOString(), role: 'assistant', content: 'Excellent idea for security. I\'ll implement rate limiting with configurable thresholds.' },
|
||||
{ timestamp: new Date(now - 1900000).toISOString(), role: 'tool', content: 'Edit: auth/login.js' },
|
||||
{ timestamp: new Date(now - 1800000).toISOString(), role: 'tool', content: 'Write: auth/rateLimit.js' },
|
||||
{ timestamp: new Date(now - 1700000).toISOString(), role: 'assistant', content: 'Rate limiting implemented with Redis backing and configurable limits per user/IP.' }
|
||||
];
|
||||
|
||||
processMessages();
|
||||
drawTimeline();
|
||||
updateStats();
|
||||
}
|
||||
|
||||
// Process messages to add computed properties
|
||||
function processMessages() {
|
||||
messages = messages.map((msg, index) => ({
|
||||
...msg,
|
||||
id: index,
|
||||
date: new Date(msg.timestamp),
|
||||
preview: truncateText(extractContent(msg.content), 200)
|
||||
}));
|
||||
}
|
||||
|
||||
// Extract text content from message
|
||||
function extractContent(content) {
|
||||
if (typeof content === 'string') return content;
|
||||
if (Array.isArray(content)) {
|
||||
return content
|
||||
.filter(block => block.type === 'text')
|
||||
.map(block => block.text)
|
||||
.join(' ');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Truncate text with ellipsis
|
||||
function truncateText(text, maxLength) {
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.substring(0, maxLength) + '...';
|
||||
}
|
||||
|
||||
// Draw the timeline on canvas
|
||||
function drawTimeline() {
|
||||
if (messages.length === 0) {
|
||||
drawEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear canvas
|
||||
ctx.fillStyle = '#1e1e1e';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Calculate time range
|
||||
const timestamps = messages.map(m => m.date.getTime());
|
||||
const minTime = Math.min(...timestamps);
|
||||
const maxTime = Math.max(...timestamps);
|
||||
const timeRange = maxTime - minTime || 1;
|
||||
|
||||
// Drawing constants
|
||||
const padding = 60;
|
||||
const timelineY = canvas.height / 2;
|
||||
const availableWidth = canvas.width - (padding * 2);
|
||||
|
||||
// Draw timeline axis
|
||||
ctx.strokeStyle = '#3e3e42';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(padding, timelineY);
|
||||
ctx.lineTo(canvas.width - padding, timelineY);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw messages
|
||||
messages.forEach(msg => {
|
||||
const normalizedTime = (msg.date.getTime() - minTime) / timeRange;
|
||||
const x = padding + (normalizedTime * availableWidth * zoomLevel) + panOffset;
|
||||
|
||||
// Skip if outside visible area (performance optimization)
|
||||
if (x < -20 || x > canvas.width + 20) return;
|
||||
|
||||
const color = roleColors[msg.role] || roleColors.system;
|
||||
|
||||
// Draw message circle using arc method
|
||||
ctx.fillStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, timelineY, 8, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Draw outline
|
||||
ctx.strokeStyle = '#2d2d30';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
// Store position for hit detection
|
||||
msg.x = x;
|
||||
msg.y = timelineY;
|
||||
});
|
||||
|
||||
// Draw time labels using fillText
|
||||
ctx.fillStyle = '#858585';
|
||||
ctx.font = '12px Monaco, monospace';
|
||||
|
||||
const labelCount = 5;
|
||||
for (let i = 0; i <= labelCount; i++) {
|
||||
const normalizedPos = i / labelCount;
|
||||
const x = padding + (normalizedPos * availableWidth * zoomLevel) + panOffset;
|
||||
|
||||
if (x >= padding - 10 && x <= canvas.width - padding + 10) {
|
||||
const time = new Date(minTime + (normalizedPos * timeRange));
|
||||
const timeStr = time.toLocaleTimeString('en-US', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
|
||||
// Draw tick mark
|
||||
ctx.strokeStyle = '#3e3e42';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, timelineY - 15);
|
||||
ctx.lineTo(x, timelineY + 15);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw label
|
||||
ctx.fillText(timeStr, x - 20, timelineY + 35);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw empty state
|
||||
function drawEmptyState() {
|
||||
ctx.fillStyle = '#1e1e1e';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = '#858585';
|
||||
ctx.font = '16px Monaco, monospace';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('No data loaded. Load a JSONL file or sample data.', canvas.width / 2, canvas.height / 2);
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
|
||||
// Update statistics
|
||||
function updateStats() {
|
||||
const total = messages.length;
|
||||
const userCount = messages.filter(m => m.role === 'user').length;
|
||||
const assistantCount = messages.filter(m => m.role === 'assistant').length;
|
||||
|
||||
document.getElementById('totalMessages').textContent = total;
|
||||
document.getElementById('userMessages').textContent = userCount;
|
||||
document.getElementById('assistantMessages').textContent = assistantCount;
|
||||
|
||||
if (total > 0) {
|
||||
const timestamps = messages.map(m => m.date.getTime());
|
||||
const duration = Math.max(...timestamps) - Math.min(...timestamps);
|
||||
const minutes = Math.floor(duration / 60000);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
|
||||
if (hours > 0) {
|
||||
document.getElementById('duration').textContent = `${hours}h ${minutes % 60}m`;
|
||||
} else {
|
||||
document.getElementById('duration').textContent = `${minutes}m`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle mouse move for tooltips (applying canvas coordinate conversion)
|
||||
canvas.addEventListener('mousemove', (e) => {
|
||||
if (isDragging) {
|
||||
const deltaX = e.clientX - lastMouseX;
|
||||
panOffset += deltaX;
|
||||
lastMouseX = e.clientX;
|
||||
drawTimeline();
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert mouse coordinates to canvas coordinates
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const mouseX = e.clientX - rect.left;
|
||||
const mouseY = e.clientY - rect.top;
|
||||
|
||||
// Find message under cursor
|
||||
let hoveredMsg = null;
|
||||
for (const msg of messages) {
|
||||
if (msg.x && msg.y) {
|
||||
const dx = mouseX - msg.x;
|
||||
const dy = mouseY - msg.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance <= 10) {
|
||||
hoveredMsg = msg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show/hide tooltip
|
||||
if (hoveredMsg) {
|
||||
tooltip.querySelector('.tooltip-role').textContent = hoveredMsg.role.toUpperCase();
|
||||
tooltip.querySelector('.tooltip-time').textContent = hoveredMsg.date.toLocaleString();
|
||||
tooltip.querySelector('.tooltip-preview').textContent = hoveredMsg.preview;
|
||||
|
||||
tooltip.style.left = e.clientX + 15 + 'px';
|
||||
tooltip.style.top = e.clientY + 15 + 'px';
|
||||
tooltip.classList.add('visible');
|
||||
} else {
|
||||
tooltip.classList.remove('visible');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle mouse leave
|
||||
canvas.addEventListener('mouseleave', () => {
|
||||
tooltip.classList.remove('visible');
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// Handle mouse down for panning
|
||||
canvas.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
lastMouseX = e.clientX;
|
||||
});
|
||||
|
||||
// Handle mouse up
|
||||
canvas.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// Handle zoom slider
|
||||
document.getElementById('zoomSlider').addEventListener('input', (e) => {
|
||||
zoomLevel = parseFloat(e.target.value);
|
||||
drawTimeline();
|
||||
});
|
||||
|
||||
// Reset view
|
||||
document.getElementById('resetViewBtn').addEventListener('click', () => {
|
||||
zoomLevel = 1;
|
||||
panOffset = 0;
|
||||
document.getElementById('zoomSlider').value = 1;
|
||||
drawTimeline();
|
||||
});
|
||||
|
||||
// Load sample data button
|
||||
document.getElementById('loadSampleBtn').addEventListener('click', loadSampleData);
|
||||
|
||||
// File input handler
|
||||
document.getElementById('fileInput').addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
try {
|
||||
const content = event.target.result;
|
||||
const lines = content.trim().split('\n');
|
||||
|
||||
messages = lines
|
||||
.map(line => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(msg => msg && msg.timestamp)
|
||||
.map(msg => ({
|
||||
timestamp: msg.timestamp,
|
||||
role: msg.message?.role || msg.type || 'system',
|
||||
content: msg.message?.content || ''
|
||||
}));
|
||||
|
||||
processMessages();
|
||||
drawTimeline();
|
||||
updateStats();
|
||||
|
||||
// Reset view
|
||||
zoomLevel = 1;
|
||||
panOffset = 0;
|
||||
document.getElementById('zoomSlider').value = 1;
|
||||
} catch (error) {
|
||||
alert('Error parsing file: ' + error.message);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
|
||||
// Handle window resize
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
|
||||
// Initialize
|
||||
resizeCanvas();
|
||||
drawTimeline();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,782 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dashboard Layout Tool - Claude Code DevTools</title>
|
||||
<style>
|
||||
/* CSS Reset & Base */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-primary: #0d1117;
|
||||
--bg-secondary: #161b22;
|
||||
--bg-tertiary: #21262d;
|
||||
--border-color: #30363d;
|
||||
--text-primary: #c9d1d9;
|
||||
--text-secondary: #8b949e;
|
||||
--accent-blue: #58a6ff;
|
||||
--accent-green: #3fb950;
|
||||
--accent-yellow: #d29922;
|
||||
--accent-purple: #bc8cff;
|
||||
--accent-red: #f85149;
|
||||
--shadow: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 1.5rem 2rem;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
box-shadow: 0 2px 8px var(--shadow);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent-blue);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
header .tagline {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Main Grid Dashboard Layout */
|
||||
/* Technique 1: grid-template-areas for named layout regions */
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"stats stats timeline timeline"
|
||||
"usage usage activity activity"
|
||||
"performance performance activity activity"
|
||||
"docs docs docs docs";
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: auto auto auto auto;
|
||||
gap: 1.5rem;
|
||||
padding: 2rem;
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Grid Area Assignments */
|
||||
.stats-panel {
|
||||
grid-area: stats;
|
||||
}
|
||||
|
||||
.timeline-panel {
|
||||
grid-area: timeline;
|
||||
}
|
||||
|
||||
.usage-panel {
|
||||
grid-area: usage;
|
||||
}
|
||||
|
||||
.activity-panel {
|
||||
grid-area: activity;
|
||||
}
|
||||
|
||||
.performance-panel {
|
||||
grid-area: performance;
|
||||
}
|
||||
|
||||
.docs {
|
||||
grid-area: docs;
|
||||
}
|
||||
|
||||
/* Panel Styling */
|
||||
.panel {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 1px 3px var(--shadow);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.panel:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px var(--shadow);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--accent-blue);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.panel-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* Stats Grid - Technique 2: auto-fit with minmax for responsive cards */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border-color);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-card.blue .stat-value { color: var(--accent-blue); }
|
||||
.stat-card.green .stat-value { color: var(--accent-green); }
|
||||
.stat-card.yellow .stat-value { color: var(--accent-yellow); }
|
||||
.stat-card.purple .stat-value { color: var(--accent-purple); }
|
||||
|
||||
/* Timeline */
|
||||
.timeline {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.timeline-time {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
min-width: 60px;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.timeline-desc {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.timeline-badge {
|
||||
display: inline-block;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline-badge.edit { background: var(--accent-blue); color: #000; }
|
||||
.timeline-badge.read { background: var(--accent-green); color: #000; }
|
||||
.timeline-badge.bash { background: var(--accent-yellow); color: #000; }
|
||||
.timeline-badge.search { background: var(--accent-purple); color: #000; }
|
||||
|
||||
/* Tool Usage Chart */
|
||||
.usage-bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.usage-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.usage-label {
|
||||
min-width: 80px;
|
||||
font-size: 0.875rem;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
}
|
||||
|
||||
.usage-bar-bg {
|
||||
flex: 1;
|
||||
height: 24px;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.usage-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--accent-blue), var(--accent-purple));
|
||||
transition: width 1s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Activity Feed */
|
||||
.activity-feed {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.activity-feed::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.activity-feed::-webkit-scrollbar-track {
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.activity-feed::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid var(--accent-blue);
|
||||
}
|
||||
|
||||
.activity-item.success { border-left-color: var(--accent-green); }
|
||||
.activity-item.warning { border-left-color: var(--accent-yellow); }
|
||||
.activity-item.error { border-left-color: var(--accent-red); }
|
||||
|
||||
.activity-icon {
|
||||
font-size: 1.25rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.activity-meta {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Performance Metrics Grid */
|
||||
.performance-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.metric-title {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--accent-green);
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
}
|
||||
|
||||
.metric-bar {
|
||||
height: 4px;
|
||||
background: var(--bg-primary);
|
||||
border-radius: 2px;
|
||||
margin-top: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.metric-bar-fill {
|
||||
height: 100%;
|
||||
background: var(--accent-green);
|
||||
transition: width 1s ease;
|
||||
}
|
||||
|
||||
/* Documentation Section */
|
||||
.docs {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.docs h2 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
font-size: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.doc-content h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.doc-content p {
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.doc-content ul, .doc-content ol {
|
||||
margin-bottom: 1rem;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.doc-content li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.doc-content strong {
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.doc-content code {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 3px;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 0.875rem;
|
||||
color: var(--accent-yellow);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
background: var(--bg-secondary);
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding: 1.5rem 2rem;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
footer p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--accent-blue);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Responsive Layout - Technique 3: Media queries with grid-template-areas */
|
||||
@media (max-width: 1200px) {
|
||||
main {
|
||||
grid-template-areas:
|
||||
"stats stats timeline timeline"
|
||||
"usage usage usage usage"
|
||||
"activity activity activity activity"
|
||||
"performance performance performance performance"
|
||||
"docs docs docs docs";
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
main {
|
||||
grid-template-areas:
|
||||
"stats"
|
||||
"timeline"
|
||||
"usage"
|
||||
"activity"
|
||||
"performance"
|
||||
"docs";
|
||||
grid-template-columns: 1fr;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.panel {
|
||||
animation: fadeIn 0.5s ease forwards;
|
||||
}
|
||||
|
||||
.stats-panel { animation-delay: 0.1s; }
|
||||
.timeline-panel { animation-delay: 0.2s; }
|
||||
.usage-panel { animation-delay: 0.3s; }
|
||||
.activity-panel { animation-delay: 0.4s; }
|
||||
.performance-panel { animation-delay: 0.5s; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>📊 Dashboard Layout Tool</h1>
|
||||
<p class="tagline">Responsive developer analytics dashboard powered by CSS Grid</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Stats Panel -->
|
||||
<section class="panel stats-panel">
|
||||
<h2><span class="panel-icon">📈</span>Session Overview</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card blue">
|
||||
<span class="stat-value" id="stat-sessions">24</span>
|
||||
<span class="stat-label">Sessions</span>
|
||||
</div>
|
||||
<div class="stat-card green">
|
||||
<span class="stat-value" id="stat-messages">1,847</span>
|
||||
<span class="stat-label">Messages</span>
|
||||
</div>
|
||||
<div class="stat-card yellow">
|
||||
<span class="stat-value" id="stat-tools">432</span>
|
||||
<span class="stat-label">Tool Calls</span>
|
||||
</div>
|
||||
<div class="stat-card purple">
|
||||
<span class="stat-value" id="stat-hours">18.5h</span>
|
||||
<span class="stat-label">Active Time</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Timeline Panel -->
|
||||
<section class="panel timeline-panel">
|
||||
<h2><span class="panel-icon">🕐</span>Recent Activity</h2>
|
||||
<div class="timeline">
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">14:32</div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-title">
|
||||
Created dashboard component
|
||||
<span class="timeline-badge edit">Edit</span>
|
||||
</div>
|
||||
<div class="timeline-desc">Modified 3 files in /src/components</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">14:28</div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-title">
|
||||
Read configuration files
|
||||
<span class="timeline-badge read">Read</span>
|
||||
</div>
|
||||
<div class="timeline-desc">Analyzed package.json and tsconfig.json</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">14:15</div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-title">
|
||||
Ran test suite
|
||||
<span class="timeline-badge bash">Bash</span>
|
||||
</div>
|
||||
<div class="timeline-desc">npm test -- 23 tests passed</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">14:02</div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-title">
|
||||
Searched codebase
|
||||
<span class="timeline-badge search">Search</span>
|
||||
</div>
|
||||
<div class="timeline-desc">Found 12 matches for "useEffect"</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tool Usage Panel -->
|
||||
<section class="panel usage-panel">
|
||||
<h2><span class="panel-icon">🔧</span>Tool Usage Distribution</h2>
|
||||
<div class="usage-bars" id="usage-chart"></div>
|
||||
</section>
|
||||
|
||||
<!-- Activity Feed Panel -->
|
||||
<section class="panel activity-panel">
|
||||
<h2><span class="panel-icon">📋</span>Activity Feed</h2>
|
||||
<div class="activity-feed" id="activity-feed"></div>
|
||||
</section>
|
||||
|
||||
<!-- Performance Metrics Panel -->
|
||||
<section class="panel performance-panel">
|
||||
<h2><span class="panel-icon">⚡</span>Performance Metrics</h2>
|
||||
<div class="performance-grid">
|
||||
<div class="metric-card">
|
||||
<div class="metric-title">Avg Response</div>
|
||||
<div class="metric-value">2.3s</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-bar-fill" style="width: 77%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-title">Success Rate</div>
|
||||
<div class="metric-value">94.2%</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-bar-fill" style="width: 94.2%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-title">Token Usage</div>
|
||||
<div class="metric-value">847K</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-bar-fill" style="width: 65%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-title">Files Modified</div>
|
||||
<div class="metric-value">142</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-bar-fill" style="width: 82%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Documentation -->
|
||||
<section class="docs">
|
||||
<h2>About This Tool</h2>
|
||||
<div class="doc-content">
|
||||
<h3>Purpose</h3>
|
||||
<p>A demonstration of CSS Grid Layout techniques applied to create a flexible, responsive developer dashboard for Claude Code analytics. This tool showcases how to build modular dashboard layouts that adapt seamlessly to different screen sizes while maintaining visual hierarchy and usability.</p>
|
||||
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<li><strong>Named Grid Areas</strong> - Semantic layout regions using <code>grid-template-areas</code></li>
|
||||
<li><strong>Responsive Auto-Fit</strong> - Stats cards that automatically adjust columns with <code>auto-fit</code> and <code>minmax()</code></li>
|
||||
<li><strong>Consistent Spacing</strong> - Clean gaps between panels using CSS Grid <code>gap</code> property</li>
|
||||
<li><strong>Multi-Panel Layout</strong> - 6 distinct dashboard sections arranged in an intelligent grid</li>
|
||||
<li><strong>Adaptive Breakpoints</strong> - Layout reconfigures at 1200px and 768px for optimal viewing</li>
|
||||
<li><strong>Dark Developer Theme</strong> - Eye-friendly color scheme for extended coding sessions</li>
|
||||
<li><strong>Interactive Metrics</strong> - Live data visualization with tool usage, timeline, and performance stats</li>
|
||||
</ul>
|
||||
|
||||
<h3>Web Research Integration</h3>
|
||||
<p><strong>Source:</strong> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout" target="_blank">MDN - CSS Grid Layout</a></p>
|
||||
<p><strong>Techniques Applied:</strong></p>
|
||||
<ul>
|
||||
<li><strong>grid-template-areas</strong> - Created named layout regions ("stats", "timeline", "usage", "activity", "performance", "docs") for semantic, maintainable dashboard structure. This allows easy reconfiguration at different breakpoints by simply redefining the areas.</li>
|
||||
<li><strong>auto-fit with minmax()</strong> - Implemented responsive card grids (stats-grid, performance-grid) that automatically adjust column count based on available space using <code>repeat(auto-fit, minmax(120px, 1fr))</code>. Cards flow and resize without media queries.</li>
|
||||
<li><strong>gap property</strong> - Applied consistent spacing between all grid items using the <code>gap: 1.5rem</code> property, eliminating the need for margin calculations and ensuring uniform visual rhythm across the entire dashboard.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Grid Layout Structure</h3>
|
||||
<p>The main dashboard uses a 4-column grid with named areas:</p>
|
||||
<ul>
|
||||
<li><strong>Desktop (>1200px):</strong> 4x4 grid with stats/timeline sharing top row, usage/activity in middle rows</li>
|
||||
<li><strong>Tablet (768-1200px):</strong> 4-column grid with full-width sections stacked vertically</li>
|
||||
<li><strong>Mobile (<768px):</strong> Single column stack for optimal mobile viewing</li>
|
||||
</ul>
|
||||
|
||||
<h3>Usage</h3>
|
||||
<ol>
|
||||
<li>Open the tool to see a complete analytics dashboard layout</li>
|
||||
<li>Resize the browser window to observe responsive grid reconfiguration</li>
|
||||
<li>Notice how named grid areas reorganize at different breakpoints</li>
|
||||
<li>Observe auto-fit behavior in stats cards and performance metrics</li>
|
||||
<li>Use this pattern for building your own modular dashboards</li>
|
||||
<li>Inspect the CSS to understand grid-template-areas syntax</li>
|
||||
</ol>
|
||||
|
||||
<h3>Developer Notes</h3>
|
||||
<p>This dashboard demonstrates key advantages of CSS Grid for developer tools:</p>
|
||||
<ul>
|
||||
<li><strong>Maintainability:</strong> Named grid areas make layout intent clear and changes simple</li>
|
||||
<li><strong>Flexibility:</strong> Easy to add, remove, or reorder dashboard panels</li>
|
||||
<li><strong>Responsiveness:</strong> Auto-fit and minmax() eliminate brittle breakpoint logic</li>
|
||||
<li><strong>Performance:</strong> Pure CSS solution with no JavaScript layout calculations</li>
|
||||
<li><strong>Accessibility:</strong> Grid maintains semantic HTML structure regardless of visual layout</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Claude Code DevTools | Generated via web-enhanced infinite loop</p>
|
||||
<p>Web Source: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout" target="_blank">MDN - CSS Grid Layout</a></p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Generate tool usage bars
|
||||
const toolUsageData = [
|
||||
{ tool: 'Edit', count: 156, max: 200 },
|
||||
{ tool: 'Read', count: 142, max: 200 },
|
||||
{ tool: 'Bash', count: 89, max: 200 },
|
||||
{ tool: 'Grep', count: 67, max: 200 },
|
||||
{ tool: 'Write', count: 45, max: 200 },
|
||||
{ tool: 'Glob', count: 33, max: 200 }
|
||||
];
|
||||
|
||||
const usageChart = document.getElementById('usage-chart');
|
||||
toolUsageData.forEach(item => {
|
||||
const percentage = (item.count / item.max * 100).toFixed(1);
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'usage-bar';
|
||||
bar.innerHTML = `
|
||||
<div class="usage-label">${item.tool}</div>
|
||||
<div class="usage-bar-bg">
|
||||
<div class="usage-bar-fill" style="width: ${percentage}%">
|
||||
${item.count}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
usageChart.appendChild(bar);
|
||||
});
|
||||
|
||||
// Generate activity feed
|
||||
const activities = [
|
||||
{ type: 'success', icon: '✅', title: 'Successfully deployed to production', meta: '2 minutes ago', class: 'success' },
|
||||
{ type: 'info', icon: '📝', title: 'Updated README documentation', meta: '15 minutes ago', class: '' },
|
||||
{ type: 'warning', icon: '⚠️', title: 'Deprecated API usage detected', meta: '32 minutes ago', class: 'warning' },
|
||||
{ type: 'success', icon: '🧪', title: 'All tests passed (23/23)', meta: '1 hour ago', class: 'success' },
|
||||
{ type: 'info', icon: '🔍', title: 'Code review requested', meta: '2 hours ago', class: '' },
|
||||
{ type: 'error', icon: '❌', title: 'Build failed on branch feature-123', meta: '3 hours ago', class: 'error' },
|
||||
{ type: 'success', icon: '🚀', title: 'Feature flag enabled: dark-mode', meta: '4 hours ago', class: 'success' },
|
||||
{ type: 'info', icon: '💾', title: 'Database backup completed', meta: '5 hours ago', class: '' }
|
||||
];
|
||||
|
||||
const activityFeed = document.getElementById('activity-feed');
|
||||
activities.forEach(activity => {
|
||||
const item = document.createElement('div');
|
||||
item.className = `activity-item ${activity.class}`;
|
||||
item.innerHTML = `
|
||||
<div class="activity-icon">${activity.icon}</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">${activity.title}</div>
|
||||
<div class="activity-meta">${activity.meta}</div>
|
||||
</div>
|
||||
`;
|
||||
activityFeed.appendChild(item);
|
||||
});
|
||||
|
||||
// Animate stats on load
|
||||
function animateValue(id, start, end, duration) {
|
||||
const element = document.getElementById(id);
|
||||
const startTime = performance.now();
|
||||
|
||||
function update(currentTime) {
|
||||
const elapsed = currentTime - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
|
||||
const current = Math.floor(start + (end - start) * progress);
|
||||
|
||||
if (id === 'stat-hours') {
|
||||
element.textContent = (start + (end - start) * progress).toFixed(1) + 'h';
|
||||
} else if (id === 'stat-messages') {
|
||||
element.textContent = current.toLocaleString();
|
||||
} else {
|
||||
element.textContent = current;
|
||||
}
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
|
||||
// Trigger animations after page load
|
||||
window.addEventListener('load', () => {
|
||||
animateValue('stat-sessions', 0, 24, 1000);
|
||||
animateValue('stat-messages', 0, 1847, 1200);
|
||||
animateValue('stat-tools', 0, 432, 1100);
|
||||
animateValue('stat-hours', 0, 18.5, 1300);
|
||||
});
|
||||
|
||||
// Add keyboard shortcut hint
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.ctrlKey && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
alert('Keyboard shortcuts:\n\nCtrl+K: This help menu\nCtrl+R: Refresh dashboard\nCtrl+E: Export data');
|
||||
}
|
||||
if (e.ctrlKey && e.key === 'r') {
|
||||
e.preventDefault();
|
||||
location.reload();
|
||||
}
|
||||
if (e.ctrlKey && e.key === 'e') {
|
||||
e.preventDefault();
|
||||
alert('Export functionality would trigger here (not implemented in this demo)');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,686 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Infinite Agents - Demo Dashboard</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary: #6366f1;
|
||||
--primary-dark: #4f46e5;
|
||||
--secondary: #8b5cf6;
|
||||
--success: #10b981;
|
||||
--warning: #f59e0b;
|
||||
--danger: #ef4444;
|
||||
--dark: #1e293b;
|
||||
--dark-light: #334155;
|
||||
--light: #f8fafc;
|
||||
--border: #e2e8f0;
|
||||
--text: #0f172a;
|
||||
--text-muted: #64748b;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 30px 0;
|
||||
margin-bottom: 40px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Stats Bar */
|
||||
.stats-bar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Search & Filter */
|
||||
.controls {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#searchInput {
|
||||
flex: 1;
|
||||
padding: 15px 20px;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 10px;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
#searchInput:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 10px 20px;
|
||||
border: 2px solid var(--border);
|
||||
background: white;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* Category Sections */
|
||||
.category-section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
}
|
||||
|
||||
.category-title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.category-title h2 {
|
||||
font-size: 1.8rem;
|
||||
color: var(--text);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.category-title p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.category-count {
|
||||
background: var(--light);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Demo Grid */
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.demo-card:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.demo-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||||
}
|
||||
|
||||
.demo-number {
|
||||
display: inline-block;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
padding: 4px 12px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.demo-description {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.demo-footer {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.demo-type {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.demo-link {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
transition: gap 0.3s ease;
|
||||
}
|
||||
|
||||
.demo-link:hover {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 30px;
|
||||
margin-top: 50px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
footer p {
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.github-link {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.stats-bar {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 50px;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* Hidden */
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="header-content">
|
||||
<h1>🤖 Infinite Agents</h1>
|
||||
<p class="subtitle">Progressive AI-Generated Demo Gallery</p>
|
||||
<p style="color: var(--text-muted); font-size: 0.9rem;">
|
||||
Exploring web-enhanced infinite agentic loops for progressive learning
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Statistics -->
|
||||
<div class="stats-bar">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="totalDemos">73</div>
|
||||
<div class="stat-label">Total Demos</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">4</div>
|
||||
<div class="stat-label">Categories</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="threejsCount">5</div>
|
||||
<div class="stat-label">Three.js 3D</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="uiCount">65</div>
|
||||
<div class="stat-label">UI Components</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search & Filter Controls -->
|
||||
<div class="controls">
|
||||
<div class="search-box">
|
||||
<input type="text" id="searchInput" placeholder="🔍 Search demos by name, type, or technique...">
|
||||
</div>
|
||||
<div class="filter-buttons">
|
||||
<button class="filter-btn active" data-filter="all">All Demos</button>
|
||||
<button class="filter-btn" data-filter="threejs">Three.js 3D</button>
|
||||
<button class="filter-btn" data-filter="sdg">SDG Networks</button>
|
||||
<button class="filter-btn" data-filter="ui-single">UI Hybrid (Single File)</button>
|
||||
<button class="filter-btn" data-filter="ui-modular">UI Hybrid (Modular)</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Three.js Category -->
|
||||
<div class="category-section" data-category="threejs">
|
||||
<div class="category-header">
|
||||
<div class="category-icon">🎨</div>
|
||||
<div class="category-title">
|
||||
<h2>Three.js 3D Visualizations</h2>
|
||||
<p>Progressive WebGL/WebGPU visualizations with foundation → expert learning path</p>
|
||||
</div>
|
||||
<div class="category-count">5 demos</div>
|
||||
</div>
|
||||
<div class="demo-grid" id="threejs-grid"></div>
|
||||
</div>
|
||||
|
||||
<!-- SDG Networks Category -->
|
||||
<div class="category-section" data-category="sdg">
|
||||
<div class="category-header">
|
||||
<div class="category-icon">🌍</div>
|
||||
<div class="category-title">
|
||||
<h2>SDG Network Visualizations</h2>
|
||||
<p>Force-directed network graphs with API discovery and progressive D3.js techniques</p>
|
||||
</div>
|
||||
<div class="category-count">3 demos</div>
|
||||
</div>
|
||||
<div class="demo-grid" id="sdg-grid"></div>
|
||||
</div>
|
||||
|
||||
<!-- UI Hybrid Single File Category -->
|
||||
<div class="category-section" data-category="ui-single">
|
||||
<div class="category-header">
|
||||
<div class="category-icon">✨</div>
|
||||
<div class="category-title">
|
||||
<h2>Themed Hybrid UI Components</h2>
|
||||
<p>Self-contained HTML components with unique themes and multi-functional design</p>
|
||||
</div>
|
||||
<div class="category-count">60 demos</div>
|
||||
</div>
|
||||
<div class="demo-grid" id="ui-single-grid"></div>
|
||||
</div>
|
||||
|
||||
<!-- UI Hybrid Modular Category -->
|
||||
<div class="category-section" data-category="ui-modular">
|
||||
<div class="category-header">
|
||||
<div class="category-icon">🧩</div>
|
||||
<div class="category-title">
|
||||
<h2>Modular UI Components</h2>
|
||||
<p>Professional 3-file architecture (HTML/CSS/JS) with separation of concerns</p>
|
||||
</div>
|
||||
<div class="category-count">5 demos</div>
|
||||
</div>
|
||||
<div class="demo-grid" id="ui-modular-grid"></div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p><strong>Infinite Agents</strong> - Web-Enhanced Infinite Agentic Loop Pattern</p>
|
||||
<p>Generated with parallel AI agents using progressive learning from web sources</p>
|
||||
<p style="margin-top: 15px;">
|
||||
<a href="CLAUDE.md" class="github-link">📖 Documentation</a> •
|
||||
<a href="ai_docs/threejs_infinite_loop_manual.md" class="github-link">📚 Three.js Manual</a> •
|
||||
<a href="README.md" class="github-link">ℹ️ About</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Demo Data
|
||||
const demos = {
|
||||
threejs: [
|
||||
{
|
||||
number: 1,
|
||||
title: 'Rotating Geometries',
|
||||
description: 'Foundation: Scene setup, camera, renderer, and basic geometric objects with rotation animation',
|
||||
path: 'threejs_viz/threejs_viz_1.html',
|
||||
type: 'Foundation',
|
||||
techniques: ['Scene Setup', 'PerspectiveCamera', 'WebGLRenderer', 'Basic Animation']
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: 'Animated Lighting',
|
||||
description: 'Foundation: Dynamic lighting with orbiting colored point lights and material interaction',
|
||||
path: 'threejs_viz/threejs_viz_2.html',
|
||||
type: 'Foundation',
|
||||
techniques: ['PointLight', 'AmbientLight', 'MeshStandardMaterial', 'Light Animation']
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: 'Particle Universe',
|
||||
description: 'Foundation: 10,000 GPU-accelerated particles with custom shaders and BufferGeometry',
|
||||
path: 'threejs_viz/threejs_viz_3.html',
|
||||
type: 'Foundation',
|
||||
techniques: ['BufferGeometry', 'Custom Shaders', 'Points', 'GPU Particles']
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
title: 'Material Gallery',
|
||||
description: 'Foundation: Showcase of 6 different Three.js material types and their properties',
|
||||
path: 'threejs_viz/threejs_viz_4.html',
|
||||
type: 'Foundation',
|
||||
techniques: ['Materials', 'PBR', 'Lighting', 'MeshStandardMaterial']
|
||||
},
|
||||
{
|
||||
number: 5,
|
||||
title: 'Geometry Morphing',
|
||||
description: 'Foundation: Dynamic geometry transformation with easing and choreographed animation',
|
||||
path: 'threejs_viz/threejs_viz_5.html',
|
||||
type: 'Foundation',
|
||||
techniques: ['Transformation', 'Easing', 'Animation', 'Morphing']
|
||||
}
|
||||
],
|
||||
sdg: [
|
||||
{
|
||||
number: 1,
|
||||
title: 'SDG Network Viz 1',
|
||||
description: 'Force-directed network graph with API-sourced SDG data',
|
||||
path: 'sdg_viz/sdg_viz_1.html',
|
||||
type: 'Network',
|
||||
techniques: ['D3.js', 'Force Simulation', 'API Integration']
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: 'SDG Network Viz 2',
|
||||
description: 'Enhanced network visualization with interactive node exploration',
|
||||
path: 'sdg_viz/sdg_viz_2.html',
|
||||
type: 'Network',
|
||||
techniques: ['D3.js', 'Interactivity', 'Data Binding']
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: 'SDG Network Viz 3',
|
||||
description: 'Advanced network with progressive enhancement techniques',
|
||||
path: 'sdg_viz/sdg_viz_3.html',
|
||||
type: 'Network',
|
||||
techniques: ['D3.js', 'Progressive Enhancement', 'Complex Networks']
|
||||
}
|
||||
],
|
||||
uiSingle: [],
|
||||
uiModular: [
|
||||
{
|
||||
number: 1,
|
||||
title: 'UI Hybrid 1 (Modular)',
|
||||
description: 'Professional 3-file architecture with separated HTML, CSS, and JavaScript',
|
||||
path: 'src_group/ui_hybrid_1/index.html',
|
||||
type: 'Modular',
|
||||
techniques: ['Separation of Concerns', 'Modular Architecture']
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: 'UI Hybrid 2 (Modular)',
|
||||
description: 'Themed component with professional file organization',
|
||||
path: 'src_group/ui_hybrid_2/index.html',
|
||||
type: 'Modular',
|
||||
techniques: ['File Organization', 'Best Practices']
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: 'UI Hybrid 3 (Modular)',
|
||||
description: 'Multi-functional component with clean architecture',
|
||||
path: 'src_group/ui_hybrid_3/index.html',
|
||||
type: 'Modular',
|
||||
techniques: ['Clean Code', 'Maintainability']
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
title: 'UI Hybrid 4 (Modular)',
|
||||
description: 'Advanced themed component with production-ready structure',
|
||||
path: 'src_group/ui_hybrid_4/index.html',
|
||||
type: 'Modular',
|
||||
techniques: ['Production Ready', 'Team Collaboration']
|
||||
},
|
||||
{
|
||||
number: 5,
|
||||
title: 'UI Hybrid 5 (Modular)',
|
||||
description: 'Sophisticated UI with separated concerns and scalable design',
|
||||
path: 'src_group/ui_hybrid_5/index.html',
|
||||
type: 'Modular',
|
||||
techniques: ['Scalability', 'Professional Architecture']
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Generate UI Single File demos (src/ and src_infinite/)
|
||||
for (let i = 1; i <= 35; i++) {
|
||||
demos.uiSingle.push({
|
||||
number: i,
|
||||
title: `UI Hybrid ${i}`,
|
||||
description: 'Themed hybrid UI component combining multiple interface elements',
|
||||
path: `src/ui_hybrid_${i}.html`,
|
||||
type: 'Single File',
|
||||
techniques: ['Themed Design', 'Hybrid Components']
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 25; i++) {
|
||||
demos.uiSingle.push({
|
||||
number: i + 35,
|
||||
title: `UI Hybrid ${i} (Infinite)`,
|
||||
description: 'Infinite mode generated themed component',
|
||||
path: `src_infinite/ui_hybrid_${i}.html`,
|
||||
type: 'Single File (Infinite)',
|
||||
techniques: ['Infinite Generation', 'Progressive Complexity']
|
||||
});
|
||||
}
|
||||
|
||||
// Render demos
|
||||
function renderDemoCard(demo, category) {
|
||||
return `
|
||||
<div class="demo-card" data-category="${category}" data-title="${demo.title.toLowerCase()}" data-type="${demo.type.toLowerCase()}">
|
||||
<div class="demo-header">
|
||||
<span class="demo-number">#${demo.number}</span>
|
||||
<h3 class="demo-title">${demo.title}</h3>
|
||||
<p class="demo-description">${demo.description}</p>
|
||||
</div>
|
||||
<div class="demo-footer">
|
||||
<span class="demo-type">${demo.type}</span>
|
||||
<a href="${demo.path}" class="demo-link" target="_blank">
|
||||
View Demo →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Initialize grids
|
||||
document.getElementById('threejs-grid').innerHTML = demos.threejs.map(d => renderDemoCard(d, 'threejs')).join('');
|
||||
document.getElementById('sdg-grid').innerHTML = demos.sdg.map(d => renderDemoCard(d, 'sdg')).join('');
|
||||
document.getElementById('ui-single-grid').innerHTML = demos.uiSingle.map(d => renderDemoCard(d, 'ui-single')).join('');
|
||||
document.getElementById('ui-modular-grid').innerHTML = demos.uiModular.map(d => renderDemoCard(d, 'ui-modular')).join('');
|
||||
|
||||
// Search functionality
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const searchTerm = e.target.value.toLowerCase();
|
||||
document.querySelectorAll('.demo-card').forEach(card => {
|
||||
const title = card.dataset.title;
|
||||
const type = card.dataset.type;
|
||||
const matches = title.includes(searchTerm) || type.includes(searchTerm);
|
||||
card.classList.toggle('hidden', !matches);
|
||||
});
|
||||
});
|
||||
|
||||
// Filter functionality
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
// Update active state
|
||||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
|
||||
const filter = btn.dataset.filter;
|
||||
|
||||
// Show/hide category sections
|
||||
document.querySelectorAll('.category-section').forEach(section => {
|
||||
if (filter === 'all') {
|
||||
section.classList.remove('hidden');
|
||||
} else {
|
||||
section.classList.toggle('hidden', section.dataset.category !== filter);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset search
|
||||
searchInput.value = '';
|
||||
document.querySelectorAll('.demo-card').forEach(card => {
|
||||
card.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add click handler to demo cards
|
||||
document.querySelectorAll('.demo-card').forEach(card => {
|
||||
card.addEventListener('click', (e) => {
|
||||
if (!e.target.classList.contains('demo-link')) {
|
||||
const link = card.querySelector('.demo-link');
|
||||
window.open(link.href, '_blank');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Update stats
|
||||
document.getElementById('totalDemos').textContent = demos.threejs.length + demos.sdg.length + demos.uiSingle.length + demos.uiModular.length;
|
||||
document.getElementById('threejsCount').textContent = demos.threejs.length;
|
||||
document.getElementById('uiCount').textContent = demos.uiSingle.length + demos.uiModular.length;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,467 @@
|
|||
# Claude Code Developer Tools - Progressive Specification
|
||||
|
||||
## Core Challenge
|
||||
|
||||
Create **self-contained web-based developer tools** that enable observability, search, coordination, and shared memory across Claude Code instances. Each tool should progressively build upon web-learned techniques to create increasingly sophisticated systems for understanding and orchestrating AI coding sessions.
|
||||
|
||||
## Output Requirements
|
||||
|
||||
**File Naming**: `claude_devtool_[iteration_number].html`
|
||||
|
||||
**Content Structure**: Complete self-contained HTML tool with inline CSS and JavaScript
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>[Tool Name] - Claude Code DevTools</title>
|
||||
<style>
|
||||
/* Modern developer-focused UI */
|
||||
/* Dark theme by default with light mode option */
|
||||
/* Code syntax highlighting */
|
||||
/* Responsive layouts for data visualization */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>[Tool Name]</h1>
|
||||
<p class="tagline">[Tool Purpose]</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Tool Interface -->
|
||||
<section class="tool-interface">
|
||||
<!-- Interactive controls -->
|
||||
<!-- Data input/connection areas -->
|
||||
<!-- Real-time visualization -->
|
||||
</section>
|
||||
|
||||
<!-- Results/Output Area -->
|
||||
<section class="results">
|
||||
<!-- Search results, analytics, visualizations -->
|
||||
<!-- Export capabilities -->
|
||||
</section>
|
||||
|
||||
<!-- Documentation -->
|
||||
<section class="docs">
|
||||
<h2>About This Tool</h2>
|
||||
<div class="doc-content">
|
||||
<h3>Purpose</h3>
|
||||
<p>[What problem this solves]</p>
|
||||
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<li>[Feature 1]</li>
|
||||
<li>[Feature 2]</li>
|
||||
</ul>
|
||||
|
||||
<h3>Web Research Integration</h3>
|
||||
<p><strong>Source:</strong> [URL]</p>
|
||||
<p><strong>Techniques Applied:</strong></p>
|
||||
<ul>
|
||||
<li>[Technique 1 from web source]</li>
|
||||
<li>[Technique 2 from web source]</li>
|
||||
</ul>
|
||||
|
||||
<h3>Usage</h3>
|
||||
<ol>
|
||||
<li>[Step 1]</li>
|
||||
<li>[Step 2]</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Claude Code DevTools | Generated via web-enhanced infinite loop</p>
|
||||
<p>Web Source: <a href="[URL]">[URL]</a></p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// All JavaScript inline - no external dependencies
|
||||
// Core functionality implementation
|
||||
// Web API integration (localStorage, IndexedDB, etc.)
|
||||
// Real-time data processing
|
||||
// Visualization rendering
|
||||
// Export capabilities
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Tool Categories & Progressive Evolution
|
||||
|
||||
### **Foundation Tools (Iterations 1-5): Data Access & Visualization**
|
||||
|
||||
**Focus**: Basic transcript reading, parsing, and simple visualizations
|
||||
|
||||
**Example Tools**:
|
||||
- **Transcript Viewer**: Load and display Claude Code JSONL transcripts with syntax highlighting
|
||||
- **Session Timeline**: Visualize conversation flow over time with message markers
|
||||
- **Tool Usage Chart**: Bar chart showing which tools were used and how often
|
||||
- **Search Interface**: Simple keyword search across transcript content
|
||||
- **Export Utility**: Convert JSONL to JSON, CSV, or Markdown formats
|
||||
|
||||
**Web Learning Focus**:
|
||||
- File API and FileReader for local file handling
|
||||
- LocalStorage and IndexedDB for data persistence
|
||||
- Canvas or SVG for basic charts
|
||||
- CSS Grid and Flexbox for layouts
|
||||
- JSON parsing and data transformation
|
||||
|
||||
### **Intermediate Tools (Iterations 6-12): Advanced Search & Analytics**
|
||||
|
||||
**Focus**: Sophisticated search, filtering, pattern detection, and analytics
|
||||
|
||||
**Example Tools**:
|
||||
- **Multi-Facet Search**: Fuzzy search, regex patterns, field-specific filtering
|
||||
- **Conversation Graph**: Node-link diagram showing message relationships (parent-child)
|
||||
- **Productivity Dashboard**: Analytics on coding velocity, tool efficiency, session patterns
|
||||
- **Error Pattern Detector**: Identify and categorize common errors across sessions
|
||||
- **Code Extraction Tool**: Pull out all code blocks from conversations with syntax highlighting
|
||||
- **Session Comparator**: Side-by-side comparison of different coding sessions
|
||||
|
||||
**Web Learning Focus**:
|
||||
- Advanced regex and fuzzy matching algorithms
|
||||
- D3.js or similar for interactive visualizations
|
||||
- WebWorkers for performance with large datasets
|
||||
- Advanced filtering and sorting algorithms
|
||||
- Statistical analysis and pattern recognition
|
||||
|
||||
### **Advanced Tools (Iterations 13-20): Coordination & Communication**
|
||||
|
||||
**Focus**: Multi-instance coordination, shared memory, real-time collaboration
|
||||
|
||||
**Example Tools**:
|
||||
- **Session Orchestrator**: Coordinate multiple Claude Code instances on different tasks
|
||||
- **Shared Context Manager**: Maintain shared knowledge base across instances
|
||||
- **Cross-Instance Search**: Search transcripts from multiple machines/projects
|
||||
- **Collaboration Board**: Real-time task assignment and progress tracking
|
||||
- **Knowledge Graph Builder**: Build interconnected knowledge from all conversations
|
||||
- **MCP Server Monitor**: Track and visualize MCP server activity across instances
|
||||
- **Agent Communication Hub**: Message passing between different Claude agents
|
||||
|
||||
**Web Learning Focus**:
|
||||
- WebSocket and Server-Sent Events for real-time communication
|
||||
- SharedArrayBuffer for advanced memory sharing
|
||||
- Service Workers for background sync
|
||||
- IndexedDB for large-scale data storage
|
||||
- Broadcast Channel API for tab communication
|
||||
- WebRTC for peer-to-peer coordination
|
||||
|
||||
### **Expert Tools (Iterations 21+): AI-Enhanced & Predictive Systems**
|
||||
|
||||
**Focus**: Machine learning integration, predictive analytics, automated workflows
|
||||
|
||||
**Example Tools**:
|
||||
- **Smart Context Recommender**: Suggest relevant past conversations for current task
|
||||
- **Predictive Tool Selector**: Predict which tools will be needed next
|
||||
- **Workflow Automation Builder**: Record and replay common coding patterns
|
||||
- **Anomaly Detector**: Identify unusual patterns or potential issues
|
||||
- **Session Optimizer**: Suggest improvements based on past successful patterns
|
||||
- **Auto-Tagger**: Automatically categorize and tag conversations
|
||||
- **Semantic Search**: Vector-based similarity search across all transcripts
|
||||
|
||||
**Web Learning Focus**:
|
||||
- TensorFlow.js for in-browser ML
|
||||
- Vector embeddings and similarity search
|
||||
- Natural language processing libraries
|
||||
- Recommendation algorithms
|
||||
- Clustering and classification techniques
|
||||
- Automated pattern recognition
|
||||
|
||||
## Design Principles
|
||||
|
||||
### **Developer-First Design**
|
||||
- **Dark Theme Primary**: Easy on eyes during long coding sessions
|
||||
- **Monospace Fonts**: Code-friendly typography
|
||||
- **Syntax Highlighting**: Proper code colorization in all contexts
|
||||
- **Keyboard Shortcuts**: Power-user keyboard navigation
|
||||
- **Copy-Friendly**: Easy to copy data, code, commands
|
||||
- **Performance Focused**: Fast load, responsive interactions
|
||||
|
||||
### **Self-Contained Architecture**
|
||||
- **No External Dependencies**: All code inline (CSS, JS, data processing)
|
||||
- **Offline Capable**: Works without internet after initial load
|
||||
- **Browser Storage**: Leverage localStorage, IndexedDB for persistence
|
||||
- **File API Integration**: Direct file access where browser supports it
|
||||
- **Progressive Enhancement**: Basic functionality without advanced features
|
||||
|
||||
### **Data Integration Strategies**
|
||||
|
||||
**Claude Code Transcript Format**:
|
||||
```json
|
||||
{
|
||||
"uuid": "message-uuid",
|
||||
"sessionId": "session-id",
|
||||
"timestamp": "2025-10-09T18:00:00Z",
|
||||
"message": {
|
||||
"role": "user|assistant",
|
||||
"content": "message content or array of content blocks"
|
||||
},
|
||||
"type": "user|assistant|meta|tool",
|
||||
"cwd": "/working/directory",
|
||||
"version": "1.0.0",
|
||||
"gitBranch": "main"
|
||||
}
|
||||
```
|
||||
|
||||
**Tool Should Handle**:
|
||||
- JSONL line-by-line parsing
|
||||
- Message threading via parent-child relationships
|
||||
- Tool call extraction from content blocks
|
||||
- Usage statistics calculation
|
||||
- Multi-session aggregation
|
||||
- Export to multiple formats
|
||||
|
||||
### **Observability Features**
|
||||
|
||||
**What to Track**:
|
||||
- Session duration and message counts
|
||||
- Tool usage frequency and success rates
|
||||
- Model used and token consumption
|
||||
- Error patterns and resolution times
|
||||
- Working directory and git branch context
|
||||
- File read/write operations
|
||||
- Command executions
|
||||
- Search query patterns
|
||||
|
||||
**Visualization Types**:
|
||||
- Timeline views of session activity
|
||||
- Network graphs of message relationships
|
||||
- Heatmaps of activity by time/day
|
||||
- Bar/pie charts for tool usage
|
||||
- Scatter plots for correlation analysis
|
||||
- Tree maps for hierarchical data
|
||||
|
||||
### **Search Capabilities**
|
||||
|
||||
**Search Modes**:
|
||||
- Full-text search across all content
|
||||
- Regex pattern matching
|
||||
- Fuzzy matching for typo tolerance
|
||||
- Field-specific search (content, session ID, tools, etc.)
|
||||
- Date range filtering
|
||||
- Role-based filtering (user vs assistant)
|
||||
- Tool-based filtering
|
||||
- Model-based filtering
|
||||
|
||||
**Result Features**:
|
||||
- Contextual snippets with highlighting
|
||||
- Score-based ranking
|
||||
- Grouped by session
|
||||
- Exportable results
|
||||
- Result caching for performance
|
||||
|
||||
### **Coordination Mechanisms**
|
||||
|
||||
**Cross-Instance Communication**:
|
||||
- Shared localStorage namespace conventions
|
||||
- IndexedDB for larger shared datasets
|
||||
- File-based message passing (watch directories)
|
||||
- WebSocket server for real-time sync
|
||||
- Broadcast Channel for same-origin tabs
|
||||
|
||||
**Shared Memory Patterns**:
|
||||
- Central knowledge base structure
|
||||
- Version control for concurrent edits
|
||||
- Conflict resolution strategies
|
||||
- Cache invalidation protocols
|
||||
- State synchronization methods
|
||||
|
||||
## Progressive Web Learning Integration
|
||||
|
||||
### **Foundation Level (Iterations 1-5)**
|
||||
|
||||
**Web Resources to Learn From**:
|
||||
- MDN Web Docs on File API and FileReader
|
||||
- LocalStorage and IndexedDB tutorials
|
||||
- Canvas and SVG chart examples
|
||||
- JSON parsing best practices
|
||||
- Responsive CSS Grid/Flexbox layouts
|
||||
|
||||
**Techniques to Apply**:
|
||||
- File reading and parsing patterns
|
||||
- Data storage strategies
|
||||
- Basic visualization techniques
|
||||
- Clean UI component design
|
||||
- Error handling patterns
|
||||
|
||||
### **Intermediate Level (Iterations 6-12)**
|
||||
|
||||
**Web Resources to Learn From**:
|
||||
- D3.js documentation and examples
|
||||
- Observable notebooks for visualization
|
||||
- Advanced search algorithm implementations
|
||||
- Performance optimization guides
|
||||
- WebWorker usage patterns
|
||||
|
||||
**Techniques to Apply**:
|
||||
- Interactive data visualizations
|
||||
- Efficient search algorithms
|
||||
- Background processing with workers
|
||||
- Advanced filtering techniques
|
||||
- Statistical analysis methods
|
||||
|
||||
### **Advanced Level (Iterations 13-20)**
|
||||
|
||||
**Web Resources to Learn From**:
|
||||
- WebSocket and SSE real-time communication
|
||||
- Service Worker documentation
|
||||
- Broadcast Channel API guides
|
||||
- WebRTC peer-to-peer examples
|
||||
- Advanced IndexedDB patterns
|
||||
|
||||
**Techniques to Apply**:
|
||||
- Real-time synchronization
|
||||
- Background sync strategies
|
||||
- Cross-tab communication
|
||||
- P2P data sharing
|
||||
- Large-scale data management
|
||||
|
||||
### **Expert Level (Iterations 21+)**
|
||||
|
||||
**Web Resources to Learn From**:
|
||||
- TensorFlow.js tutorials
|
||||
- Vector search implementations
|
||||
- NLP in JavaScript guides
|
||||
- ML model optimization
|
||||
- Recommendation system patterns
|
||||
|
||||
**Techniques to Apply**:
|
||||
- In-browser machine learning
|
||||
- Semantic search algorithms
|
||||
- Predictive analytics
|
||||
- Automated categorization
|
||||
- Pattern recognition
|
||||
|
||||
## Quality Standards
|
||||
|
||||
### **Functionality**
|
||||
- Tool must solve a real Claude Code developer need
|
||||
- Must handle realistic data volumes (1000+ messages)
|
||||
- Must provide clear value over manual approaches
|
||||
- Error handling for malformed data
|
||||
- Graceful degradation for missing data
|
||||
|
||||
### **Usability**
|
||||
- Intuitive interface requiring no documentation to start
|
||||
- Clear visual hierarchy and information architecture
|
||||
- Responsive design for different screen sizes
|
||||
- Accessible (keyboard navigation, screen readers)
|
||||
- Fast initial load and responsive interactions
|
||||
|
||||
### **Code Quality**
|
||||
- Clean, well-commented JavaScript
|
||||
- Organized CSS with consistent naming
|
||||
- Efficient algorithms and data structures
|
||||
- No memory leaks or performance issues
|
||||
- Browser compatibility (modern browsers)
|
||||
|
||||
### **Documentation**
|
||||
- Clear tool purpose statement
|
||||
- Feature list with examples
|
||||
- Web source attribution with specifics
|
||||
- Techniques learned and applied
|
||||
- Usage instructions
|
||||
|
||||
### **Web Integration**
|
||||
- Must cite specific web source URL
|
||||
- Must demonstrate 1-3 specific techniques from source
|
||||
- Must show how web learning improved the tool
|
||||
- Should build on techniques from previous iterations
|
||||
- Must document what was learned
|
||||
|
||||
## Iteration Examples
|
||||
|
||||
### **Example 1: Session Timeline Viewer (Foundation)**
|
||||
**Web Source**: MDN Canvas Tutorial
|
||||
**Techniques Applied**:
|
||||
- Canvas API for rendering timeline
|
||||
- RequestAnimationFrame for smooth scrolling
|
||||
- Mouse event handling for interaction
|
||||
**Features**:
|
||||
- Horizontal timeline of session messages
|
||||
- Color-coded by message role
|
||||
- Hover for message preview
|
||||
- Click to expand full message
|
||||
|
||||
### **Example 7: Conversation Graph Visualizer (Intermediate)**
|
||||
**Web Source**: D3.js Force-Directed Graph Tutorial
|
||||
**Techniques Applied**:
|
||||
- D3 force simulation for node layout
|
||||
- Drag interactions for node repositioning
|
||||
- SVG path generation for message links
|
||||
**Features**:
|
||||
- Interactive node-link diagram
|
||||
- Parent-child message relationships
|
||||
- Tool usage indicators on nodes
|
||||
- Zoom and pan controls
|
||||
|
||||
### **Example 15: Shared Context Manager (Advanced)**
|
||||
**Web Source**: IndexedDB API Guide
|
||||
**Techniques Applied**:
|
||||
- IndexedDB for cross-session storage
|
||||
- Broadcast Channel for tab sync
|
||||
- Structured cloning for data transfer
|
||||
**Features**:
|
||||
- Central knowledge base across instances
|
||||
- Real-time updates when data changes
|
||||
- Query interface for context retrieval
|
||||
- Export/import for sharing
|
||||
|
||||
### **Example 23: Smart Context Recommender (Expert)**
|
||||
**Web Source**: TensorFlow.js Text Classification
|
||||
**Techniques Applied**:
|
||||
- Universal Sentence Encoder for embeddings
|
||||
- Cosine similarity for relevance scoring
|
||||
- K-nearest neighbors for recommendations
|
||||
**Features**:
|
||||
- Semantic search across all sessions
|
||||
- Context similarity scoring
|
||||
- Automatic related-session suggestions
|
||||
- Learning from usage patterns
|
||||
|
||||
## Ultra-Thinking Directive
|
||||
|
||||
Before each tool creation, deeply consider:
|
||||
|
||||
**Problem Space**:
|
||||
- What specific pain point does this solve for Claude Code users?
|
||||
- How do developers currently handle this need manually?
|
||||
- What would make this tool indispensable?
|
||||
- How does this fit into the broader developer workflow?
|
||||
|
||||
**Web Learning Integration**:
|
||||
- What web techniques would most benefit this tool?
|
||||
- How can the assigned URL's teachings be applied creatively?
|
||||
- What patterns from the web source are most valuable?
|
||||
- How can we adapt web examples to our specific use case?
|
||||
|
||||
**Technical Approach**:
|
||||
- What's the most efficient data structure for this tool?
|
||||
- How do we handle large datasets without performance issues?
|
||||
- What browser APIs provide the best capabilities?
|
||||
- How do we ensure offline functionality?
|
||||
|
||||
**User Experience**:
|
||||
- How can we make complex data immediately understandable?
|
||||
- What visualizations best convey the information?
|
||||
- How do we balance power-user features with simplicity?
|
||||
- What keyboard shortcuts would power users want?
|
||||
|
||||
**Progressive Enhancement**:
|
||||
- How does this build on previous tool iterations?
|
||||
- What techniques from earlier tools can be reused?
|
||||
- How does this enable future tool possibilities?
|
||||
- What knowledge does this add to our toolkit?
|
||||
|
||||
**Generate tools that are:**
|
||||
- **Immediately Useful**: Solve real problems developers face today
|
||||
- **Progressively Enhanced**: Each iteration more sophisticated than the last
|
||||
- **Web-Informed**: Demonstrably improved by web research
|
||||
- **Self-Contained**: Work perfectly as standalone HTML files
|
||||
- **Developer-Friendly**: Built by developers, for developers
|
||||
- **Future-Ready**: Enable coordination and advanced workflows
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
{
|
||||
"name": "Claude Code Developer Tools URL Strategy",
|
||||
"description": "Progressive web learning resources for building Claude Code observability, search, and coordination tools",
|
||||
"progression": "foundation → intermediate → advanced → expert",
|
||||
|
||||
"priming_urls": [
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications",
|
||||
"topic": "File API fundamentals for reading local transcript files",
|
||||
"key_concepts": ["FileReader", "Blob", "File handling", "JSONL parsing"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB",
|
||||
"topic": "IndexedDB for storing and querying large transcript datasets",
|
||||
"key_concepts": ["Object stores", "Indexes", "Transactions", "Cursors"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial",
|
||||
"topic": "Canvas API for data visualization",
|
||||
"key_concepts": ["2D context", "Drawing shapes", "Animations", "Interactions"]
|
||||
}
|
||||
],
|
||||
|
||||
"foundation": {
|
||||
"description": "Basic file handling, storage, and simple visualizations (Iterations 1-5)",
|
||||
"urls": [
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/FileReader",
|
||||
"topic": "FileReader API for loading transcript files",
|
||||
"iteration_range": "1-2",
|
||||
"techniques": ["readAsText", "onload events", "error handling", "progress tracking"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API",
|
||||
"topic": "LocalStorage for caching search results and preferences",
|
||||
"iteration_range": "1-3",
|
||||
"techniques": ["setItem/getItem", "JSON serialization", "storage events", "quota management"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas",
|
||||
"topic": "Canvas basics for timeline visualizations",
|
||||
"iteration_range": "2-4",
|
||||
"techniques": ["fillRect", "strokeStyle", "text rendering", "coordinate systems"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout",
|
||||
"topic": "CSS Grid for responsive tool layouts",
|
||||
"iteration_range": "1-5",
|
||||
"techniques": ["grid-template-areas", "auto-fit", "minmax", "gap"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse",
|
||||
"topic": "JSON parsing for JSONL transcript processing",
|
||||
"iteration_range": "1-2",
|
||||
"techniques": ["parse", "stringify", "reviver functions", "error handling"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial",
|
||||
"topic": "SVG for scalable charts and graphs",
|
||||
"iteration_range": "3-5",
|
||||
"techniques": ["path elements", "viewBox", "transforms", "CSS styling"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"intermediate": {
|
||||
"description": "Advanced search, analytics, interactive visualizations (Iterations 6-12)",
|
||||
"urls": [
|
||||
{
|
||||
"url": "https://d3js.org/getting-started",
|
||||
"topic": "D3.js fundamentals for data visualization",
|
||||
"iteration_range": "6-8",
|
||||
"techniques": ["data binding", "scales", "axes", "transitions"]
|
||||
},
|
||||
{
|
||||
"url": "https://observablehq.com/@d3/force-directed-graph",
|
||||
"topic": "Force-directed graphs for conversation relationships",
|
||||
"iteration_range": "7-9",
|
||||
"techniques": ["force simulation", "node dragging", "link rendering", "zoom behavior"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers",
|
||||
"topic": "Web Workers for background search processing",
|
||||
"iteration_range": "8-10",
|
||||
"techniques": ["postMessage", "onmessage", "transferable objects", "shared workers"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions",
|
||||
"topic": "Advanced regex for pattern searching",
|
||||
"iteration_range": "6-8",
|
||||
"techniques": ["lookahead/lookbehind", "capturing groups", "flags", "Unicode support"]
|
||||
},
|
||||
{
|
||||
"url": "https://observablehq.com/@d3/bar-chart",
|
||||
"topic": "Interactive bar charts for tool usage analytics",
|
||||
"iteration_range": "6-7",
|
||||
"techniques": ["ordinal scales", "tooltips", "hover effects", "sorting"]
|
||||
},
|
||||
{
|
||||
"url": "https://observablehq.com/@d3/zoomable-sunburst",
|
||||
"topic": "Hierarchical sunburst for nested data",
|
||||
"iteration_range": "9-11",
|
||||
"techniques": ["hierarchical data", "partition layout", "arc generation", "zoom transitions"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API",
|
||||
"topic": "Advanced IndexedDB queries and indexes",
|
||||
"iteration_range": "10-12",
|
||||
"techniques": ["compound indexes", "key ranges", "cursors", "versioning"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"advanced": {
|
||||
"description": "Real-time coordination, cross-instance communication, shared state (Iterations 13-20)",
|
||||
"urls": [
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API",
|
||||
"topic": "Broadcast Channel for cross-tab communication",
|
||||
"iteration_range": "13-15",
|
||||
"techniques": ["postMessage", "onmessage", "same-origin communication", "close"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API",
|
||||
"topic": "WebSocket for real-time multi-instance coordination",
|
||||
"iteration_range": "14-16",
|
||||
"techniques": ["connect", "send", "onmessage", "reconnection strategies"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API",
|
||||
"topic": "Service Workers for background sync",
|
||||
"iteration_range": "15-17",
|
||||
"techniques": ["registration", "fetch events", "cache API", "background sync"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events",
|
||||
"topic": "Server-Sent Events for live updates",
|
||||
"iteration_range": "14-16",
|
||||
"techniques": ["EventSource", "event listeners", "reconnection", "custom events"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API",
|
||||
"topic": "WebRTC for peer-to-peer data sharing",
|
||||
"iteration_range": "17-19",
|
||||
"techniques": ["RTCPeerConnection", "data channels", "signaling", "NAT traversal"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/SharedArrayBuffer",
|
||||
"topic": "SharedArrayBuffer for shared memory",
|
||||
"iteration_range": "18-20",
|
||||
"techniques": ["Atomics", "memory sharing", "synchronization", "race conditions"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/Notification",
|
||||
"topic": "Notifications API for cross-instance alerts",
|
||||
"iteration_range": "15-17",
|
||||
"techniques": ["requestPermission", "notification options", "click events", "persistence"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"expert": {
|
||||
"description": "ML-powered features, semantic search, predictive analytics (Iterations 21+)",
|
||||
"urls": [
|
||||
{
|
||||
"url": "https://www.tensorflow.org/js/tutorials",
|
||||
"topic": "TensorFlow.js for in-browser machine learning",
|
||||
"iteration_range": "21-23",
|
||||
"techniques": ["model loading", "inference", "training", "optimization"]
|
||||
},
|
||||
{
|
||||
"url": "https://www.tensorflow.org/js/tutorials/conversion/import_saved_model",
|
||||
"topic": "Using pre-trained models for text classification",
|
||||
"iteration_range": "22-24",
|
||||
"techniques": ["model conversion", "inference", "preprocessing", "postprocessing"]
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/tensorflow/tfjs-models/tree/master/universal-sentence-encoder",
|
||||
"topic": "Universal Sentence Encoder for semantic search",
|
||||
"iteration_range": "23-25",
|
||||
"techniques": ["embeddings", "cosine similarity", "vector search", "caching"]
|
||||
},
|
||||
{
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial",
|
||||
"topic": "WebGL for GPU-accelerated visualizations",
|
||||
"iteration_range": "24-26",
|
||||
"techniques": ["shaders", "buffers", "textures", "rendering pipeline"]
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/tensorflow/tfjs-examples",
|
||||
"topic": "TensorFlow.js examples for pattern recognition",
|
||||
"iteration_range": "21-25",
|
||||
"techniques": ["classification", "clustering", "anomaly detection", "time series"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"dynamic_search_templates": [
|
||||
"Claude Code transcript parsing site:stackoverflow.com",
|
||||
"IndexedDB full-text search implementation site:github.com",
|
||||
"JavaScript fuzzy string matching algorithms",
|
||||
"D3.js interactive timeline visualization",
|
||||
"WebSocket real-time coordination patterns",
|
||||
"Browser-based vector similarity search",
|
||||
"TensorFlow.js text classification tutorial",
|
||||
"Service Worker background sync patterns",
|
||||
"Canvas performance optimization techniques",
|
||||
"LocalStorage vs IndexedDB comparison"
|
||||
],
|
||||
|
||||
"fallback_resources": [
|
||||
{
|
||||
"category": "General Web APIs",
|
||||
"url": "https://developer.mozilla.org/en-US/docs/Web/API",
|
||||
"use_when": "Specific API documentation needed"
|
||||
},
|
||||
{
|
||||
"category": "JavaScript Patterns",
|
||||
"url": "https://javascript.info",
|
||||
"use_when": "Modern JavaScript techniques needed"
|
||||
},
|
||||
{
|
||||
"category": "Data Visualization",
|
||||
"url": "https://observablehq.com/@d3",
|
||||
"use_when": "D3.js examples and patterns needed"
|
||||
},
|
||||
{
|
||||
"category": "Machine Learning",
|
||||
"url": "https://www.tensorflow.org/js",
|
||||
"use_when": "ML implementation guidance needed"
|
||||
}
|
||||
],
|
||||
|
||||
"notes": [
|
||||
"URLs progress from basic web APIs to advanced coordination and ML",
|
||||
"Each iteration should demonstrate specific techniques from assigned URL",
|
||||
"Foundation iterations establish core patterns that advanced iterations build upon",
|
||||
"Expert iterations combine multiple techniques from earlier learnings",
|
||||
"All tools must remain self-contained single HTML files",
|
||||
"Focus on developer tools that solve real Claude Code workflow challenges"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue