klaude-code 1.2.2__py3-none-any.whl → 1.2.4__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.
- klaude_code/cli/main.py +7 -0
- klaude_code/cli/runtime.py +6 -6
- klaude_code/command/__init__.py +9 -5
- klaude_code/command/clear_cmd.py +3 -24
- klaude_code/command/command_abc.py +36 -1
- klaude_code/command/export_cmd.py +16 -20
- klaude_code/command/help_cmd.py +1 -0
- klaude_code/command/model_cmd.py +3 -30
- klaude_code/command/{prompt-update-dev-doc.md → prompt-dev-docs-update.md} +3 -2
- klaude_code/command/{prompt-dev-doc.md → prompt-dev-docs.md} +3 -2
- klaude_code/command/prompt-init.md +2 -5
- klaude_code/command/prompt_command.py +3 -3
- klaude_code/command/registry.py +6 -7
- klaude_code/command/status_cmd.py +111 -0
- klaude_code/config/config.py +1 -1
- klaude_code/config/list_model.py +1 -1
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/agent.py +2 -11
- klaude_code/core/executor.py +155 -14
- klaude_code/core/prompts/prompt-gemini.md +1 -1
- klaude_code/core/reminders.py +24 -0
- klaude_code/core/task.py +10 -0
- klaude_code/core/tool/shell/bash_tool.py +6 -2
- klaude_code/core/tool/sub_agent_tool.py +1 -1
- klaude_code/core/tool/tool_context.py +1 -1
- klaude_code/core/tool/tool_registry.py +1 -1
- klaude_code/core/tool/tool_runner.py +1 -1
- klaude_code/core/tool/web/mermaid_tool.py +1 -1
- klaude_code/llm/__init__.py +3 -4
- klaude_code/llm/anthropic/client.py +12 -9
- klaude_code/llm/openai_compatible/client.py +2 -18
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +2 -2
- klaude_code/llm/openrouter/client.py +2 -18
- klaude_code/llm/openrouter/input.py +6 -2
- klaude_code/llm/registry.py +2 -71
- klaude_code/llm/responses/client.py +2 -0
- klaude_code/llm/{metadata_tracker.py → usage.py} +49 -2
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/llm_param.py +12 -0
- klaude_code/protocol/model.py +30 -3
- klaude_code/protocol/op.py +14 -14
- klaude_code/protocol/op_handler.py +28 -0
- klaude_code/protocol/tools.py +0 -2
- klaude_code/session/export.py +124 -35
- klaude_code/session/session.py +1 -1
- klaude_code/session/templates/export_session.html +383 -39
- klaude_code/ui/__init__.py +6 -2
- klaude_code/ui/modes/exec/display.py +26 -0
- klaude_code/ui/modes/repl/event_handler.py +5 -1
- klaude_code/ui/renderers/developer.py +62 -11
- klaude_code/ui/renderers/metadata.py +33 -24
- klaude_code/ui/renderers/sub_agent.py +1 -1
- klaude_code/ui/renderers/tools.py +2 -2
- klaude_code/ui/renderers/user_input.py +18 -22
- klaude_code/ui/rich/status.py +13 -2
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.4.dist-info}/METADATA +1 -1
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.4.dist-info}/RECORD +60 -58
- /klaude_code/{core → protocol}/sub_agent.py +0 -0
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.4.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.4.dist-info}/entry_points.txt +0 -0
|
@@ -4,16 +4,29 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Klaude Code - $first_user_message</title>
|
|
7
|
-
<link
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
<link
|
|
8
|
+
rel="icon"
|
|
9
|
+
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%230851b2%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22><polyline points=%2216 18 22 12 16 6%22></polyline><polyline points=%228 6 2 12 8 18%22></polyline></svg>"
|
|
10
|
+
/>
|
|
11
|
+
<link
|
|
12
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/iosevka/400.css"
|
|
13
|
+
rel="stylesheet"
|
|
14
|
+
/>
|
|
15
|
+
<link
|
|
16
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/iosevka/500.css"
|
|
17
|
+
rel="stylesheet"
|
|
18
|
+
/>
|
|
19
|
+
<link
|
|
20
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/iosevka/800.css"
|
|
21
|
+
rel="stylesheet"
|
|
22
|
+
/>
|
|
10
23
|
<style>
|
|
11
24
|
:root {
|
|
12
25
|
--bg-body: #ededed;
|
|
13
26
|
--bg-container: #f0f0f0;
|
|
14
27
|
--bg-card: #f0f0f0;
|
|
15
28
|
--border: #c8c8c8;
|
|
16
|
-
--text: #
|
|
29
|
+
--text: #111111;
|
|
17
30
|
--text-dim: #64748b;
|
|
18
31
|
--accent: #0851b2;
|
|
19
32
|
--accent-dim: rgba(8, 145, 178, 0.08);
|
|
@@ -21,12 +34,14 @@
|
|
|
21
34
|
--error: #dc2626;
|
|
22
35
|
--bg-error: #ffebee;
|
|
23
36
|
--bg-code: #f3f3f3;
|
|
24
|
-
--font-sans: "
|
|
25
|
-
--font-mono: "
|
|
26
|
-
--font-
|
|
27
|
-
--font-
|
|
28
|
-
--font-size-
|
|
29
|
-
--font-size-
|
|
37
|
+
--font-sans: "Iosevka", "SF Mono", Menlo, monospace;
|
|
38
|
+
--font-mono: "Iosevka", "SF Mono", Menlo, monospace;
|
|
39
|
+
--font-markdown: "Iosevka", "SF Mono", Menlo, monospace;
|
|
40
|
+
--font-weight-bold: 800;
|
|
41
|
+
--font-size-xs: 13px;
|
|
42
|
+
--font-size-sm: 14px;
|
|
43
|
+
--font-size-base: 16px;
|
|
44
|
+
--font-size-lg: 18px;
|
|
30
45
|
--spacing-xs: 4px;
|
|
31
46
|
--spacing-sm: 8px;
|
|
32
47
|
--spacing-md: 12px;
|
|
@@ -66,7 +81,7 @@
|
|
|
66
81
|
|
|
67
82
|
.header h1 {
|
|
68
83
|
font-size: 20px;
|
|
69
|
-
font-weight:
|
|
84
|
+
font-weight: var(--font-weight-bold);
|
|
70
85
|
margin-bottom: 16px;
|
|
71
86
|
color: var(--text);
|
|
72
87
|
display: inline-block;
|
|
@@ -86,9 +101,8 @@
|
|
|
86
101
|
|
|
87
102
|
.meta-label {
|
|
88
103
|
font-size: var(--font-size-sm);
|
|
89
|
-
font-weight:
|
|
104
|
+
font-weight: var(--font-weight-bold);
|
|
90
105
|
text-transform: uppercase;
|
|
91
|
-
letter-spacing: 0.05em;
|
|
92
106
|
color: var(--text-dim);
|
|
93
107
|
}
|
|
94
108
|
|
|
@@ -116,8 +130,7 @@
|
|
|
116
130
|
font-family: var(--font-mono);
|
|
117
131
|
font-size: var(--font-size-sm);
|
|
118
132
|
text-transform: uppercase;
|
|
119
|
-
|
|
120
|
-
font-weight: 700;
|
|
133
|
+
font-weight: var(--font-weight-bold);
|
|
121
134
|
cursor: pointer;
|
|
122
135
|
user-select: none;
|
|
123
136
|
list-style: none;
|
|
@@ -184,14 +197,17 @@
|
|
|
184
197
|
|
|
185
198
|
.role-label {
|
|
186
199
|
font-size: var(--font-size-sm);
|
|
187
|
-
font-weight:
|
|
200
|
+
font-weight: var(--font-weight-bold);
|
|
188
201
|
text-transform: uppercase;
|
|
189
|
-
letter-spacing: 0.05em;
|
|
190
202
|
margin-bottom: 4px;
|
|
191
203
|
color: var(--text-dim);
|
|
192
204
|
display: flex;
|
|
193
205
|
align-items: center;
|
|
194
206
|
gap: 8px;
|
|
207
|
+
width: 100%;
|
|
208
|
+
}
|
|
209
|
+
.message-header .role-label {
|
|
210
|
+
width: auto;
|
|
195
211
|
}
|
|
196
212
|
.role-label.user {
|
|
197
213
|
color: var(--accent);
|
|
@@ -210,8 +226,7 @@
|
|
|
210
226
|
font-family: var(--font-mono);
|
|
211
227
|
font-size: var(--font-size-xs);
|
|
212
228
|
text-transform: uppercase;
|
|
213
|
-
|
|
214
|
-
font-weight: 700;
|
|
229
|
+
font-weight: var(--font-weight-bold);
|
|
215
230
|
cursor: pointer;
|
|
216
231
|
user-select: none;
|
|
217
232
|
list-style: none;
|
|
@@ -279,14 +294,14 @@
|
|
|
279
294
|
border: 1px solid var(--border);
|
|
280
295
|
background: transparent;
|
|
281
296
|
color: var(--text-dim);
|
|
297
|
+
font-family: var(--font-mono);
|
|
282
298
|
font-size: var(--font-size-xs);
|
|
283
299
|
text-transform: uppercase;
|
|
284
|
-
letter-spacing: 0.08em;
|
|
285
300
|
padding: 2px 10px;
|
|
286
301
|
border-radius: 999px;
|
|
287
302
|
cursor: pointer;
|
|
288
303
|
transition: color 0.2s, border-color 0.2s, background 0.2s;
|
|
289
|
-
font-weight:
|
|
304
|
+
font-weight: var(--font-weight-bold);
|
|
290
305
|
}
|
|
291
306
|
.raw-toggle:hover {
|
|
292
307
|
color: var(--text);
|
|
@@ -303,14 +318,14 @@
|
|
|
303
318
|
border: 1px solid var(--border);
|
|
304
319
|
background: transparent;
|
|
305
320
|
color: var(--text-dim);
|
|
321
|
+
font-family: var(--font-mono);
|
|
306
322
|
font-size: var(--font-size-xs);
|
|
307
323
|
text-transform: uppercase;
|
|
308
|
-
letter-spacing: 0.08em;
|
|
309
324
|
padding: 2px 10px;
|
|
310
325
|
border-radius: 999px;
|
|
311
326
|
cursor: pointer;
|
|
312
327
|
transition: color 0.2s, border-color 0.2s, background 0.2s;
|
|
313
|
-
font-weight:
|
|
328
|
+
font-weight: var(--font-weight-bold);
|
|
314
329
|
}
|
|
315
330
|
.copy-raw-btn:hover {
|
|
316
331
|
color: var(--text);
|
|
@@ -321,14 +336,14 @@
|
|
|
321
336
|
border: 1px solid var(--border);
|
|
322
337
|
background: transparent;
|
|
323
338
|
color: var(--text-dim);
|
|
339
|
+
font-family: var(--font-mono);
|
|
324
340
|
font-size: var(--font-size-xs);
|
|
325
341
|
text-transform: uppercase;
|
|
326
|
-
letter-spacing: 0.08em;
|
|
327
342
|
padding: 2px 10px;
|
|
328
343
|
border-radius: 999px;
|
|
329
344
|
cursor: pointer;
|
|
330
345
|
transition: color 0.2s, border-color 0.2s, background 0.2s;
|
|
331
|
-
font-weight:
|
|
346
|
+
font-weight: var(--font-weight-bold);
|
|
332
347
|
}
|
|
333
348
|
.copy-mermaid-btn:hover {
|
|
334
349
|
color: var(--text);
|
|
@@ -361,7 +376,7 @@
|
|
|
361
376
|
border-radius: 0;
|
|
362
377
|
padding: 0;
|
|
363
378
|
box-shadow: none;
|
|
364
|
-
font-weight:
|
|
379
|
+
font-weight: var(--font-weight-bold);
|
|
365
380
|
}
|
|
366
381
|
|
|
367
382
|
.thinking-block {
|
|
@@ -371,9 +386,15 @@
|
|
|
371
386
|
border-left: 2px solid var(--border);
|
|
372
387
|
color: var(--text-dim);
|
|
373
388
|
font-style: italic;
|
|
374
|
-
font-size: var(--font-size-
|
|
389
|
+
font-size: var(--font-size-sm);
|
|
375
390
|
background: var(--bg-body);
|
|
376
391
|
}
|
|
392
|
+
.thinking-block.markdown-body p {
|
|
393
|
+
margin-bottom: 8px;
|
|
394
|
+
}
|
|
395
|
+
.thinking-block.markdown-body p:last-child {
|
|
396
|
+
margin-bottom: 0;
|
|
397
|
+
}
|
|
377
398
|
|
|
378
399
|
/* Response Metadata */
|
|
379
400
|
.response-metadata {
|
|
@@ -392,7 +413,7 @@
|
|
|
392
413
|
line-height: 1.4;
|
|
393
414
|
}
|
|
394
415
|
.metadata-model {
|
|
395
|
-
font-weight:
|
|
416
|
+
font-weight: var(--font-weight-bold);
|
|
396
417
|
color: var(--text);
|
|
397
418
|
}
|
|
398
419
|
.metadata-provider {
|
|
@@ -427,7 +448,7 @@
|
|
|
427
448
|
}
|
|
428
449
|
|
|
429
450
|
.tool-name {
|
|
430
|
-
font-weight:
|
|
451
|
+
font-weight: var(--font-weight-bold);
|
|
431
452
|
color: var(--accent);
|
|
432
453
|
}
|
|
433
454
|
.tool-id {
|
|
@@ -435,6 +456,24 @@
|
|
|
435
456
|
font-size: var(--font-size-xs);
|
|
436
457
|
}
|
|
437
458
|
|
|
459
|
+
.tool-header-right {
|
|
460
|
+
display: flex;
|
|
461
|
+
align-items: center;
|
|
462
|
+
gap: 8px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.timestamp {
|
|
466
|
+
font-family: var(--font-mono);
|
|
467
|
+
font-size: var(--font-size-xs);
|
|
468
|
+
color: var(--text-dim);
|
|
469
|
+
margin-left: auto;
|
|
470
|
+
font-weight: normal;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.assistant-toolbar .timestamp {
|
|
474
|
+
margin-right: 8px;
|
|
475
|
+
}
|
|
476
|
+
|
|
438
477
|
.tool-args {
|
|
439
478
|
padding: 12px;
|
|
440
479
|
background: var(--bg-body);
|
|
@@ -455,8 +494,7 @@
|
|
|
455
494
|
font-family: var(--font-mono);
|
|
456
495
|
font-size: var(--font-size-xs);
|
|
457
496
|
text-transform: uppercase;
|
|
458
|
-
|
|
459
|
-
font-weight: 700;
|
|
497
|
+
font-weight: var(--font-weight-bold);
|
|
460
498
|
cursor: pointer;
|
|
461
499
|
user-select: none;
|
|
462
500
|
list-style: none;
|
|
@@ -557,7 +595,7 @@
|
|
|
557
595
|
|
|
558
596
|
/* Markdown Elements */
|
|
559
597
|
.markdown-body {
|
|
560
|
-
font-family: var(--font-
|
|
598
|
+
font-family: var(--font-markdown);
|
|
561
599
|
}
|
|
562
600
|
.markdown-body hr {
|
|
563
601
|
height: 0;
|
|
@@ -574,7 +612,7 @@
|
|
|
574
612
|
border: 1px solid var(--border);
|
|
575
613
|
}
|
|
576
614
|
.markdown-body code {
|
|
577
|
-
font-family: var(--font-
|
|
615
|
+
font-family: var(--font-markdown);
|
|
578
616
|
font-size: 0.9em;
|
|
579
617
|
background: var(--bg-code);
|
|
580
618
|
padding: 2px 4px;
|
|
@@ -648,8 +686,7 @@
|
|
|
648
686
|
font-family: var(--font-mono);
|
|
649
687
|
font-size: var(--font-size-xs);
|
|
650
688
|
text-transform: uppercase;
|
|
651
|
-
|
|
652
|
-
font-weight: 700;
|
|
689
|
+
font-weight: var(--font-weight-bold);
|
|
653
690
|
cursor: pointer;
|
|
654
691
|
user-select: none;
|
|
655
692
|
list-style: none;
|
|
@@ -689,6 +726,17 @@
|
|
|
689
726
|
padding-top: 24px;
|
|
690
727
|
}
|
|
691
728
|
|
|
729
|
+
.footer-link {
|
|
730
|
+
color: inherit;
|
|
731
|
+
text-decoration: none;
|
|
732
|
+
font-weight: 600;
|
|
733
|
+
transition: color 0.2s;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.footer-link:hover {
|
|
737
|
+
color: var(--accent);
|
|
738
|
+
}
|
|
739
|
+
|
|
692
740
|
.expandable {
|
|
693
741
|
cursor: pointer;
|
|
694
742
|
}
|
|
@@ -757,6 +805,42 @@
|
|
|
757
805
|
color: var(--text-dim);
|
|
758
806
|
}
|
|
759
807
|
|
|
808
|
+
/* Scroll to Bottom Button */
|
|
809
|
+
.scroll-btn {
|
|
810
|
+
position: fixed;
|
|
811
|
+
bottom: 24px;
|
|
812
|
+
right: 24px;
|
|
813
|
+
width: 40px;
|
|
814
|
+
height: 40px;
|
|
815
|
+
border-radius: 50%;
|
|
816
|
+
background: var(--bg-card);
|
|
817
|
+
border: 1px solid var(--border);
|
|
818
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
819
|
+
color: var(--text-dim);
|
|
820
|
+
display: flex;
|
|
821
|
+
align-items: center;
|
|
822
|
+
justify-content: center;
|
|
823
|
+
cursor: pointer;
|
|
824
|
+
opacity: 0;
|
|
825
|
+
visibility: hidden;
|
|
826
|
+
transform: translateY(10px);
|
|
827
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
828
|
+
z-index: 100;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.scroll-btn:hover {
|
|
832
|
+
color: var(--accent);
|
|
833
|
+
border-color: var(--accent);
|
|
834
|
+
transform: translateY(-2px);
|
|
835
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
.scroll-btn.visible {
|
|
839
|
+
opacity: 1;
|
|
840
|
+
visibility: visible;
|
|
841
|
+
transform: translateY(0);
|
|
842
|
+
}
|
|
843
|
+
|
|
760
844
|
/* Tool details in Available Tools section */
|
|
761
845
|
.tool-details {
|
|
762
846
|
margin-bottom: 2px;
|
|
@@ -798,9 +882,8 @@
|
|
|
798
882
|
}
|
|
799
883
|
.tool-params-title {
|
|
800
884
|
font-size: var(--font-size-xs);
|
|
801
|
-
font-weight:
|
|
885
|
+
font-weight: var(--font-weight-bold);
|
|
802
886
|
text-transform: uppercase;
|
|
803
|
-
letter-spacing: 0.05em;
|
|
804
887
|
color: var(--text-dim);
|
|
805
888
|
margin-bottom: 8px;
|
|
806
889
|
}
|
|
@@ -825,9 +908,81 @@
|
|
|
825
908
|
font-size: var(--font-size-sm);
|
|
826
909
|
margin-top: 2px;
|
|
827
910
|
}
|
|
911
|
+
/* TOC Sidebar */
|
|
912
|
+
.toc-sidebar {
|
|
913
|
+
position: fixed;
|
|
914
|
+
top: 33vh;
|
|
915
|
+
left: 20px;
|
|
916
|
+
width: 220px;
|
|
917
|
+
bottom: 33vh;
|
|
918
|
+
overflow-y: auto;
|
|
919
|
+
padding-right: 12px;
|
|
920
|
+
/* Vertical padding to offset mask */
|
|
921
|
+
padding-top: 30px;
|
|
922
|
+
padding-bottom: 30px;
|
|
923
|
+
display: none;
|
|
924
|
+
scrollbar-width: none;
|
|
925
|
+
z-index: 100;
|
|
926
|
+
/* Linear mask for fading edges */
|
|
927
|
+
-webkit-mask-image: linear-gradient(
|
|
928
|
+
to bottom,
|
|
929
|
+
transparent 0%,
|
|
930
|
+
black 30px,
|
|
931
|
+
black calc(100% - 30px),
|
|
932
|
+
transparent 100%
|
|
933
|
+
);
|
|
934
|
+
mask-image: linear-gradient(
|
|
935
|
+
to bottom,
|
|
936
|
+
transparent 0%,
|
|
937
|
+
black 30px,
|
|
938
|
+
black calc(100% - 30px),
|
|
939
|
+
transparent 100%
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.toc-sidebar::-webkit-scrollbar {
|
|
944
|
+
display: none;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/* Show TOC on wide screens */
|
|
948
|
+
@media (min-width: 1400px) {
|
|
949
|
+
.toc-sidebar {
|
|
950
|
+
display: block;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.toc-item {
|
|
955
|
+
display: block;
|
|
956
|
+
padding: 3px 10px;
|
|
957
|
+
margin-bottom: 1px;
|
|
958
|
+
font-family: var(--font-mono);
|
|
959
|
+
font-size: 12px;
|
|
960
|
+
line-height: 1.3;
|
|
961
|
+
color: var(--text-dim);
|
|
962
|
+
text-decoration: none;
|
|
963
|
+
cursor: pointer;
|
|
964
|
+
transition: all 0.2s;
|
|
965
|
+
white-space: nowrap;
|
|
966
|
+
overflow: hidden;
|
|
967
|
+
text-overflow: ellipsis;
|
|
968
|
+
border-radius: 4px;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.toc-item:hover {
|
|
972
|
+
color: var(--text);
|
|
973
|
+
background: rgba(0, 0, 0, 0.03);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.toc-item.active {
|
|
977
|
+
color: var(--text);
|
|
978
|
+
background: var(--bg-card);
|
|
979
|
+
font-weight: bold;
|
|
980
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
981
|
+
}
|
|
828
982
|
</style>
|
|
829
983
|
</head>
|
|
830
984
|
<body>
|
|
985
|
+
<div id="toc-sidebar" class="toc-sidebar"></div>
|
|
831
986
|
<div class="container">
|
|
832
987
|
<div class="header">
|
|
833
988
|
<h1>Klaude Code</h1>
|
|
@@ -871,17 +1026,48 @@
|
|
|
871
1026
|
<div class="message-stream">$messages_html</div>
|
|
872
1027
|
|
|
873
1028
|
<div class="footer">
|
|
874
|
-
Generated by
|
|
875
|
-
|
|
1029
|
+
Generated by
|
|
1030
|
+
<a
|
|
1031
|
+
href="https://github.com/inspirepan/klaude-code"
|
|
1032
|
+
class="footer-link"
|
|
1033
|
+
target="_blank"
|
|
1034
|
+
>klaude-code</a
|
|
1035
|
+
>
|
|
1036
|
+
• $footer_time • $total_messages messages
|
|
876
1037
|
</div>
|
|
877
1038
|
</div>
|
|
878
1039
|
|
|
1040
|
+
<div id="scroll-btn" class="scroll-btn" title="Scroll to bottom">
|
|
1041
|
+
<svg
|
|
1042
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1043
|
+
width="20"
|
|
1044
|
+
height="20"
|
|
1045
|
+
viewBox="0 0 24 24"
|
|
1046
|
+
fill="none"
|
|
1047
|
+
stroke="currentColor"
|
|
1048
|
+
stroke-width="2"
|
|
1049
|
+
stroke-linecap="round"
|
|
1050
|
+
stroke-linejoin="round"
|
|
1051
|
+
>
|
|
1052
|
+
<line x1="12" y1="5" x2="12" y2="19"></line>
|
|
1053
|
+
<polyline points="19 12 12 19 5 12"></polyline>
|
|
1054
|
+
</svg>
|
|
1055
|
+
</div>
|
|
1056
|
+
|
|
879
1057
|
<link
|
|
880
1058
|
rel="stylesheet"
|
|
881
1059
|
href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css"
|
|
882
1060
|
/>
|
|
883
1061
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
|
|
884
1062
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
1063
|
+
<script type="module">
|
|
1064
|
+
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
1065
|
+
mermaid.initialize({
|
|
1066
|
+
startOnLoad: true,
|
|
1067
|
+
theme: "neutral",
|
|
1068
|
+
fontFamily: '"Iosevka", "SF Mono", Menlo, monospace',
|
|
1069
|
+
});
|
|
1070
|
+
</script>
|
|
885
1071
|
<script>
|
|
886
1072
|
// Markdown rendering and Syntax Highlighting
|
|
887
1073
|
document.querySelectorAll(".markdown-content").forEach((el) => {
|
|
@@ -1077,6 +1263,164 @@
|
|
|
1077
1263
|
}
|
|
1078
1264
|
});
|
|
1079
1265
|
});
|
|
1266
|
+
|
|
1267
|
+
// Scroll to bottom button
|
|
1268
|
+
const scrollBtn = document.getElementById("scroll-btn");
|
|
1269
|
+
|
|
1270
|
+
function updateScrollBtn() {
|
|
1271
|
+
// Show button if we are not at the bottom
|
|
1272
|
+
const isAtBottom =
|
|
1273
|
+
window.innerHeight + window.scrollY >=
|
|
1274
|
+
document.body.offsetHeight - 100;
|
|
1275
|
+
if (isAtBottom) {
|
|
1276
|
+
scrollBtn.classList.remove("visible");
|
|
1277
|
+
} else {
|
|
1278
|
+
scrollBtn.classList.add("visible");
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
window.addEventListener("scroll", updateScrollBtn);
|
|
1283
|
+
window.addEventListener("resize", updateScrollBtn);
|
|
1284
|
+
updateScrollBtn(); // Initial check
|
|
1285
|
+
|
|
1286
|
+
scrollBtn.addEventListener("click", () => {
|
|
1287
|
+
window.scrollTo({
|
|
1288
|
+
top: document.body.scrollHeight,
|
|
1289
|
+
behavior: "smooth",
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
// TOC Logic
|
|
1294
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
1295
|
+
const tocSidebar = document.getElementById("toc-sidebar");
|
|
1296
|
+
if (!tocSidebar) return;
|
|
1297
|
+
|
|
1298
|
+
const sections = [];
|
|
1299
|
+
let userCount = 0;
|
|
1300
|
+
let assistantCount = 0;
|
|
1301
|
+
|
|
1302
|
+
// 1. System Prompt
|
|
1303
|
+
const sysPrompt = document.querySelector(
|
|
1304
|
+
".collapsible-section summary"
|
|
1305
|
+
);
|
|
1306
|
+
if (sysPrompt && sysPrompt.textContent.includes("System Prompt")) {
|
|
1307
|
+
const details = sysPrompt.parentElement;
|
|
1308
|
+
details.id = details.id || "section-system-prompt";
|
|
1309
|
+
sections.push({
|
|
1310
|
+
id: details.id,
|
|
1311
|
+
label: "System Prompt",
|
|
1312
|
+
el: details,
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// 2. Messages and Tools
|
|
1317
|
+
const stream = document.querySelector(".message-stream");
|
|
1318
|
+
if (stream) {
|
|
1319
|
+
// Use a Walker to find top-level relevant items if structure is complex
|
|
1320
|
+
// But assuming flat children of message-stream based on CSS
|
|
1321
|
+
Array.from(stream.children).forEach((child) => {
|
|
1322
|
+
let label = null;
|
|
1323
|
+
let idPrefix = "section";
|
|
1324
|
+
|
|
1325
|
+
if (child.classList.contains("message-group")) {
|
|
1326
|
+
const roleLabel = child.querySelector(".role-label");
|
|
1327
|
+
if (roleLabel) {
|
|
1328
|
+
if (roleLabel.classList.contains("user")) {
|
|
1329
|
+
userCount++;
|
|
1330
|
+
label = `USER $${userCount}`;
|
|
1331
|
+
idPrefix = "user";
|
|
1332
|
+
} else if (roleLabel.classList.contains("assistant")) {
|
|
1333
|
+
assistantCount++;
|
|
1334
|
+
label = `ASSISTANT $${assistantCount}`;
|
|
1335
|
+
idPrefix = "assistant";
|
|
1336
|
+
} else {
|
|
1337
|
+
label = roleLabel.textContent.trim();
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
} else if (child.classList.contains("tool-call")) {
|
|
1341
|
+
const toolName = child.querySelector(".tool-name");
|
|
1342
|
+
if (toolName) {
|
|
1343
|
+
label = toolName.textContent.trim();
|
|
1344
|
+
idPrefix = "tool";
|
|
1345
|
+
} else {
|
|
1346
|
+
label = "Tool";
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
if (label) {
|
|
1351
|
+
child.id =
|
|
1352
|
+
child.id ||
|
|
1353
|
+
`$${idPrefix}-$${Math.random().toString(36).substr(2, 9)}`;
|
|
1354
|
+
sections.push({ id: child.id, label: label, el: child });
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Render TOC
|
|
1360
|
+
sections.forEach((section) => {
|
|
1361
|
+
const item = document.createElement("div");
|
|
1362
|
+
item.className = "toc-item";
|
|
1363
|
+
item.textContent = section.label;
|
|
1364
|
+
item.dataset.target = section.id;
|
|
1365
|
+
item.addEventListener("click", () => {
|
|
1366
|
+
section.el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
1367
|
+
});
|
|
1368
|
+
tocSidebar.appendChild(item);
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
// Scroll Spy with throttling
|
|
1372
|
+
let ticking = false;
|
|
1373
|
+
const offset = 150; // Pixel offset for "active" area
|
|
1374
|
+
|
|
1375
|
+
function updateActiveSection() {
|
|
1376
|
+
let currentId = sections.length > 0 ? sections[0].id : null;
|
|
1377
|
+
const scrollY = window.scrollY;
|
|
1378
|
+
|
|
1379
|
+
// We look for the last section that has passed the top threshold (+ offset)
|
|
1380
|
+
for (let i = 0; i < sections.length; i++) {
|
|
1381
|
+
const el = sections[i].el;
|
|
1382
|
+
// If element top is above the "active line" (scrollY + offset)
|
|
1383
|
+
if (el.offsetTop <= scrollY + offset) {
|
|
1384
|
+
currentId = sections[i].id;
|
|
1385
|
+
} else {
|
|
1386
|
+
// Since sections are ordered by position, we can stop once we find one below the line
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// Update UI
|
|
1392
|
+
document.querySelectorAll(".toc-item").forEach((item) => {
|
|
1393
|
+
const isActive = item.dataset.target === currentId;
|
|
1394
|
+
if (item.classList.contains("active") !== isActive) {
|
|
1395
|
+
item.classList.toggle("active", isActive);
|
|
1396
|
+
if (isActive) {
|
|
1397
|
+
// Auto-scroll sidebar to visible
|
|
1398
|
+
const sidebarRect = tocSidebar.getBoundingClientRect();
|
|
1399
|
+
const itemRect = item.getBoundingClientRect();
|
|
1400
|
+
// Check if item is out of view in sidebar (accounting for mask)
|
|
1401
|
+
if (
|
|
1402
|
+
itemRect.top < sidebarRect.top + 40 ||
|
|
1403
|
+
itemRect.bottom > sidebarRect.bottom - 40
|
|
1404
|
+
) {
|
|
1405
|
+
item.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
ticking = false;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
window.addEventListener("scroll", () => {
|
|
1415
|
+
if (!ticking) {
|
|
1416
|
+
window.requestAnimationFrame(updateActiveSection);
|
|
1417
|
+
ticking = true;
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
// Initial update
|
|
1422
|
+
updateActiveSection();
|
|
1423
|
+
});
|
|
1080
1424
|
</script>
|
|
1081
1425
|
</body>
|
|
1082
1426
|
</html>
|
klaude_code/ui/__init__.py
CHANGED
|
@@ -21,7 +21,7 @@ Factory Functions:
|
|
|
21
21
|
from .core.display import DisplayABC
|
|
22
22
|
from .core.input import InputProviderABC
|
|
23
23
|
from .modes.debug.display import DebugEventDisplay
|
|
24
|
-
from .modes.exec.display import ExecDisplay
|
|
24
|
+
from .modes.exec.display import ExecDisplay, StreamJsonDisplay
|
|
25
25
|
|
|
26
26
|
# --- Display Mode Implementations ---
|
|
27
27
|
from .modes.repl.display import REPLDisplay
|
|
@@ -53,16 +53,19 @@ def create_default_display(
|
|
|
53
53
|
return repl_display
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def create_exec_display(debug: bool = False) -> DisplayABC:
|
|
56
|
+
def create_exec_display(debug: bool = False, stream_json: bool = False) -> DisplayABC:
|
|
57
57
|
"""
|
|
58
58
|
Create a display for exec (non-interactive) mode.
|
|
59
59
|
|
|
60
60
|
Args:
|
|
61
61
|
debug: If True, wrap the display with DebugEventDisplay to log all events.
|
|
62
|
+
stream_json: If True, stream all events as JSON lines instead of normal output.
|
|
62
63
|
|
|
63
64
|
Returns:
|
|
64
65
|
A DisplayABC implementation that only outputs task results.
|
|
65
66
|
"""
|
|
67
|
+
if stream_json:
|
|
68
|
+
return StreamJsonDisplay()
|
|
66
69
|
exec_display = ExecDisplay()
|
|
67
70
|
if debug:
|
|
68
71
|
return DebugEventDisplay(exec_display)
|
|
@@ -76,6 +79,7 @@ __all__ = [
|
|
|
76
79
|
# Display mode implementations
|
|
77
80
|
"REPLDisplay",
|
|
78
81
|
"ExecDisplay",
|
|
82
|
+
"StreamJsonDisplay",
|
|
79
83
|
"DebugEventDisplay",
|
|
80
84
|
# Input implementations
|
|
81
85
|
"PromptToolkitInput",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from typing import override
|
|
2
3
|
|
|
3
4
|
from klaude_code.protocol import events
|
|
@@ -35,3 +36,28 @@ class ExecDisplay(DisplayABC):
|
|
|
35
36
|
async def stop(self) -> None:
|
|
36
37
|
"""Do nothing on stop."""
|
|
37
38
|
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class StreamJsonDisplay(DisplayABC):
|
|
42
|
+
"""A display implementation that streams all events as JSON lines."""
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
async def consume_event(self, event: events.Event) -> None:
|
|
46
|
+
"""Stream each event as a JSON line."""
|
|
47
|
+
if isinstance(event, events.EndEvent):
|
|
48
|
+
return
|
|
49
|
+
event_type = type(event).__name__
|
|
50
|
+
json_data = event.model_dump_json()
|
|
51
|
+
# Output format: {"type": "EventName", "data": {...}}
|
|
52
|
+
print(f'{{"type": "{event_type}", "data": {json_data}}}', flush=True)
|
|
53
|
+
sys.stdout.flush()
|
|
54
|
+
|
|
55
|
+
@override
|
|
56
|
+
async def start(self) -> None:
|
|
57
|
+
"""Do nothing on start."""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@override
|
|
61
|
+
async def stop(self) -> None:
|
|
62
|
+
"""Do nothing on stop."""
|
|
63
|
+
pass
|