klaude-code 1.2.1__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 +9 -4
- klaude_code/cli/runtime.py +42 -43
- klaude_code/command/__init__.py +7 -5
- klaude_code/command/clear_cmd.py +6 -29
- klaude_code/command/command_abc.py +44 -8
- klaude_code/command/diff_cmd.py +33 -27
- klaude_code/command/export_cmd.py +18 -26
- klaude_code/command/help_cmd.py +10 -8
- klaude_code/command/model_cmd.py +11 -40
- 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 +6 -6
- klaude_code/command/refresh_cmd.py +4 -5
- klaude_code/command/registry.py +16 -19
- klaude_code/command/terminal_setup_cmd.py +12 -11
- klaude_code/config/__init__.py +4 -0
- klaude_code/config/config.py +25 -26
- klaude_code/config/list_model.py +8 -3
- klaude_code/config/select_model.py +1 -1
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/__init__.py +0 -3
- klaude_code/core/agent.py +25 -50
- klaude_code/core/executor.py +268 -101
- klaude_code/core/prompt.py +12 -12
- klaude_code/core/{prompt → prompts}/prompt-gemini.md +1 -1
- klaude_code/core/reminders.py +76 -95
- klaude_code/core/task.py +21 -14
- klaude_code/core/tool/__init__.py +45 -11
- klaude_code/core/tool/file/apply_patch.py +5 -1
- klaude_code/core/tool/file/apply_patch_tool.py +11 -13
- klaude_code/core/tool/file/edit_tool.py +27 -23
- klaude_code/core/tool/file/multi_edit_tool.py +15 -17
- klaude_code/core/tool/file/read_tool.py +41 -36
- klaude_code/core/tool/file/write_tool.py +13 -15
- klaude_code/core/tool/memory/memory_tool.py +85 -68
- klaude_code/core/tool/memory/skill_tool.py +10 -12
- klaude_code/core/tool/shell/bash_tool.py +24 -22
- klaude_code/core/tool/shell/command_safety.py +12 -1
- klaude_code/core/tool/sub_agent_tool.py +11 -12
- klaude_code/core/tool/todo/todo_write_tool.py +21 -28
- klaude_code/core/tool/todo/update_plan_tool.py +14 -24
- klaude_code/core/tool/tool_abc.py +3 -4
- klaude_code/core/tool/tool_context.py +7 -7
- klaude_code/core/tool/tool_registry.py +30 -47
- klaude_code/core/tool/tool_runner.py +35 -43
- klaude_code/core/tool/truncation.py +14 -20
- klaude_code/core/tool/web/mermaid_tool.py +12 -14
- klaude_code/core/tool/web/web_fetch_tool.py +15 -17
- klaude_code/core/turn.py +19 -7
- klaude_code/llm/__init__.py +3 -4
- klaude_code/llm/anthropic/client.py +30 -46
- klaude_code/llm/anthropic/input.py +4 -11
- klaude_code/llm/client.py +29 -8
- klaude_code/llm/input_common.py +66 -36
- klaude_code/llm/openai_compatible/client.py +42 -84
- klaude_code/llm/openai_compatible/input.py +11 -16
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +2 -2
- klaude_code/llm/openrouter/client.py +40 -289
- klaude_code/llm/openrouter/input.py +13 -35
- klaude_code/llm/openrouter/reasoning_handler.py +209 -0
- klaude_code/llm/registry.py +5 -75
- klaude_code/llm/responses/client.py +34 -55
- klaude_code/llm/responses/input.py +24 -26
- klaude_code/llm/usage.py +109 -0
- klaude_code/protocol/__init__.py +4 -0
- klaude_code/protocol/events.py +3 -2
- klaude_code/protocol/{llm_parameter.py → llm_param.py} +12 -32
- klaude_code/protocol/model.py +49 -4
- klaude_code/protocol/op.py +18 -16
- klaude_code/protocol/op_handler.py +28 -0
- klaude_code/{core → protocol}/sub_agent.py +7 -0
- klaude_code/session/export.py +150 -70
- klaude_code/session/session.py +28 -14
- klaude_code/session/templates/export_session.html +180 -42
- klaude_code/trace/__init__.py +2 -2
- klaude_code/trace/log.py +11 -5
- klaude_code/ui/__init__.py +91 -8
- klaude_code/ui/core/__init__.py +1 -0
- klaude_code/ui/core/display.py +103 -0
- klaude_code/ui/core/input.py +71 -0
- klaude_code/ui/modes/__init__.py +1 -0
- klaude_code/ui/modes/debug/__init__.py +1 -0
- klaude_code/ui/{base/debug_event_display.py → modes/debug/display.py} +9 -5
- klaude_code/ui/modes/exec/__init__.py +1 -0
- klaude_code/ui/{base/exec_display.py → modes/exec/display.py} +28 -2
- klaude_code/ui/{repl → modes/repl}/__init__.py +5 -6
- klaude_code/ui/modes/repl/clipboard.py +152 -0
- klaude_code/ui/modes/repl/completers.py +429 -0
- klaude_code/ui/modes/repl/display.py +60 -0
- klaude_code/ui/modes/repl/event_handler.py +375 -0
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +198 -0
- klaude_code/ui/modes/repl/key_bindings.py +170 -0
- klaude_code/ui/{repl → modes/repl}/renderer.py +109 -132
- klaude_code/ui/renderers/assistant.py +21 -0
- klaude_code/ui/renderers/common.py +0 -16
- klaude_code/ui/renderers/developer.py +18 -18
- klaude_code/ui/renderers/diffs.py +36 -14
- klaude_code/ui/renderers/errors.py +1 -1
- klaude_code/ui/renderers/metadata.py +50 -27
- klaude_code/ui/renderers/sub_agent.py +43 -9
- klaude_code/ui/renderers/thinking.py +33 -1
- klaude_code/ui/renderers/tools.py +212 -20
- klaude_code/ui/renderers/user_input.py +19 -23
- klaude_code/ui/rich/__init__.py +1 -0
- klaude_code/ui/{rich_ext → rich}/searchable_text.py +3 -1
- klaude_code/ui/{renderers → rich}/status.py +29 -18
- klaude_code/ui/{base → rich}/theme.py +8 -2
- klaude_code/ui/terminal/__init__.py +1 -0
- klaude_code/ui/{base/terminal_color.py → terminal/color.py} +4 -1
- klaude_code/ui/{base/terminal_control.py → terminal/control.py} +1 -0
- klaude_code/ui/{base/terminal_notifier.py → terminal/notifier.py} +5 -2
- klaude_code/ui/utils/__init__.py +1 -0
- klaude_code/ui/{base/utils.py → utils/common.py} +35 -3
- {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/METADATA +1 -1
- klaude_code-1.2.3.dist-info/RECORD +161 -0
- klaude_code/core/clipboard_manifest.py +0 -124
- klaude_code/llm/openrouter/tool_call_accumulator.py +0 -80
- klaude_code/ui/base/__init__.py +0 -1
- klaude_code/ui/base/display_abc.py +0 -36
- klaude_code/ui/base/input_abc.py +0 -20
- klaude_code/ui/repl/display.py +0 -36
- klaude_code/ui/repl/event_handler.py +0 -247
- klaude_code/ui/repl/input.py +0 -773
- klaude_code/ui/rich_ext/__init__.py +0 -1
- klaude_code-1.2.1.dist-info/RECORD +0 -151
- /klaude_code/core/{prompt → prompts}/prompt-claude-code.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-codex.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent-explore.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent-oracle.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent-webfetch.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent.md +0 -0
- /klaude_code/ui/{base → core}/stage_manager.py +0 -0
- /klaude_code/ui/{rich_ext → rich}/live.py +0 -0
- /klaude_code/ui/{rich_ext → rich}/markdown.py +0 -0
- /klaude_code/ui/{rich_ext → rich}/quote.py +0 -0
- /klaude_code/ui/{base → terminal}/progress_bar.py +0 -0
- /klaude_code/ui/{base → utils}/debouncer.py +0 -0
- {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.1.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/trace/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from .log import DebugType, log, log_debug, logger, set_debug_logging
|
|
1
|
+
from .log import DebugType, is_debug_enabled, log, log_debug, logger, set_debug_logging
|
|
2
2
|
|
|
3
|
-
__all__ = ["log", "log_debug", "logger", "set_debug_logging", "DebugType"]
|
|
3
|
+
__all__ = ["log", "log_debug", "logger", "set_debug_logging", "DebugType", "is_debug_enabled"]
|
klaude_code/trace/log.py
CHANGED
|
@@ -7,7 +7,7 @@ from rich.console import Console
|
|
|
7
7
|
from rich.logging import RichHandler
|
|
8
8
|
from rich.text import Text
|
|
9
9
|
|
|
10
|
-
from klaude_code
|
|
10
|
+
from klaude_code import const
|
|
11
11
|
|
|
12
12
|
# Module-level logger
|
|
13
13
|
logger = logging.getLogger("klaude_code")
|
|
@@ -87,13 +87,13 @@ def set_debug_logging(
|
|
|
87
87
|
|
|
88
88
|
# Determine output mode
|
|
89
89
|
use_file = write_to_file if write_to_file is not None else True
|
|
90
|
-
file_path = log_file if log_file is not None else DEFAULT_DEBUG_LOG_FILE
|
|
90
|
+
file_path = log_file if log_file is not None else const.DEFAULT_DEBUG_LOG_FILE
|
|
91
91
|
|
|
92
92
|
if use_file:
|
|
93
93
|
_file_handler = RotatingFileHandler(
|
|
94
94
|
file_path,
|
|
95
|
-
maxBytes=LOG_MAX_BYTES,
|
|
96
|
-
backupCount=LOG_BACKUP_COUNT,
|
|
95
|
+
maxBytes=const.LOG_MAX_BYTES,
|
|
96
|
+
backupCount=const.LOG_BACKUP_COUNT,
|
|
97
97
|
encoding="utf-8",
|
|
98
98
|
)
|
|
99
99
|
_file_handler.setLevel(logging.DEBUG)
|
|
@@ -121,7 +121,8 @@ def log(*objects: str | tuple[str, str], style: str = "") -> None:
|
|
|
121
121
|
style: Default style for all objects
|
|
122
122
|
"""
|
|
123
123
|
log_console.print(
|
|
124
|
-
*((Text(obj[0], style=obj[1]) if isinstance(obj, tuple) else Text(obj)) for obj in objects),
|
|
124
|
+
*((Text(obj[0], style=obj[1]) if isinstance(obj, tuple) else Text(obj)) for obj in objects),
|
|
125
|
+
style=style,
|
|
125
126
|
)
|
|
126
127
|
|
|
127
128
|
|
|
@@ -160,3 +161,8 @@ def _build_message(objects: Iterable[str | tuple[str, str]]) -> str:
|
|
|
160
161
|
else:
|
|
161
162
|
parts.append(obj)
|
|
162
163
|
return " ".join(parts)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def is_debug_enabled() -> bool:
|
|
167
|
+
"""Check if debug logging is currently enabled."""
|
|
168
|
+
return _debug_enabled
|
klaude_code/ui/__init__.py
CHANGED
|
@@ -1,8 +1,91 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
"""
|
|
2
|
+
UI Module - Display and Input Abstractions for klaude-code
|
|
3
|
+
|
|
4
|
+
This module provides the UI layer for klaude-code, including display modes
|
|
5
|
+
and input providers. The UI is designed around three main concepts:
|
|
6
|
+
|
|
7
|
+
Display Modes:
|
|
8
|
+
- REPLDisplay: Interactive terminal mode with Rich rendering, spinners, and live updates
|
|
9
|
+
- ExecDisplay: Non-interactive exec mode that only outputs task results
|
|
10
|
+
- DebugEventDisplay: Decorator that logs all events for debugging purposes
|
|
11
|
+
|
|
12
|
+
Input Providers:
|
|
13
|
+
- PromptToolkitInput: Interactive input with prompt-toolkit (completions, keybindings)
|
|
14
|
+
|
|
15
|
+
Factory Functions:
|
|
16
|
+
- create_default_display(): Creates the appropriate display for interactive mode
|
|
17
|
+
- create_exec_display(): Creates the appropriate display for exec mode
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# --- Abstract Interfaces ---
|
|
21
|
+
from .core.display import DisplayABC
|
|
22
|
+
from .core.input import InputProviderABC
|
|
23
|
+
from .modes.debug.display import DebugEventDisplay
|
|
24
|
+
from .modes.exec.display import ExecDisplay, StreamJsonDisplay
|
|
25
|
+
|
|
26
|
+
# --- Display Mode Implementations ---
|
|
27
|
+
from .modes.repl.display import REPLDisplay
|
|
28
|
+
|
|
29
|
+
# --- Input Implementations ---
|
|
30
|
+
from .modes.repl.input_prompt_toolkit import PromptToolkitInput
|
|
31
|
+
from .terminal.notifier import TerminalNotifier
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_default_display(
|
|
35
|
+
debug: bool = False,
|
|
36
|
+
theme: str | None = None,
|
|
37
|
+
notifier: TerminalNotifier | None = None,
|
|
38
|
+
) -> DisplayABC:
|
|
39
|
+
"""
|
|
40
|
+
Create the default display for interactive REPL mode.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
debug: If True, wrap the display with DebugEventDisplay to log all events.
|
|
44
|
+
theme: Optional theme name ("light" or "dark") for syntax highlighting.
|
|
45
|
+
notifier: Optional terminal notifier for desktop notifications.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A DisplayABC implementation suitable for interactive use.
|
|
49
|
+
"""
|
|
50
|
+
repl_display = REPLDisplay(theme=theme, notifier=notifier)
|
|
51
|
+
if debug:
|
|
52
|
+
return DebugEventDisplay(repl_display)
|
|
53
|
+
return repl_display
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def create_exec_display(debug: bool = False, stream_json: bool = False) -> DisplayABC:
|
|
57
|
+
"""
|
|
58
|
+
Create a display for exec (non-interactive) mode.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
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.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
A DisplayABC implementation that only outputs task results.
|
|
66
|
+
"""
|
|
67
|
+
if stream_json:
|
|
68
|
+
return StreamJsonDisplay()
|
|
69
|
+
exec_display = ExecDisplay()
|
|
70
|
+
if debug:
|
|
71
|
+
return DebugEventDisplay(exec_display)
|
|
72
|
+
return exec_display
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
# Abstract interfaces
|
|
77
|
+
"DisplayABC",
|
|
78
|
+
"InputProviderABC",
|
|
79
|
+
# Display mode implementations
|
|
80
|
+
"REPLDisplay",
|
|
81
|
+
"ExecDisplay",
|
|
82
|
+
"StreamJsonDisplay",
|
|
83
|
+
"DebugEventDisplay",
|
|
84
|
+
# Input implementations
|
|
85
|
+
"PromptToolkitInput",
|
|
86
|
+
# Factory functions
|
|
87
|
+
"create_default_display",
|
|
88
|
+
"create_exec_display",
|
|
89
|
+
# Supporting types
|
|
90
|
+
"TerminalNotifier",
|
|
91
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Core UI abstractions
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from asyncio import Queue
|
|
3
|
+
|
|
4
|
+
from klaude_code.protocol import events
|
|
5
|
+
from klaude_code.trace import log
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DisplayABC(ABC):
|
|
9
|
+
"""
|
|
10
|
+
Abstract base class for UI display implementations.
|
|
11
|
+
|
|
12
|
+
A Display is responsible for rendering events from the executor to the user.
|
|
13
|
+
Implementations can range from simple text output (ExecDisplay) to rich
|
|
14
|
+
interactive terminals (REPLDisplay) or debugging wrappers (DebugEventDisplay).
|
|
15
|
+
|
|
16
|
+
Lifecycle:
|
|
17
|
+
1. start() is called once before any events are consumed.
|
|
18
|
+
2. consume_event() is called for each event from the executor.
|
|
19
|
+
3. stop() is called once when the display is shutting down (after EndEvent).
|
|
20
|
+
|
|
21
|
+
Typical Usage:
|
|
22
|
+
display = create_default_display()
|
|
23
|
+
await display.consume_event_loop(event_queue)
|
|
24
|
+
|
|
25
|
+
# Or manually:
|
|
26
|
+
await display.start()
|
|
27
|
+
try:
|
|
28
|
+
async for event in events:
|
|
29
|
+
if isinstance(event, EndEvent):
|
|
30
|
+
break
|
|
31
|
+
await display.consume_event(event)
|
|
32
|
+
finally:
|
|
33
|
+
await display.stop()
|
|
34
|
+
|
|
35
|
+
Thread Safety:
|
|
36
|
+
Display implementations should be used from a single async task.
|
|
37
|
+
The consume_event_loop method handles the standard event loop pattern.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def consume_event(self, event: events.Event) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Process a single event from the executor.
|
|
44
|
+
|
|
45
|
+
This method is called for each event except EndEvent, which triggers stop().
|
|
46
|
+
Implementations should handle all relevant event types and render them
|
|
47
|
+
appropriately for the user.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
event: The event to process. See klaude_code.protocol.events for types.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
async def start(self) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Initialize the display before processing events.
|
|
57
|
+
|
|
58
|
+
Called once before any consume_event calls. Use this for any setup
|
|
59
|
+
that needs to happen before rendering begins (e.g., initializing
|
|
60
|
+
terminal state, starting background tasks).
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
async def stop(self) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Clean up the display after all events have been processed.
|
|
67
|
+
|
|
68
|
+
Called once after EndEvent is received. Use this for cleanup such as
|
|
69
|
+
stopping spinners, restoring terminal state, or flushing output buffers.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
async def consume_event_loop(self, q: Queue[events.Event]) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Main event loop that processes events from a queue.
|
|
75
|
+
|
|
76
|
+
This is the standard entry point for running a display. It handles:
|
|
77
|
+
- Calling start() before processing
|
|
78
|
+
- Consuming events until EndEvent is received
|
|
79
|
+
- Calling stop() after EndEvent
|
|
80
|
+
- Error handling and logging for individual events
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
q: An asyncio Queue of events to process. The loop exits when
|
|
84
|
+
an EndEvent is received.
|
|
85
|
+
"""
|
|
86
|
+
await self.start()
|
|
87
|
+
while True:
|
|
88
|
+
event = await q.get()
|
|
89
|
+
try:
|
|
90
|
+
if isinstance(event, events.EndEvent):
|
|
91
|
+
await self.stop()
|
|
92
|
+
break
|
|
93
|
+
await self.consume_event(event)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
import traceback
|
|
96
|
+
|
|
97
|
+
log(
|
|
98
|
+
f"Error in consume_event_loop, {e.__class__.__name__}, {e}",
|
|
99
|
+
style="red",
|
|
100
|
+
)
|
|
101
|
+
log(traceback.format_exc(), style="red")
|
|
102
|
+
finally:
|
|
103
|
+
q.task_done()
|