klaude-code 1.2.2__py3-none-any.whl → 1.2.3__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 +7 -5
- klaude_code/command/clear_cmd.py +3 -24
- klaude_code/command/command_abc.py +36 -1
- klaude_code/command/export_cmd.py +14 -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/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/llm_param.py +12 -0
- klaude_code/protocol/model.py +23 -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 +180 -42
- 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 +6 -10
- 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.3.dist-info}/METADATA +1 -1
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.3.dist-info}/RECORD +58 -57
- /klaude_code/{core → protocol}/sub_agent.py +0 -0
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.3.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.2.dist-info → klaude_code-1.2.3.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
|
}
|
|
@@ -858,9 +941,7 @@
|
|
|
858
941
|
<div
|
|
859
942
|
class="details-content system-prompt-content"
|
|
860
943
|
style="font-family: var(--font-mono); white-space: pre-wrap"
|
|
861
|
-
>
|
|
862
|
-
$system_prompt
|
|
863
|
-
</div>
|
|
944
|
+
>$system_prompt</div>
|
|
864
945
|
</details>
|
|
865
946
|
|
|
866
947
|
<details class="collapsible-section">
|
|
@@ -871,17 +952,48 @@
|
|
|
871
952
|
<div class="message-stream">$messages_html</div>
|
|
872
953
|
|
|
873
954
|
<div class="footer">
|
|
874
|
-
Generated by
|
|
875
|
-
|
|
955
|
+
Generated by
|
|
956
|
+
<a
|
|
957
|
+
href="https://github.com/inspirepan/klaude-code"
|
|
958
|
+
class="footer-link"
|
|
959
|
+
target="_blank"
|
|
960
|
+
>klaude-code</a
|
|
961
|
+
>
|
|
962
|
+
• $footer_time • $total_messages messages
|
|
876
963
|
</div>
|
|
877
964
|
</div>
|
|
878
965
|
|
|
966
|
+
<div id="scroll-btn" class="scroll-btn" title="Scroll to bottom">
|
|
967
|
+
<svg
|
|
968
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
969
|
+
width="20"
|
|
970
|
+
height="20"
|
|
971
|
+
viewBox="0 0 24 24"
|
|
972
|
+
fill="none"
|
|
973
|
+
stroke="currentColor"
|
|
974
|
+
stroke-width="2"
|
|
975
|
+
stroke-linecap="round"
|
|
976
|
+
stroke-linejoin="round"
|
|
977
|
+
>
|
|
978
|
+
<line x1="12" y1="5" x2="12" y2="19"></line>
|
|
979
|
+
<polyline points="19 12 12 19 5 12"></polyline>
|
|
980
|
+
</svg>
|
|
981
|
+
</div>
|
|
982
|
+
|
|
879
983
|
<link
|
|
880
984
|
rel="stylesheet"
|
|
881
985
|
href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css"
|
|
882
986
|
/>
|
|
883
987
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
|
|
884
988
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
989
|
+
<script type="module">
|
|
990
|
+
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
991
|
+
mermaid.initialize({
|
|
992
|
+
startOnLoad: true,
|
|
993
|
+
theme: "neutral",
|
|
994
|
+
fontFamily: '"Iosevka", "SF Mono", Menlo, monospace',
|
|
995
|
+
});
|
|
996
|
+
</script>
|
|
885
997
|
<script>
|
|
886
998
|
// Markdown rendering and Syntax Highlighting
|
|
887
999
|
document.querySelectorAll(".markdown-content").forEach((el) => {
|
|
@@ -1077,6 +1189,32 @@
|
|
|
1077
1189
|
}
|
|
1078
1190
|
});
|
|
1079
1191
|
});
|
|
1192
|
+
|
|
1193
|
+
// Scroll to bottom button
|
|
1194
|
+
const scrollBtn = document.getElementById("scroll-btn");
|
|
1195
|
+
|
|
1196
|
+
function updateScrollBtn() {
|
|
1197
|
+
// Show button if we are not at the bottom
|
|
1198
|
+
const isAtBottom =
|
|
1199
|
+
window.innerHeight + window.scrollY >=
|
|
1200
|
+
document.body.offsetHeight - 100;
|
|
1201
|
+
if (isAtBottom) {
|
|
1202
|
+
scrollBtn.classList.remove("visible");
|
|
1203
|
+
} else {
|
|
1204
|
+
scrollBtn.classList.add("visible");
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
window.addEventListener("scroll", updateScrollBtn);
|
|
1209
|
+
window.addEventListener("resize", updateScrollBtn);
|
|
1210
|
+
updateScrollBtn(); // Initial check
|
|
1211
|
+
|
|
1212
|
+
scrollBtn.addEventListener("click", () => {
|
|
1213
|
+
window.scrollTo({
|
|
1214
|
+
top: document.body.scrollHeight,
|
|
1215
|
+
behavior: "smooth",
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1080
1218
|
</script>
|
|
1081
1219
|
</body>
|
|
1082
1220
|
</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
|
|
@@ -107,7 +107,7 @@ class SpinnerStatusState:
|
|
|
107
107
|
if self._base_status:
|
|
108
108
|
result = Text(self._base_status)
|
|
109
109
|
if activity_text:
|
|
110
|
-
result.append(" ")
|
|
110
|
+
result.append(" | ")
|
|
111
111
|
result.append_text(activity_text)
|
|
112
112
|
return result
|
|
113
113
|
if activity_text:
|
|
@@ -286,12 +286,14 @@ class DisplayEventHandler:
|
|
|
286
286
|
self.renderer.display_task_finish(event)
|
|
287
287
|
if not self.renderer.is_sub_agent_session(event.session_id):
|
|
288
288
|
emit_osc94(OSC94States.HIDDEN)
|
|
289
|
+
self.spinner_status.reset()
|
|
289
290
|
self.renderer.spinner_stop()
|
|
290
291
|
await self.stage_manager.transition_to(Stage.WAITING)
|
|
291
292
|
self._maybe_notify_task_finish(event)
|
|
292
293
|
|
|
293
294
|
async def _on_interrupt(self, event: events.InterruptEvent) -> None:
|
|
294
295
|
self.renderer.spinner_stop()
|
|
296
|
+
self.spinner_status.reset()
|
|
295
297
|
await self.stage_manager.transition_to(Stage.WAITING)
|
|
296
298
|
emit_osc94(OSC94States.HIDDEN)
|
|
297
299
|
self.renderer.display_interrupt()
|
|
@@ -302,11 +304,13 @@ class DisplayEventHandler:
|
|
|
302
304
|
self.renderer.display_error(event)
|
|
303
305
|
if not event.can_retry:
|
|
304
306
|
self.renderer.spinner_stop()
|
|
307
|
+
self.spinner_status.reset()
|
|
305
308
|
|
|
306
309
|
async def _on_end(self, event: events.EndEvent) -> None:
|
|
307
310
|
emit_osc94(OSC94States.HIDDEN)
|
|
308
311
|
await self.stage_manager.transition_to(Stage.WAITING)
|
|
309
312
|
self.renderer.spinner_stop()
|
|
313
|
+
self.spinner_status.reset()
|
|
310
314
|
|
|
311
315
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
312
316
|
# Private helper methods
|
|
@@ -16,7 +16,7 @@ def need_render_developer_message(e: events.DeveloperMessageEvent) -> bool:
|
|
|
16
16
|
or e.item.external_file_changes
|
|
17
17
|
or e.item.todo_use
|
|
18
18
|
or e.item.at_files
|
|
19
|
-
or e.item.
|
|
19
|
+
or e.item.user_image_count
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
|
|
@@ -74,16 +74,12 @@ def render_developer_message(e: events.DeveloperMessageEvent) -> RenderableType:
|
|
|
74
74
|
)
|
|
75
75
|
parts.append(grid)
|
|
76
76
|
|
|
77
|
-
if
|
|
77
|
+
if uic := e.item.user_image_count:
|
|
78
78
|
grid = create_grid()
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
("Read ", ThemeKey.REMINDER),
|
|
84
|
-
Text(f"{img_tag} Image", style=ThemeKey.REMINDER_BOLD),
|
|
85
|
-
),
|
|
86
|
-
)
|
|
79
|
+
grid.add_row(
|
|
80
|
+
Text(" +", style=ThemeKey.REMINDER),
|
|
81
|
+
Text(f"Attached {uic} image{'s' if uic > 1 else ''}", style=ThemeKey.REMINDER),
|
|
82
|
+
)
|
|
87
83
|
parts.append(grid)
|
|
88
84
|
|
|
89
85
|
return Group(*parts) if parts else Text("")
|
|
@@ -41,33 +41,32 @@ def render_response_metadata(e: events.ResponseMetadataEvent) -> RenderableType:
|
|
|
41
41
|
|
|
42
42
|
if metadata.usage is not None:
|
|
43
43
|
# Input
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
input_parts: list[tuple[str, str]] = [
|
|
45
|
+
("input:", ThemeKey.METADATA_DIM),
|
|
46
|
+
(format_number(metadata.usage.input_tokens), ThemeKey.METADATA_DIM),
|
|
47
|
+
]
|
|
48
|
+
if metadata.usage.input_cost is not None:
|
|
49
|
+
input_parts.append((f"(${metadata.usage.input_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
50
|
+
parts.append(Text.assemble(*input_parts))
|
|
50
51
|
|
|
51
52
|
# Cached
|
|
52
53
|
if metadata.usage.cached_tokens > 0:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
),
|
|
61
|
-
)
|
|
62
|
-
)
|
|
54
|
+
cached_parts: list[tuple[str, str]] = [
|
|
55
|
+
("cached:", ThemeKey.METADATA_DIM),
|
|
56
|
+
(format_number(metadata.usage.cached_tokens), ThemeKey.METADATA_DIM),
|
|
57
|
+
]
|
|
58
|
+
if metadata.usage.cache_read_cost is not None:
|
|
59
|
+
cached_parts.append((f"(${metadata.usage.cache_read_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
60
|
+
parts.append(Text.assemble(*cached_parts))
|
|
63
61
|
|
|
64
62
|
# Output
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
output_parts: list[tuple[str, str]] = [
|
|
64
|
+
("output:", ThemeKey.METADATA_DIM),
|
|
65
|
+
(format_number(metadata.usage.output_tokens), ThemeKey.METADATA_DIM),
|
|
66
|
+
]
|
|
67
|
+
if metadata.usage.output_cost is not None:
|
|
68
|
+
output_parts.append((f"(${metadata.usage.output_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
69
|
+
parts.append(Text.assemble(*output_parts))
|
|
71
70
|
|
|
72
71
|
# Reasoning
|
|
73
72
|
if metadata.usage.reasoning_tokens > 0:
|
|
@@ -105,16 +104,26 @@ def render_response_metadata(e: events.ResponseMetadataEvent) -> RenderableType:
|
|
|
105
104
|
)
|
|
106
105
|
)
|
|
107
106
|
|
|
108
|
-
#
|
|
107
|
+
# Duration
|
|
109
108
|
if metadata.task_duration_s is not None:
|
|
110
109
|
parts.append(
|
|
111
110
|
Text.assemble(
|
|
112
|
-
("
|
|
111
|
+
("time", ThemeKey.METADATA_DIM),
|
|
113
112
|
(":", ThemeKey.METADATA_DIM),
|
|
114
113
|
(f"{metadata.task_duration_s:.1f}s", ThemeKey.METADATA_DIM),
|
|
115
114
|
)
|
|
116
115
|
)
|
|
117
116
|
|
|
117
|
+
# Cost (USD)
|
|
118
|
+
if metadata.usage is not None and metadata.usage.total_cost is not None:
|
|
119
|
+
parts.append(
|
|
120
|
+
Text.assemble(
|
|
121
|
+
("cost", ThemeKey.METADATA_DIM),
|
|
122
|
+
(":", ThemeKey.METADATA_DIM),
|
|
123
|
+
(f"${metadata.usage.total_cost:.4f}", ThemeKey.METADATA_DIM),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
118
127
|
if parts:
|
|
119
128
|
line2 = Text("/", style=ThemeKey.METADATA_DIM).join(parts)
|
|
120
129
|
renderables.append(Padding(line2, (0, 0, 0, 2)))
|
|
@@ -6,8 +6,8 @@ from rich.style import Style
|
|
|
6
6
|
from rich.text import Text
|
|
7
7
|
|
|
8
8
|
from klaude_code import const
|
|
9
|
-
from klaude_code.core.sub_agent import get_sub_agent_profile_by_tool
|
|
10
9
|
from klaude_code.protocol import events, model
|
|
10
|
+
from klaude_code.protocol.sub_agent import get_sub_agent_profile_by_tool
|
|
11
11
|
from klaude_code.ui.rich.markdown import NoInsetMarkdown
|
|
12
12
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
13
13
|
|
|
@@ -6,8 +6,8 @@ from rich.padding import Padding
|
|
|
6
6
|
from rich.text import Text
|
|
7
7
|
|
|
8
8
|
from klaude_code import const
|
|
9
|
-
from klaude_code.core.sub_agent import is_sub_agent_tool as _is_sub_agent_tool
|
|
10
9
|
from klaude_code.protocol import events, model
|
|
10
|
+
from klaude_code.protocol.sub_agent import is_sub_agent_tool as _is_sub_agent_tool
|
|
11
11
|
from klaude_code.ui.renderers import diffs as r_diffs
|
|
12
12
|
from klaude_code.ui.renderers.common import create_grid
|
|
13
13
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
@@ -449,7 +449,7 @@ def get_tool_active_form(tool_name: str) -> str:
|
|
|
449
449
|
return _TOOL_ACTIVE_FORM[tool_name]
|
|
450
450
|
|
|
451
451
|
# Check sub agent profiles
|
|
452
|
-
from klaude_code.
|
|
452
|
+
from klaude_code.protocol.sub_agent import get_sub_agent_profile_by_tool
|
|
453
453
|
|
|
454
454
|
profile = get_sub_agent_profile_by_tool(tool_name)
|
|
455
455
|
if profile and profile.active_form:
|