codedocent 0.1.0__py3-none-any.whl

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,1032 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>codedocent — interactive</title>
7
+ <script>
8
+ if (localStorage.getItem('cd-theme') === 'light')
9
+ document.documentElement.classList.add('light-mode');
10
+ </script>
11
+ <style>
12
+ :root {
13
+ --bg-body: #1a1a2e;
14
+ --bg-header: #12122a;
15
+ --bg-node: #222240;
16
+ --bg-imports: #1e1e38;
17
+ --bg-pseudocode: #1e2a38;
18
+ --bg-source: #0d0d1a;
19
+ --bg-badge: #2e2e50;
20
+ --bg-btn: #2e2e50;
21
+ --bg-btn-hover: #3a3a5a;
22
+ --bg-btn-active: #4466aa;
23
+ --bg-breadcrumb: #1e1e38;
24
+ --bg-header-hover: #2a2a48;
25
+
26
+ --text-primary: #e0e0e0;
27
+ --text-body: #e0e0e0;
28
+ --text-name: #e0e0f0;
29
+ --text-badge: #aaaacc;
30
+ --text-lines: #8888aa;
31
+ --text-range: #8888aa;
32
+ --text-warning: #FFD700;
33
+ --text-imports-label: #8888aa;
34
+ --text-imports-item: #bbbbcc;
35
+ --text-summary: #bbbbcc;
36
+ --text-placeholder: #666688;
37
+ --text-pseudocode-label: #8888aa;
38
+ --text-pseudocode: #ccccdd;
39
+ --text-btn: #bbbbcc;
40
+ --text-link: #7799ee;
41
+ --text-toggle: #8888aa;
42
+ --text-analyzing: #8888aa;
43
+ --text-breadcrumb-sep: #666688;
44
+ --text-breadcrumb-current: #e0e0e0;
45
+ --text-source: #D4D4D4;
46
+
47
+ --border-node: #3a3a5a;
48
+ --border-imports: #3a3a5a;
49
+ --border-pseudocode: #3a4a5a;
50
+ --border-btn: #4a4a6a;
51
+ --border-breadcrumb: #3a3a5a;
52
+ --border-textarea: #555;
53
+
54
+ --shadow-node: rgba(0, 0, 0, 0.3);
55
+ }
56
+
57
+ :root.light-mode {
58
+ --bg-body: #F5F5F5;
59
+ --bg-header: #1A1A2E;
60
+ --bg-node: #FFFFFF;
61
+ --bg-imports: #F9F9F9;
62
+ --bg-pseudocode: #F0F4F8;
63
+ --bg-source: #1E1E2E;
64
+ --bg-badge: #EEEEEE;
65
+ --bg-btn: #F5F5F5;
66
+ --bg-btn-hover: #E8E8E8;
67
+ --bg-btn-active: #1A1A2E;
68
+ --bg-breadcrumb: #EDEDF0;
69
+ --bg-header-hover: #FAFAFA;
70
+
71
+ --text-primary: #333;
72
+ --text-body: #333;
73
+ --text-name: #1A1A2E;
74
+ --text-badge: #666666;
75
+ --text-lines: #888888;
76
+ --text-range: #AAAAAA;
77
+ --text-warning: #B8860B;
78
+ --text-imports-label: #999999;
79
+ --text-imports-item: #555555;
80
+ --text-summary: #555555;
81
+ --text-placeholder: #AAAAAA;
82
+ --text-pseudocode-label: #999999;
83
+ --text-pseudocode: #444444;
84
+ --text-btn: #555;
85
+ --text-link: #5566DD;
86
+ --text-toggle: #888;
87
+ --text-analyzing: #888;
88
+ --text-breadcrumb-sep: #999;
89
+ --text-breadcrumb-current: #333;
90
+ --text-source: #D4D4D4;
91
+
92
+ --border-node: #E0E0E0;
93
+ --border-imports: #E8E8E8;
94
+ --border-pseudocode: #D0D8E0;
95
+ --border-btn: #D0D0D0;
96
+ --border-breadcrumb: #D8D8DC;
97
+ --border-textarea: #555;
98
+
99
+ --shadow-node: rgba(0, 0, 0, 0.06);
100
+ }
101
+
102
+ *, *::before, *::after {
103
+ box-sizing: border-box;
104
+ }
105
+
106
+ body {
107
+ margin: 0;
108
+ padding: 0;
109
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
110
+ background: var(--bg-body);
111
+ color: var(--text-body);
112
+ line-height: 1.5;
113
+ }
114
+
115
+ .cd-header {
116
+ background: var(--bg-header);
117
+ color: #FFFFFF;
118
+ padding: 16px 24px;
119
+ margin-bottom: 0;
120
+ display: flex;
121
+ justify-content: space-between;
122
+ align-items: center;
123
+ }
124
+
125
+ .cd-header__title {
126
+ margin: 0;
127
+ font-size: 20px;
128
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
129
+ font-weight: 700;
130
+ }
131
+
132
+ .cd-header__path {
133
+ margin: 4px 0 0;
134
+ font-size: 13px;
135
+ opacity: 0.7;
136
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
137
+ }
138
+
139
+ .cd-theme-toggle {
140
+ background: none;
141
+ border: 1px solid rgba(255,255,255,0.3);
142
+ border-radius: 8px;
143
+ font-size: 20px;
144
+ cursor: pointer;
145
+ padding: 4px 8px;
146
+ color: #FFF;
147
+ line-height: 1;
148
+ }
149
+
150
+ .cd-theme-toggle:hover {
151
+ border-color: rgba(255,255,255,0.6);
152
+ }
153
+
154
+ .cd-breadcrumb {
155
+ background: var(--bg-breadcrumb);
156
+ border-bottom: 1px solid var(--border-breadcrumb);
157
+ padding: 8px 24px;
158
+ font-size: 13px;
159
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
160
+ margin-bottom: 24px;
161
+ display: flex;
162
+ flex-wrap: wrap;
163
+ gap: 4px;
164
+ align-items: center;
165
+ }
166
+
167
+ .cd-breadcrumb__item {
168
+ color: var(--text-link);
169
+ cursor: pointer;
170
+ text-decoration: none;
171
+ }
172
+
173
+ .cd-breadcrumb__item:hover {
174
+ text-decoration: underline;
175
+ }
176
+
177
+ .cd-breadcrumb__sep {
178
+ color: var(--text-breadcrumb-sep);
179
+ margin: 0 2px;
180
+ }
181
+
182
+ .cd-breadcrumb__current {
183
+ color: var(--text-breadcrumb-current);
184
+ font-weight: 600;
185
+ }
186
+
187
+ .cd-main {
188
+ max-width: 960px;
189
+ margin: 0 auto;
190
+ padding: 0 16px 48px;
191
+ }
192
+
193
+ .cd-node {
194
+ background: var(--bg-node);
195
+ border: 1px solid var(--border-node);
196
+ border-radius: 8px;
197
+ box-shadow: 0 1px 3px var(--shadow-node);
198
+ margin-bottom: 12px;
199
+ border-left-width: 5px;
200
+ border-left-style: solid;
201
+ border-left-color: var(--node-color, #CCCCCC);
202
+ }
203
+
204
+ .cd-node__header {
205
+ display: flex;
206
+ flex-wrap: wrap;
207
+ align-items: center;
208
+ gap: 8px;
209
+ padding: 10px 14px;
210
+ cursor: pointer;
211
+ user-select: none;
212
+ }
213
+
214
+ .cd-node__header:hover {
215
+ background: var(--bg-header-hover);
216
+ }
217
+
218
+ .cd-node__toggle {
219
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
220
+ font-size: 12px;
221
+ color: var(--text-toggle);
222
+ width: 16px;
223
+ text-align: center;
224
+ flex-shrink: 0;
225
+ }
226
+
227
+ .cd-node__icon {
228
+ font-size: 16px;
229
+ flex-shrink: 0;
230
+ }
231
+
232
+ .cd-node__name {
233
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
234
+ font-weight: 700;
235
+ font-size: 14px;
236
+ color: var(--text-name);
237
+ }
238
+
239
+ .cd-node__badge {
240
+ display: inline-block;
241
+ font-size: 10px;
242
+ font-weight: 600;
243
+ text-transform: uppercase;
244
+ letter-spacing: 0.05em;
245
+ padding: 2px 8px;
246
+ border-radius: 10px;
247
+ background: var(--bg-badge);
248
+ color: var(--text-badge);
249
+ }
250
+
251
+ .cd-node__lines {
252
+ font-size: 12px;
253
+ color: var(--text-lines);
254
+ }
255
+
256
+ .cd-node__range {
257
+ font-size: 12px;
258
+ color: var(--text-range);
259
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
260
+ }
261
+
262
+ .cd-node__quality {
263
+ font-size: 14px;
264
+ flex-shrink: 0;
265
+ }
266
+
267
+ .cd-node__warning-text {
268
+ font-size: 11px;
269
+ color: var(--text-warning);
270
+ font-style: italic;
271
+ }
272
+
273
+ .cd-node__body {
274
+ padding: 0 14px 10px;
275
+ display: none;
276
+ }
277
+
278
+ .cd-node__body--open {
279
+ display: block;
280
+ }
281
+
282
+ .cd-imports {
283
+ background: var(--bg-imports);
284
+ border: 1px solid var(--border-imports);
285
+ border-radius: 6px;
286
+ padding: 8px 12px;
287
+ margin-bottom: 8px;
288
+ }
289
+
290
+ .cd-imports__label {
291
+ font-size: 10px;
292
+ font-weight: 700;
293
+ text-transform: uppercase;
294
+ letter-spacing: 0.08em;
295
+ color: var(--text-imports-label);
296
+ margin-bottom: 4px;
297
+ }
298
+
299
+ .cd-imports__item {
300
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
301
+ font-size: 13px;
302
+ color: var(--text-imports-item);
303
+ padding: 1px 0;
304
+ }
305
+
306
+ .cd-summary {
307
+ color: var(--text-summary);
308
+ font-size: 13px;
309
+ margin-bottom: 8px;
310
+ }
311
+
312
+ .cd-analyze-link {
313
+ color: var(--text-link);
314
+ cursor: pointer;
315
+ font-size: 13px;
316
+ font-style: italic;
317
+ margin-bottom: 8px;
318
+ }
319
+
320
+ .cd-analyze-link:hover {
321
+ text-decoration: underline;
322
+ }
323
+
324
+ .cd-analyzing {
325
+ color: var(--text-analyzing);
326
+ font-size: 13px;
327
+ font-style: italic;
328
+ margin-bottom: 8px;
329
+ animation: cd-pulse 1.5s ease-in-out infinite;
330
+ }
331
+
332
+ @keyframes cd-pulse {
333
+ 0%, 100% { opacity: 1; }
334
+ 50% { opacity: 0.4; }
335
+ }
336
+
337
+ .cd-pseudocode {
338
+ background: var(--bg-pseudocode);
339
+ border: 1px solid var(--border-pseudocode);
340
+ border-radius: 6px;
341
+ padding: 8px 12px;
342
+ margin-bottom: 8px;
343
+ }
344
+
345
+ .cd-pseudocode__label {
346
+ font-size: 10px;
347
+ font-weight: 700;
348
+ text-transform: uppercase;
349
+ letter-spacing: 0.08em;
350
+ color: var(--text-pseudocode-label);
351
+ margin-bottom: 4px;
352
+ }
353
+
354
+ .cd-pseudocode__code {
355
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
356
+ font-size: 13px;
357
+ color: var(--text-pseudocode);
358
+ margin: 0;
359
+ white-space: pre-wrap;
360
+ }
361
+
362
+ .cd-warnings {
363
+ margin-bottom: 8px;
364
+ }
365
+
366
+ .cd-warnings__item {
367
+ font-size: 13px;
368
+ color: var(--text-warning);
369
+ padding: 2px 0;
370
+ }
371
+
372
+ .cd-node__children {
373
+ padding-left: 16px;
374
+ }
375
+
376
+ .cd-code-actions {
377
+ display: flex;
378
+ flex-wrap: wrap;
379
+ gap: 6px;
380
+ margin-bottom: 8px;
381
+ }
382
+
383
+ .cd-code-btn {
384
+ font-size: 12px;
385
+ font-weight: 600;
386
+ padding: 4px 12px;
387
+ border-radius: 14px;
388
+ border: 1px solid var(--border-btn);
389
+ background: var(--bg-btn);
390
+ color: var(--text-btn);
391
+ cursor: pointer;
392
+ transition: background 0.15s, color 0.15s;
393
+ }
394
+
395
+ .cd-code-btn:hover {
396
+ background: var(--bg-btn-hover);
397
+ }
398
+
399
+ .cd-code-btn--active {
400
+ background: var(--bg-btn-active);
401
+ color: #FFF;
402
+ border-color: var(--bg-btn-active);
403
+ }
404
+
405
+ .cd-code-btn--copied {
406
+ background: #2E7D32;
407
+ color: #FFF;
408
+ border-color: #2E7D32;
409
+ }
410
+
411
+ .cd-source-display {
412
+ display: none;
413
+ background: var(--bg-source);
414
+ color: var(--text-source);
415
+ border-radius: 6px;
416
+ padding: 12px;
417
+ margin-bottom: 8px;
418
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
419
+ font-size: 13px;
420
+ overflow-x: auto;
421
+ white-space: pre;
422
+ line-height: 1.5;
423
+ margin-top: 0;
424
+ }
425
+
426
+ .cd-source-display--open {
427
+ display: block;
428
+ }
429
+
430
+ @media (max-width: 600px) {
431
+ .cd-header {
432
+ padding: 12px 12px;
433
+ }
434
+ .cd-breadcrumb {
435
+ padding: 8px 12px;
436
+ }
437
+ .cd-main {
438
+ padding: 0 8px 32px;
439
+ }
440
+ .cd-node__header {
441
+ padding: 8px 10px;
442
+ }
443
+ .cd-node__body {
444
+ padding: 0 10px 8px;
445
+ }
446
+ .cd-node__children {
447
+ padding-left: 8px;
448
+ }
449
+ }
450
+
451
+ .cd-replace-panel {
452
+ margin-bottom: 8px;
453
+ }
454
+ .cd-replace-panel textarea {
455
+ width: 100%;
456
+ min-height: 120px;
457
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
458
+ font-size: 13px;
459
+ background: var(--bg-source);
460
+ color: var(--text-source);
461
+ border: 1px solid var(--border-textarea);
462
+ border-radius: 6px;
463
+ padding: 12px;
464
+ line-height: 1.5;
465
+ resize: vertical;
466
+ box-sizing: border-box;
467
+ }
468
+ .cd-replace-panel__btns {
469
+ display: flex;
470
+ gap: 6px;
471
+ margin-top: 6px;
472
+ }
473
+ .cd-replace-msg {
474
+ font-size: 12px;
475
+ margin-top: 4px;
476
+ padding: 4px 8px;
477
+ border-radius: 4px;
478
+ }
479
+ .cd-replace-msg--ok {
480
+ background: #2E7D32;
481
+ color: #FFF;
482
+ }
483
+ .cd-replace-msg--err {
484
+ background: #C62828;
485
+ color: #FFF;
486
+ }
487
+ </style>
488
+ </head>
489
+ <body>
490
+
491
+ <header class="cd-header">
492
+ <div class="cd-header__text">
493
+ <h1 class="cd-header__title">codedocent</h1>
494
+ <p class="cd-header__path" id="cd-root-path"></p>
495
+ </div>
496
+ <button class="cd-theme-toggle" id="cd-theme-toggle"
497
+ onclick="cdToggleTheme()" aria-label="Toggle theme"></button>
498
+ </header>
499
+
500
+ <nav class="cd-breadcrumb" id="cd-breadcrumb">
501
+ <span class="cd-breadcrumb__current" id="cd-bc-root"></span>
502
+ </nav>
503
+
504
+ <main class="cd-main" id="cd-main"></main>
505
+
506
+ <script>
507
+ function cdToggleTheme() {
508
+ var isLight = document.documentElement.classList.toggle('light-mode');
509
+ localStorage.setItem('cd-theme', isLight ? 'light' : 'dark');
510
+ document.getElementById('cd-theme-toggle').textContent = isLight ? '\u{1F319}' : '\u2600\uFE0F';
511
+ }
512
+ (function() {
513
+ var btn = document.getElementById('cd-theme-toggle');
514
+ if (btn) btn.textContent = document.documentElement.classList.contains('light-mode') ? '\u{1F319}' : '\u2600\uFE0F';
515
+ })();
516
+
517
+ const TREE_DATA = {{ tree_json | safe }};
518
+
519
+ // Track expanded path for breadcrumbs
520
+ let breadcrumbPath = [TREE_DATA];
521
+
522
+ function escapeHtml(s) {
523
+ if (!s) return '';
524
+ const d = document.createElement('div');
525
+ d.textContent = s;
526
+ return d.innerHTML;
527
+ }
528
+
529
+ function qualityEmoji(q) {
530
+ if (q === 'warning') return '\u{1F534}';
531
+ if (q === 'complex') return '\u{1F7E1}';
532
+ return '\u{1F7E2}';
533
+ }
534
+
535
+ var sourceCache = {};
536
+
537
+ function cdFetchSource(nodeId, callback) {
538
+ if (sourceCache[nodeId]) {
539
+ callback(sourceCache[nodeId]);
540
+ return;
541
+ }
542
+ fetch('/api/analyze/' + nodeId, {method: 'POST'})
543
+ .then(function(r) { return r.json(); })
544
+ .then(function(data) {
545
+ if (data.source) {
546
+ sourceCache[nodeId] = data.source;
547
+ }
548
+ callback(data.source || '');
549
+ })
550
+ .catch(function() { callback(''); });
551
+ }
552
+
553
+ function cdToggleSource(btn, pre) {
554
+ var isOpen = pre.classList.contains('cd-source-display--open');
555
+ if (isOpen) {
556
+ pre.classList.remove('cd-source-display--open');
557
+ btn.classList.remove('cd-code-btn--active');
558
+ btn.textContent = 'Show Code';
559
+ } else {
560
+ pre.classList.add('cd-source-display--open');
561
+ btn.classList.add('cd-code-btn--active');
562
+ btn.textContent = 'Hide Code';
563
+ }
564
+ }
565
+
566
+ function cdCopyToClipboard(text, btn) {
567
+ if (navigator.clipboard && navigator.clipboard.writeText) {
568
+ navigator.clipboard.writeText(text).then(function() {
569
+ cdShowCopied(btn);
570
+ }, function() {
571
+ cdFallbackCopy(text, btn);
572
+ });
573
+ } else {
574
+ cdFallbackCopy(text, btn);
575
+ }
576
+ }
577
+
578
+ function cdFallbackCopy(text, btn) {
579
+ var ta = document.createElement('textarea');
580
+ ta.value = text;
581
+ ta.style.position = 'fixed';
582
+ ta.style.left = '-9999px';
583
+ document.body.appendChild(ta);
584
+ ta.select();
585
+ try { document.execCommand('copy'); cdShowCopied(btn); }
586
+ catch(e) {}
587
+ document.body.removeChild(ta);
588
+ }
589
+
590
+ function cdShowCopied(btn) {
591
+ var orig = btn.textContent;
592
+ btn.textContent = 'Copied!';
593
+ btn.classList.add('cd-code-btn--copied');
594
+ setTimeout(function() {
595
+ btn.textContent = orig;
596
+ btn.classList.remove('cd-code-btn--copied');
597
+ }, 1500);
598
+ }
599
+
600
+ function cdCreateCodeActions(bodyEl, nodeId, nodeType, nodeName, filepath, language) {
601
+ if (bodyEl.querySelector('.cd-code-actions')) return;
602
+
603
+ var pre = document.createElement('pre');
604
+ pre.className = 'cd-source-display';
605
+
606
+ function ensureSource(callback) {
607
+ if (pre.dataset.loaded === 'true') {
608
+ callback(pre.textContent);
609
+ return;
610
+ }
611
+ cdFetchSource(nodeId, function(source) {
612
+ pre.textContent = source;
613
+ pre.dataset.loaded = 'true';
614
+ callback(source);
615
+ });
616
+ }
617
+
618
+ var actions = document.createElement('div');
619
+ actions.className = 'cd-code-actions';
620
+
621
+ var btnShow = document.createElement('button');
622
+ btnShow.className = 'cd-code-btn';
623
+ btnShow.textContent = 'Show Code';
624
+ btnShow.addEventListener('click', function(e) {
625
+ e.stopPropagation();
626
+ ensureSource(function() {
627
+ cdToggleSource(btnShow, pre);
628
+ });
629
+ });
630
+ actions.appendChild(btnShow);
631
+
632
+ var btnExport = document.createElement('button');
633
+ btnExport.className = 'cd-code-btn';
634
+ btnExport.textContent = 'Export Code';
635
+ btnExport.addEventListener('click', function(e) {
636
+ e.stopPropagation();
637
+ ensureSource(function(source) {
638
+ cdCopyToClipboard(source, btnExport);
639
+ });
640
+ });
641
+ actions.appendChild(btnExport);
642
+
643
+ var btnAI = document.createElement('button');
644
+ btnAI.className = 'cd-code-btn';
645
+ btnAI.textContent = 'Copy for AI';
646
+ btnAI.addEventListener('click', function(e) {
647
+ e.stopPropagation();
648
+ ensureSource(function(source) {
649
+ var lang = language || '';
650
+ var text = 'This is the ' + nodeType + ' `' + nodeName + '` from `' + (filepath || '') + '`:\n\n```' + lang + '\n' + source + '\n```';
651
+ cdCopyToClipboard(text, btnAI);
652
+ });
653
+ });
654
+ actions.appendChild(btnAI);
655
+
656
+ if (nodeType === 'function' || nodeType === 'method' || nodeType === 'class') {
657
+ var btnReplace = document.createElement('button');
658
+ btnReplace.className = 'cd-code-btn';
659
+ btnReplace.textContent = 'Replace Code';
660
+ btnReplace.addEventListener('click', function(e) {
661
+ e.stopPropagation();
662
+ ensureSource(function(source) {
663
+ cdShowReplacePanel(bodyEl, nodeId, source, pre, btnShow);
664
+ });
665
+ });
666
+ actions.appendChild(btnReplace);
667
+ }
668
+
669
+ var childrenEl = bodyEl.querySelector('.cd-node__children');
670
+ if (childrenEl) {
671
+ bodyEl.insertBefore(actions, childrenEl);
672
+ bodyEl.insertBefore(pre, childrenEl);
673
+ } else {
674
+ bodyEl.appendChild(actions);
675
+ bodyEl.appendChild(pre);
676
+ }
677
+ }
678
+
679
+ function cdShowReplacePanel(bodyEl, nodeId, currentSource, pre, btnShow) {
680
+ // Remove existing panel if any
681
+ var existing = bodyEl.querySelector('.cd-replace-panel');
682
+ if (existing) { existing.remove(); return; }
683
+
684
+ var panel = document.createElement('div');
685
+ panel.className = 'cd-replace-panel';
686
+
687
+ var ta = document.createElement('textarea');
688
+ ta.value = currentSource;
689
+ ta.addEventListener('click', function(e) { e.stopPropagation(); });
690
+ panel.appendChild(ta);
691
+
692
+ var btns = document.createElement('div');
693
+ btns.className = 'cd-replace-panel__btns';
694
+
695
+ var btnConfirm = document.createElement('button');
696
+ btnConfirm.className = 'cd-code-btn';
697
+ btnConfirm.textContent = 'Confirm Replace';
698
+ btnConfirm.addEventListener('click', function(e) {
699
+ e.stopPropagation();
700
+ btnConfirm.disabled = true;
701
+ btnConfirm.textContent = 'Replacing...';
702
+ fetch('/api/replace/' + nodeId, {
703
+ method: 'POST',
704
+ headers: {'Content-Type': 'application/json'},
705
+ body: JSON.stringify({source: ta.value})
706
+ })
707
+ .then(function(r) { return r.json(); })
708
+ .then(function(data) {
709
+ if (data.success) {
710
+ // Update source display and cache
711
+ pre.textContent = ta.value;
712
+ pre.dataset.loaded = 'true';
713
+ delete sourceCache[nodeId];
714
+ // Show success message
715
+ var msg = document.createElement('div');
716
+ msg.className = 'cd-replace-msg cd-replace-msg--ok';
717
+ msg.textContent = 'Replaced! (' + data.lines_before
718
+ + ' \u2192 ' + data.lines_after + ' lines)';
719
+ panel.appendChild(msg);
720
+ setTimeout(function() { panel.remove(); }, 2000);
721
+ } else {
722
+ var msg = document.createElement('div');
723
+ msg.className = 'cd-replace-msg cd-replace-msg--err';
724
+ msg.textContent = 'Error: ' + data.error;
725
+ panel.appendChild(msg);
726
+ btnConfirm.disabled = false;
727
+ btnConfirm.textContent = 'Confirm Replace';
728
+ }
729
+ })
730
+ .catch(function(err) {
731
+ var msg = document.createElement('div');
732
+ msg.className = 'cd-replace-msg cd-replace-msg--err';
733
+ msg.textContent = 'Network error';
734
+ panel.appendChild(msg);
735
+ btnConfirm.disabled = false;
736
+ btnConfirm.textContent = 'Confirm Replace';
737
+ });
738
+ });
739
+ btns.appendChild(btnConfirm);
740
+
741
+ var btnCancel = document.createElement('button');
742
+ btnCancel.className = 'cd-code-btn';
743
+ btnCancel.textContent = 'Cancel';
744
+ btnCancel.addEventListener('click', function(e) {
745
+ e.stopPropagation();
746
+ panel.remove();
747
+ });
748
+ btns.appendChild(btnCancel);
749
+
750
+ panel.appendChild(btns);
751
+
752
+ // Insert panel after the actions bar
753
+ var actionsEl = bodyEl.querySelector('.cd-code-actions');
754
+ if (actionsEl && actionsEl.nextSibling) {
755
+ bodyEl.insertBefore(panel, actionsEl.nextSibling);
756
+ } else {
757
+ bodyEl.appendChild(panel);
758
+ }
759
+ }
760
+
761
+ function renderNode(node, depth) {
762
+ const el = document.createElement('div');
763
+ el.className = 'cd-node';
764
+ el.style.setProperty('--node-color', node.color || '#CCCCCC');
765
+ el.dataset.nodeId = node.node_id || '';
766
+
767
+ const isRoot = depth === 0;
768
+ const hasChildren = node.children && node.children.length > 0;
769
+ const isDir = node.node_type === 'directory';
770
+
771
+ // Header
772
+ const header = document.createElement('div');
773
+ header.className = 'cd-node__header';
774
+
775
+ // Toggle indicator
776
+ const toggle = document.createElement('span');
777
+ toggle.className = 'cd-node__toggle';
778
+ if (hasChildren) {
779
+ toggle.textContent = isRoot ? 'v' : '>';
780
+ }
781
+ header.appendChild(toggle);
782
+
783
+ const icon = document.createElement('span');
784
+ icon.className = 'cd-node__icon';
785
+ icon.textContent = node.icon || '';
786
+ header.appendChild(icon);
787
+
788
+ const name = document.createElement('span');
789
+ name.className = 'cd-node__name';
790
+ name.textContent = node.name;
791
+ header.appendChild(name);
792
+
793
+ const badge = document.createElement('span');
794
+ badge.className = 'cd-node__badge';
795
+ badge.textContent = node.node_type;
796
+ header.appendChild(badge);
797
+
798
+ const lines = document.createElement('span');
799
+ lines.className = 'cd-node__lines';
800
+ lines.textContent = node.line_count + ' lines';
801
+ header.appendChild(lines);
802
+
803
+ if ((node.node_type === 'function' || node.node_type === 'method') && node.start_line && node.end_line) {
804
+ const range = document.createElement('span');
805
+ range.className = 'cd-node__range';
806
+ range.textContent = 'L' + node.start_line + '\u2013' + node.end_line;
807
+ header.appendChild(range);
808
+ }
809
+
810
+ const quality = document.createElement('span');
811
+ quality.className = 'cd-node__quality';
812
+ quality.textContent = qualityEmoji(node.quality);
813
+ header.appendChild(quality);
814
+
815
+ if (node.warnings && node.warnings.length > 0) {
816
+ const warningText = document.createElement('span');
817
+ warningText.className = 'cd-node__warning-text';
818
+ warningText.textContent = node.warnings.join('; ');
819
+ header.appendChild(warningText);
820
+ }
821
+
822
+ el.appendChild(header);
823
+
824
+ // Body
825
+ const body = document.createElement('div');
826
+ body.className = 'cd-node__body' + (isRoot ? ' cd-node__body--open' : '');
827
+
828
+ // Imports
829
+ if (node.imports && node.imports.length > 0) {
830
+ const imp = document.createElement('div');
831
+ imp.className = 'cd-imports';
832
+ imp.innerHTML = '<div class="cd-imports__label">IMPORTS</div>' +
833
+ node.imports.map(i => '<div class="cd-imports__item">\u2192 ' + escapeHtml(i) + '</div>').join('');
834
+ body.appendChild(imp);
835
+ }
836
+
837
+ // Summary / analyze link
838
+ const summaryEl = document.createElement('div');
839
+ if (node.summary) {
840
+ summaryEl.className = 'cd-summary';
841
+ summaryEl.textContent = node.summary;
842
+ body.appendChild(summaryEl);
843
+ } else if (!isDir && node.node_id) {
844
+ summaryEl.className = 'cd-analyze-link';
845
+ summaryEl.textContent = 'Click to analyze with AI...';
846
+ summaryEl.dataset.nodeId = node.node_id;
847
+ summaryEl.addEventListener('click', function(e) {
848
+ e.stopPropagation();
849
+ analyzeNode(node.node_id, summaryEl, body);
850
+ });
851
+ body.appendChild(summaryEl);
852
+ }
853
+
854
+ // Pseudocode
855
+ if (node.pseudocode && (node.node_type === 'function' || node.node_type === 'method')) {
856
+ const pc = document.createElement('div');
857
+ pc.className = 'cd-pseudocode';
858
+ pc.innerHTML = '<div class="cd-pseudocode__label">PSEUDOCODE</div>' +
859
+ '<pre class="cd-pseudocode__code">' + escapeHtml(node.pseudocode) + '</pre>';
860
+ body.appendChild(pc);
861
+ }
862
+
863
+ // Warnings
864
+ if (node.warnings && node.warnings.length > 0) {
865
+ const w = document.createElement('div');
866
+ w.className = 'cd-warnings';
867
+ w.innerHTML = node.warnings.map(
868
+ warn => '<div class="cd-warnings__item">\u26A0\uFE0F ' + escapeHtml(warn) + '</div>'
869
+ ).join('');
870
+ body.appendChild(w);
871
+ }
872
+
873
+ // Code export buttons (for pre-analyzed nodes with summaries at load time)
874
+ if (!isDir && node.node_id && node.summary) {
875
+ cdCreateCodeActions(body, node.node_id, node.node_type, node.name, node.filepath, node.language);
876
+ }
877
+
878
+ // Children
879
+ if (hasChildren) {
880
+ const childrenEl = document.createElement('div');
881
+ childrenEl.className = 'cd-node__children';
882
+ node.children.forEach(function(child) {
883
+ childrenEl.appendChild(renderNode(child, depth + 1));
884
+ });
885
+ body.appendChild(childrenEl);
886
+ }
887
+
888
+ el.appendChild(body);
889
+
890
+ // Click handler for expand/collapse
891
+ header.addEventListener('click', function() {
892
+ const isOpen = body.classList.contains('cd-node__body--open');
893
+ if (isOpen) {
894
+ body.classList.remove('cd-node__body--open');
895
+ toggle.textContent = hasChildren ? '>' : '';
896
+ } else {
897
+ body.classList.add('cd-node__body--open');
898
+ toggle.textContent = hasChildren ? 'v' : '';
899
+ updateBreadcrumb(node, el);
900
+ }
901
+ });
902
+
903
+ return el;
904
+ }
905
+
906
+ function updateBreadcrumb(node, el) {
907
+ // Build path from root to this node by walking up DOM
908
+ const path = [];
909
+ let current = el;
910
+ while (current) {
911
+ if (current.dataset && current.dataset.nodeId) {
912
+ const name = current.querySelector('.cd-node__name');
913
+ if (name) {
914
+ path.unshift({name: name.textContent, el: current});
915
+ }
916
+ }
917
+ current = current.parentElement ? current.parentElement.closest('.cd-node') : null;
918
+ }
919
+
920
+ const bc = document.getElementById('cd-breadcrumb');
921
+ bc.innerHTML = '';
922
+
923
+ path.forEach(function(item, i) {
924
+ if (i > 0) {
925
+ const sep = document.createElement('span');
926
+ sep.className = 'cd-breadcrumb__sep';
927
+ sep.textContent = '/';
928
+ bc.appendChild(sep);
929
+ }
930
+ if (i < path.length - 1) {
931
+ const link = document.createElement('span');
932
+ link.className = 'cd-breadcrumb__item';
933
+ link.textContent = item.name;
934
+ link.addEventListener('click', function() {
935
+ item.el.scrollIntoView({behavior: 'smooth', block: 'start'});
936
+ });
937
+ bc.appendChild(link);
938
+ } else {
939
+ const cur = document.createElement('span');
940
+ cur.className = 'cd-breadcrumb__current';
941
+ cur.textContent = item.name;
942
+ bc.appendChild(cur);
943
+ }
944
+ });
945
+ }
946
+
947
+ function analyzeNode(nodeId, linkEl, bodyEl) {
948
+ // Replace link with loading indicator
949
+ const loading = document.createElement('div');
950
+ loading.className = 'cd-analyzing';
951
+ loading.textContent = 'Analyzing...';
952
+ linkEl.replaceWith(loading);
953
+
954
+ fetch('/api/analyze/' + nodeId, {method: 'POST'})
955
+ .then(function(r) { return r.json(); })
956
+ .then(function(data) {
957
+ // Replace loading with summary
958
+ if (data.summary) {
959
+ const summary = document.createElement('div');
960
+ summary.className = 'cd-summary';
961
+ summary.textContent = data.summary;
962
+ loading.replaceWith(summary);
963
+
964
+ // Add pseudocode if present
965
+ if (data.pseudocode && (data.node_type === 'function' || data.node_type === 'method')) {
966
+ const pc = document.createElement('div');
967
+ pc.className = 'cd-pseudocode';
968
+ pc.innerHTML = '<div class="cd-pseudocode__label">PSEUDOCODE</div>' +
969
+ '<pre class="cd-pseudocode__code">' + escapeHtml(data.pseudocode) + '</pre>';
970
+ summary.after(pc);
971
+ }
972
+
973
+ // Add warnings if present
974
+ if (data.warnings && data.warnings.length > 0) {
975
+ const w = document.createElement('div');
976
+ w.className = 'cd-warnings';
977
+ w.innerHTML = data.warnings.map(
978
+ function(warn) { return '<div class="cd-warnings__item">\u26A0\uFE0F ' + escapeHtml(warn) + '</div>'; }
979
+ ).join('');
980
+ bodyEl.appendChild(w);
981
+ }
982
+
983
+ // Update quality indicator
984
+ if (data.quality) {
985
+ const nodeEl = bodyEl.closest('.cd-node');
986
+ const qEl = nodeEl.querySelector('.cd-node__quality');
987
+ if (qEl) qEl.textContent = qualityEmoji(data.quality);
988
+ }
989
+
990
+ // Update inline warning text
991
+ if (data.warnings && data.warnings.length > 0) {
992
+ const nodeEl = bodyEl.closest('.cd-node');
993
+ let wt = nodeEl.querySelector('.cd-node__warning-text');
994
+ if (!wt) {
995
+ wt = document.createElement('span');
996
+ wt.className = 'cd-node__warning-text';
997
+ nodeEl.querySelector('.cd-node__header').appendChild(wt);
998
+ }
999
+ wt.textContent = data.warnings.join('; ');
1000
+ }
1001
+
1002
+ // Add code export buttons
1003
+ if (data.source) {
1004
+ sourceCache[nodeId] = data.source;
1005
+ cdCreateCodeActions(bodyEl, nodeId, data.node_type, data.name, data.filepath, data.language);
1006
+ }
1007
+ } else {
1008
+ loading.textContent = 'No summary available';
1009
+ loading.className = 'cd-summary';
1010
+ }
1011
+ })
1012
+ .catch(function(err) {
1013
+ loading.textContent = 'Analysis failed: ' + err.message;
1014
+ loading.className = 'cd-summary';
1015
+ });
1016
+ }
1017
+
1018
+ // Initialize
1019
+ document.addEventListener('DOMContentLoaded', function() {
1020
+ document.getElementById('cd-root-path').textContent = TREE_DATA.filepath || TREE_DATA.name;
1021
+ document.getElementById('cd-bc-root').textContent = TREE_DATA.name;
1022
+ document.getElementById('cd-main').appendChild(renderNode(TREE_DATA, 0));
1023
+ });
1024
+
1025
+ // Shutdown on tab close
1026
+ window.addEventListener('beforeunload', function() {
1027
+ navigator.sendBeacon('/shutdown');
1028
+ });
1029
+ </script>
1030
+
1031
+ </body>
1032
+ </html>