ccakashic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,593 @@
1
+ 'use strict';
2
+
3
+ function getCSS() {
4
+ return `
5
+ :root {
6
+ --bg: #fafaf9;
7
+ --bg-secondary: #f5f5f4;
8
+ --text: #292524;
9
+ --text-muted: #78716c;
10
+ --border: #e7e5e4;
11
+ --user-bg: #e0f2fe;
12
+ --user-border: #bae6fd;
13
+ --assistant-bg: #f5f5f4;
14
+ --assistant-border: #d6d3d1;
15
+ --tool-bg: #fafaf9;
16
+ --tool-border: #e7e5e4;
17
+ --thinking-bg: #fefce8;
18
+ --thinking-border: #fde68a;
19
+ --system-text: #a8a29e;
20
+ --code-bg: #f5f5f4;
21
+ --code-text: #9f1239;
22
+ --code-block-bg: #1c1917;
23
+ --code-block-text: #e7e5e4;
24
+ --diff-add-bg: #ecfdf5;
25
+ --diff-add-text: #065f46;
26
+ --diff-del-bg: #fef2f2;
27
+ --diff-del-text: #991b1b;
28
+ --diff-hunk-text: #78716c;
29
+ --subagent-border: #a78bfa;
30
+ --link: #2563eb;
31
+ }
32
+
33
+ @media (prefers-color-scheme: dark) {
34
+ :root {
35
+ --bg: #1c1917;
36
+ --bg-secondary: #292524;
37
+ --text: #e7e5e4;
38
+ --text-muted: #a8a29e;
39
+ --border: #44403c;
40
+ --user-bg: #164e63;
41
+ --user-border: #0e7490;
42
+ --assistant-bg: #292524;
43
+ --assistant-border: #57534e;
44
+ --tool-bg: #1c1917;
45
+ --tool-border: #44403c;
46
+ --thinking-bg: #422006;
47
+ --thinking-border: #a16207;
48
+ --system-text: #78716c;
49
+ --code-bg: #352f2b;
50
+ --code-text: #fda4af;
51
+ --code-block-bg: #0c0a09;
52
+ --code-block-text: #e7e5e4;
53
+ --diff-add-bg: #052e16;
54
+ --diff-add-text: #6ee7b7;
55
+ --diff-del-bg: #2d0f0f;
56
+ --diff-del-text: #fca5a5;
57
+ --diff-hunk-text: #78716c;
58
+ --subagent-border: #8b5cf6;
59
+ --link: #7dd3fc;
60
+ }
61
+ }
62
+
63
+ * { margin: 0; padding: 0; box-sizing: border-box; }
64
+
65
+ body {
66
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
67
+ background: var(--bg);
68
+ color: var(--text);
69
+ line-height: 1.6;
70
+ }
71
+
72
+ /* GitHub Corner (by Tim Holman, tholman.com/github-corners) */
73
+ .github-corner {
74
+ position: fixed;
75
+ top: 0;
76
+ right: 0;
77
+ z-index: 200;
78
+ color: var(--bg);
79
+ }
80
+ .github-corner svg {
81
+ fill: var(--text);
82
+ color: var(--bg);
83
+ }
84
+ .github-corner:hover .octo-arm {
85
+ animation: octocat-wave 560ms ease-in-out;
86
+ }
87
+ @keyframes octocat-wave {
88
+ 0%, 100% { transform: rotate(0); }
89
+ 20%, 60% { transform: rotate(-25deg); }
90
+ 40%, 80% { transform: rotate(10deg); }
91
+ }
92
+ @media (max-width: 500px) {
93
+ .github-corner:hover .octo-arm { animation: none; }
94
+ .github-corner .octo-arm { animation: octocat-wave 560ms ease-in-out; }
95
+ }
96
+
97
+ .session-header {
98
+ max-width: 900px;
99
+ margin: 0 auto;
100
+ padding: 24px 16px 16px;
101
+ border-bottom: 1px solid var(--border);
102
+ }
103
+
104
+ .session-header h1 {
105
+ font-size: 1.25rem;
106
+ font-weight: 600;
107
+ }
108
+
109
+ .session-meta {
110
+ display: flex;
111
+ flex-wrap: wrap;
112
+ gap: 16px;
113
+ margin-top: 8px;
114
+ font-size: 0.85rem;
115
+ color: var(--text-muted);
116
+ }
117
+
118
+ .chat-container {
119
+ max-width: 900px;
120
+ margin: 0 auto;
121
+ padding: 20px 16px;
122
+ display: flex;
123
+ flex-direction: column;
124
+ gap: 28px;
125
+ }
126
+
127
+ .msg {
128
+ padding: 12px 16px;
129
+ border-radius: 16px;
130
+ max-width: 80%;
131
+ position: relative;
132
+ }
133
+
134
+ .msg .timestamp {
135
+ display: block;
136
+ font-size: 0.7rem;
137
+ color: var(--text-muted);
138
+ margin-bottom: 4px;
139
+ text-decoration: none;
140
+ }
141
+ .msg .timestamp:hover {
142
+ color: var(--link);
143
+ text-decoration: underline;
144
+ }
145
+
146
+ .msg-user {
147
+ align-self: flex-end;
148
+ margin-left: 20%;
149
+ margin-top: 32px;
150
+ background: var(--user-bg);
151
+ border: 1px solid var(--user-border);
152
+ border-radius: 16px 16px 4px 16px;
153
+ }
154
+
155
+ .msg-assistant {
156
+ align-self: flex-start;
157
+ margin-right: 20%;
158
+ background: var(--assistant-bg);
159
+ border: 1px solid var(--assistant-border);
160
+ border-radius: 16px 16px 16px 4px;
161
+ }
162
+
163
+ .msg-thinking {
164
+ align-self: flex-start;
165
+ background: var(--thinking-bg);
166
+ border: 1px solid var(--thinking-border);
167
+ padding: 6px 12px;
168
+ font-size: 0.85rem;
169
+ color: var(--text-muted);
170
+ font-style: italic;
171
+ }
172
+
173
+ .msg-tool {
174
+ align-self: flex-start;
175
+ max-width: 95%;
176
+ width: 100%;
177
+ background: var(--tool-bg);
178
+ border: 1px solid var(--tool-border);
179
+ padding: 0;
180
+ border-radius: 8px;
181
+ overflow: hidden;
182
+ }
183
+
184
+ .msg-tool details { padding: 0; }
185
+
186
+ .msg-tool summary {
187
+ padding: 10px 14px;
188
+ cursor: pointer;
189
+ font-size: 0.85rem;
190
+ display: flex;
191
+ align-items: center;
192
+ gap: 8px;
193
+ user-select: none;
194
+ list-style: none;
195
+ transition: background 0.15s;
196
+ }
197
+
198
+ .msg-tool summary::-webkit-details-marker { display: none; }
199
+
200
+ .msg-tool summary::before {
201
+ content: '\\25B6';
202
+ font-size: 0.6rem;
203
+ color: var(--text-muted);
204
+ transition: transform 0.2s;
205
+ flex-shrink: 0;
206
+ }
207
+
208
+ .msg-tool details[open] > summary::before {
209
+ transform: rotate(90deg);
210
+ }
211
+
212
+ .msg-tool summary:hover {
213
+ background: var(--bg-secondary);
214
+ }
215
+
216
+ .msg-tool summary::after {
217
+ content: 'click to expand';
218
+ font-size: 0.65rem;
219
+ color: var(--text-muted);
220
+ margin-left: auto;
221
+ opacity: 0.5;
222
+ }
223
+
224
+ .msg-tool details[open] > summary::after {
225
+ content: 'click to collapse';
226
+ }
227
+
228
+ .tool-summary { font-family: monospace; }
229
+ .tool-summary code {
230
+ background: var(--code-bg);
231
+ color: var(--code-text);
232
+ padding: 2px 6px;
233
+ border-radius: 4px;
234
+ font-size: 0.8rem;
235
+ }
236
+
237
+ .tool-details {
238
+ border-top: 1px solid var(--border);
239
+ padding: 12px 14px;
240
+ }
241
+
242
+ .tool-input { margin-bottom: 8px; }
243
+ .tool-input-label {
244
+ font-size: 0.75rem;
245
+ color: var(--text-muted);
246
+ margin-bottom: 4px;
247
+ text-transform: uppercase;
248
+ letter-spacing: 0.05em;
249
+ }
250
+
251
+ .tool-meta {
252
+ font-size: 0.8rem;
253
+ color: var(--text-muted);
254
+ margin-bottom: 8px;
255
+ font-family: monospace;
256
+ }
257
+
258
+ .tool-output { margin-top: 8px; }
259
+ .tool-output.stderr pre { border-left: 3px solid #ef4444; }
260
+
261
+ .tool-output pre, .tool-input pre {
262
+ background: var(--code-block-bg);
263
+ color: var(--code-block-text);
264
+ padding: 12px;
265
+ border-radius: 6px;
266
+ overflow-x: auto;
267
+ font-size: 0.8rem;
268
+ line-height: 1.5;
269
+ max-height: 400px;
270
+ overflow-y: auto;
271
+ }
272
+
273
+ .tool-output pre code, .tool-input pre code {
274
+ background: none;
275
+ padding: 0;
276
+ }
277
+
278
+ /* Diff styles */
279
+ .diff {
280
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
281
+ font-size: 0.8rem;
282
+ border-radius: 6px;
283
+ overflow: hidden;
284
+ border: 1px solid var(--border);
285
+ }
286
+
287
+ .diff-hunk {
288
+ background: var(--bg-secondary);
289
+ color: var(--diff-hunk-text);
290
+ padding: 4px 12px;
291
+ font-style: italic;
292
+ }
293
+
294
+ .diff-add {
295
+ background: var(--diff-add-bg);
296
+ color: var(--diff-add-text);
297
+ padding: 1px 12px;
298
+ white-space: pre-wrap;
299
+ }
300
+
301
+ .diff-del {
302
+ background: var(--diff-del-bg);
303
+ color: var(--diff-del-text);
304
+ padding: 1px 12px;
305
+ white-space: pre-wrap;
306
+ }
307
+
308
+ .diff-ctx {
309
+ padding: 1px 12px;
310
+ white-space: pre-wrap;
311
+ color: var(--text-muted);
312
+ }
313
+
314
+ /* System messages */
315
+ .msg-system {
316
+ align-self: center;
317
+ font-size: 0.75rem;
318
+ color: var(--system-text);
319
+ padding: 4px 12px;
320
+ max-width: 100%;
321
+ }
322
+
323
+ .duration {
324
+ background: var(--bg-secondary);
325
+ padding: 2px 8px;
326
+ border-radius: 4px;
327
+ border: 1px solid var(--border);
328
+ }
329
+
330
+ /* Subagent */
331
+ .msg-subagent {
332
+ max-width: 95%;
333
+ width: 100%;
334
+ padding: 0;
335
+ border: 1px solid var(--subagent-border);
336
+ border-radius: 8px;
337
+ overflow: hidden;
338
+ }
339
+
340
+ .msg-subagent summary {
341
+ padding: 10px 14px;
342
+ cursor: pointer;
343
+ font-size: 0.85rem;
344
+ background: var(--bg-secondary);
345
+ list-style: none;
346
+ display: flex;
347
+ align-items: center;
348
+ gap: 8px;
349
+ transition: background 0.15s;
350
+ }
351
+
352
+ .msg-subagent summary::-webkit-details-marker { display: none; }
353
+
354
+ .msg-subagent summary::before {
355
+ content: '\\25B6';
356
+ font-size: 0.6rem;
357
+ color: var(--subagent-border);
358
+ transition: transform 0.2s;
359
+ flex-shrink: 0;
360
+ }
361
+
362
+ .msg-subagent details[open] > summary::before {
363
+ transform: rotate(90deg);
364
+ }
365
+
366
+ .msg-subagent summary::after {
367
+ content: 'click to expand';
368
+ font-size: 0.65rem;
369
+ color: var(--text-muted);
370
+ margin-left: auto;
371
+ opacity: 0.5;
372
+ }
373
+
374
+ .msg-subagent details[open] > summary::after {
375
+ content: 'click to collapse';
376
+ }
377
+
378
+ .msg-subagent summary:hover {
379
+ background: var(--border);
380
+ }
381
+
382
+ .subagent-content {
383
+ border-top: 1px solid var(--border);
384
+ border-left: 3px solid var(--subagent-border);
385
+ padding: 12px;
386
+ display: flex;
387
+ flex-direction: column;
388
+ gap: 10px;
389
+ }
390
+
391
+ .subagent-content .msg { max-width: 100%; }
392
+
393
+ /* Inline subagent (inside tool details) */
394
+ .subagent-inline {
395
+ margin-top: 12px;
396
+ border: 1px solid var(--subagent-border);
397
+ border-radius: 8px;
398
+ overflow: hidden;
399
+ }
400
+ .subagent-inline > details > summary {
401
+ padding: 8px 12px;
402
+ cursor: pointer;
403
+ font-size: 0.8rem;
404
+ background: var(--bg-secondary);
405
+ list-style: none;
406
+ display: flex;
407
+ align-items: center;
408
+ gap: 8px;
409
+ transition: background 0.15s;
410
+ }
411
+ .subagent-inline > details > summary::-webkit-details-marker { display: none; }
412
+ .subagent-inline > details > summary::before {
413
+ content: '\\25B6';
414
+ font-size: 0.55rem;
415
+ color: var(--subagent-border);
416
+ transition: transform 0.2s;
417
+ }
418
+ .subagent-inline > details[open] > summary::before {
419
+ transform: rotate(90deg);
420
+ }
421
+ .subagent-inline > details > summary:hover { background: var(--border); }
422
+ .subagent-inline .subagent-content {
423
+ border-top: 1px solid var(--border);
424
+ border-left: 3px solid var(--subagent-border);
425
+ padding: 12px;
426
+ display: flex;
427
+ flex-direction: column;
428
+ gap: 16px;
429
+ }
430
+
431
+ /* Markdown content */
432
+ .msg-content h1, .msg-content h2, .msg-content h3 {
433
+ margin-top: 0.5em;
434
+ margin-bottom: 0.25em;
435
+ }
436
+ .msg-content h1 { font-size: 1.2rem; }
437
+ .msg-content h2 { font-size: 1.1rem; }
438
+ .msg-content h3 { font-size: 1rem; }
439
+
440
+ .msg-content p { margin: 0.4em 0; }
441
+
442
+ .msg-content pre {
443
+ background: var(--code-block-bg);
444
+ color: var(--code-block-text);
445
+ padding: 10px;
446
+ border-radius: 6px;
447
+ overflow-x: auto;
448
+ font-size: 0.8rem;
449
+ margin: 0.5em 0;
450
+ }
451
+
452
+ .msg-content code {
453
+ background: var(--code-bg);
454
+ color: var(--code-text);
455
+ padding: 2px 6px;
456
+ border-radius: 4px;
457
+ font-size: 0.85em;
458
+ font-weight: 500;
459
+ }
460
+
461
+ .msg-content pre code {
462
+ background: none;
463
+ color: inherit;
464
+ padding: 0;
465
+ font-weight: normal;
466
+ }
467
+
468
+ .msg-content ul, .msg-content ol {
469
+ margin: 0.4em 0;
470
+ padding-left: 1.5em;
471
+ }
472
+
473
+ .msg-content table {
474
+ border-collapse: collapse;
475
+ margin: 0.5em 0;
476
+ font-size: 0.85rem;
477
+ }
478
+
479
+ .msg-content th, .msg-content td {
480
+ border: 1px solid var(--border);
481
+ padding: 4px 10px;
482
+ }
483
+
484
+ .msg-content th { background: var(--bg-secondary); }
485
+
486
+ .msg-content blockquote {
487
+ border-left: 3px solid var(--border);
488
+ padding-left: 12px;
489
+ color: var(--text-muted);
490
+ margin: 0.4em 0;
491
+ }
492
+
493
+ .msg-content a {
494
+ color: var(--link);
495
+ text-decoration: none;
496
+ }
497
+ .msg-content a:hover { text-decoration: underline; }
498
+
499
+ /* Keyboard nav highlight */
500
+ .msg.focused {
501
+ outline: 2px solid var(--link);
502
+ outline-offset: 2px;
503
+ }
504
+ `;
505
+ }
506
+
507
+ function getAppJS() {
508
+ return `
509
+ (function() {
510
+ // Simple markdown renderer (no external dependency)
511
+ function renderMarkdown(text) {
512
+ // Escape HTML first (already escaped by server, but for client-side re-render)
513
+ // Actually the text is already HTML-escaped, so we need to unescape first
514
+ text = text
515
+ .replace(/&/g, '&')
516
+ .replace(/&lt;/g, '<')
517
+ .replace(/&gt;/g, '>')
518
+ .replace(/&quot;/g, '"');
519
+
520
+ // Code blocks (fenced)
521
+ text = text.replace(/\`\`\`(\\w*)?\\n([\\s\\S]*?)\`\`\`/g, function(m, lang, code) {
522
+ return '<pre><code class="language-' + (lang || '') + '">' + esc(code.trim()) + '</code></pre>';
523
+ });
524
+
525
+ // Inline code
526
+ text = text.replace(/\`([^\`]+)\`/g, '<code>' + '$1' + '</code>');
527
+
528
+ // Bold
529
+ text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
530
+
531
+ // Italic
532
+ text = text.replace(/(?<![*])\\*(?![*])(.+?)(?<![*])\\*(?![*])/g, '<em>$1</em>');
533
+
534
+ // Headers
535
+ text = text.replace(/^### (.+)$/gm, '<h3>$1</h3>');
536
+ text = text.replace(/^## (.+)$/gm, '<h2>$1</h2>');
537
+ text = text.replace(/^# (.+)$/gm, '<h1>$1</h1>');
538
+
539
+ // Unordered lists
540
+ text = text.replace(/^[\\-*] (.+)$/gm, '<li>$1</li>');
541
+ text = text.replace(/(<li>.*<\\/li>\\n?)+/g, '<ul>$&</ul>');
542
+
543
+ // Links
544
+ text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2" target="_blank">$1</a>');
545
+
546
+ // Paragraphs (simple: double newlines)
547
+ text = text.replace(/\\n\\n/g, '</p><p>');
548
+ text = '<p>' + text + '</p>';
549
+
550
+ // Clean up empty paragraphs
551
+ text = text.replace(/<p><\\/p>/g, '');
552
+ text = text.replace(/<p>(<h[123]>)/g, '$1');
553
+ text = text.replace(/(<\\/h[123]>)<\\/p>/g, '$1');
554
+ text = text.replace(/<p>(<pre>)/g, '$1');
555
+ text = text.replace(/(<\\/pre>)<\\/p>/g, '$1');
556
+ text = text.replace(/<p>(<ul>)/g, '$1');
557
+ text = text.replace(/(<\\/ul>)<\\/p>/g, '$1');
558
+
559
+ return text;
560
+ }
561
+
562
+ function esc(s) {
563
+ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
564
+ }
565
+
566
+ // Render markdown in all [data-markdown] elements
567
+ document.querySelectorAll('[data-markdown]').forEach(function(el) {
568
+ el.innerHTML = renderMarkdown(el.textContent);
569
+ });
570
+
571
+ // Keyboard navigation: j/k to move between user messages
572
+ var msgEls = Array.from(document.querySelectorAll('.msg-user, .msg-assistant'));
573
+ var currentIdx = -1;
574
+
575
+ document.addEventListener('keydown', function(e) {
576
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
577
+
578
+ if (e.key === 'j' || e.key === 'k') {
579
+ if (e.key === 'j') currentIdx = Math.min(currentIdx + 1, msgEls.length - 1);
580
+ if (e.key === 'k') currentIdx = Math.max(currentIdx - 1, 0);
581
+
582
+ msgEls.forEach(function(el) { el.classList.remove('focused'); });
583
+ if (msgEls[currentIdx]) {
584
+ msgEls[currentIdx].classList.add('focused');
585
+ msgEls[currentIdx].scrollIntoView({ behavior: 'smooth', block: 'center' });
586
+ }
587
+ }
588
+ });
589
+ })();
590
+ `;
591
+ }
592
+
593
+ module.exports = { getCSS, getAppJS };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "ccakashic",
3
+ "version": "0.1.0",
4
+ "description": "Browse Claude Code session logs (~/.claude/projects/) as beautiful HTML in your browser",
5
+ "bin": {
6
+ "cctape": "./bin/cctape.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "lib",
11
+ "vendor",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "scripts": {
18
+ "start": "node bin/cctape.js"
19
+ },
20
+ "dependencies": {},
21
+ "keywords": [
22
+ "claude",
23
+ "claude-code",
24
+ "session",
25
+ "history",
26
+ "viewer",
27
+ "log",
28
+ "jsonl",
29
+ "html"
30
+ ],
31
+ "author": "ashimon83",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/ashimon83/cctape.git"
36
+ },
37
+ "homepage": "https://github.com/ashimon83/cctape#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/ashimon83/cctape/issues"
40
+ }
41
+ }