klaude-code 1.2.24__py3-none-any.whl → 1.2.26__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/runtime.py +17 -1
- klaude_code/command/thinking_cmd.py +37 -28
- klaude_code/const.py +8 -6
- klaude_code/core/executor.py +45 -2
- klaude_code/core/tool/file/apply_patch_tool.py +26 -3
- klaude_code/protocol/model.py +24 -1
- klaude_code/protocol/op.py +11 -0
- klaude_code/protocol/op_handler.py +5 -0
- klaude_code/ui/modes/repl/display.py +2 -0
- klaude_code/ui/modes/repl/event_handler.py +23 -12
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +12 -1
- klaude_code/ui/modes/repl/renderer.py +104 -12
- klaude_code/ui/renderers/sub_agent.py +14 -12
- klaude_code/ui/renderers/thinking.py +1 -1
- klaude_code/ui/renderers/tools.py +26 -2
- klaude_code/ui/rich/code_panel.py +24 -5
- klaude_code/ui/rich/live.py +17 -0
- klaude_code/ui/rich/markdown.py +192 -109
- klaude_code/ui/rich/status.py +5 -11
- klaude_code/ui/rich/theme.py +1 -4
- {klaude_code-1.2.24.dist-info → klaude_code-1.2.26.dist-info}/METADATA +2 -1
- {klaude_code-1.2.24.dist-info → klaude_code-1.2.26.dist-info}/RECORD +24 -24
- {klaude_code-1.2.24.dist-info → klaude_code-1.2.26.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.24.dist-info → klaude_code-1.2.26.dist-info}/entry_points.txt +0 -0
klaude_code/ui/rich/markdown.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# copy from https://github.com/Aider-AI/aider/blob/main/aider/mdstream.py
|
|
2
1
|
from __future__ import annotations
|
|
3
2
|
|
|
4
3
|
import contextlib
|
|
@@ -7,13 +6,15 @@ import time
|
|
|
7
6
|
from collections.abc import Callable
|
|
8
7
|
from typing import Any, ClassVar
|
|
9
8
|
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from rich
|
|
9
|
+
from markdown_it import MarkdownIt
|
|
10
|
+
from markdown_it.token import Token
|
|
11
|
+
from rich import box
|
|
12
|
+
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
|
13
|
+
from rich.markdown import CodeBlock, Heading, Markdown, MarkdownElement, TableElement
|
|
13
14
|
from rich.rule import Rule
|
|
14
|
-
from rich.spinner import Spinner
|
|
15
15
|
from rich.style import Style, StyleType
|
|
16
16
|
from rich.syntax import Syntax
|
|
17
|
+
from rich.table import Table
|
|
17
18
|
from rich.text import Text
|
|
18
19
|
from rich.theme import Theme
|
|
19
20
|
|
|
@@ -53,6 +54,24 @@ class Divider(MarkdownElement):
|
|
|
53
54
|
yield Rule(style=style, characters="-")
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
class MinimalHeavyHeadTable(TableElement):
|
|
58
|
+
"""A table element with MINIMAL_HEAVY_HEAD box style."""
|
|
59
|
+
|
|
60
|
+
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
61
|
+
table = Table(box=box.MARKDOWN)
|
|
62
|
+
|
|
63
|
+
if self.header is not None and self.header.row is not None:
|
|
64
|
+
for column in self.header.row.cells:
|
|
65
|
+
table.add_column(column.content)
|
|
66
|
+
|
|
67
|
+
if self.body is not None:
|
|
68
|
+
for row in self.body.rows:
|
|
69
|
+
row_content = [element.content for element in row.cells]
|
|
70
|
+
table.add_row(*row_content)
|
|
71
|
+
|
|
72
|
+
yield table
|
|
73
|
+
|
|
74
|
+
|
|
56
75
|
class LeftHeading(Heading):
|
|
57
76
|
"""A heading class that renders left-justified."""
|
|
58
77
|
|
|
@@ -64,7 +83,7 @@ class LeftHeading(Heading):
|
|
|
64
83
|
yield h1_text
|
|
65
84
|
elif self.tag == "h2":
|
|
66
85
|
text.stylize(Style(bold=True, underline=False))
|
|
67
|
-
yield
|
|
86
|
+
yield text
|
|
68
87
|
else:
|
|
69
88
|
yield text
|
|
70
89
|
|
|
@@ -78,6 +97,7 @@ class NoInsetMarkdown(Markdown):
|
|
|
78
97
|
"code_block": NoInsetCodeBlock,
|
|
79
98
|
"heading_open": LeftHeading,
|
|
80
99
|
"hr": Divider,
|
|
100
|
+
"table_open": MinimalHeavyHeadTable,
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
|
|
@@ -90,23 +110,28 @@ class ThinkingMarkdown(Markdown):
|
|
|
90
110
|
"code_block": ThinkingCodeBlock,
|
|
91
111
|
"heading_open": LeftHeading,
|
|
92
112
|
"hr": Divider,
|
|
113
|
+
"table_open": MinimalHeavyHeadTable,
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
|
|
96
117
|
class MarkdownStream:
|
|
97
|
-
"""
|
|
118
|
+
"""Block-based streaming Markdown renderer.
|
|
98
119
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
This renderer is optimized for terminal UX:
|
|
121
|
+
|
|
122
|
+
- Stable area: only prints *completed* Markdown blocks to scrollback (append-only).
|
|
123
|
+
- Live area: continuously repaints only the final *possibly incomplete* block.
|
|
124
|
+
|
|
125
|
+
Block boundaries are computed with `MarkdownIt("commonmark")` (token maps / top-level tokens).
|
|
126
|
+
Rendering is done with Rich Markdown (customizable via `markdown_class`).
|
|
102
127
|
"""
|
|
103
128
|
|
|
104
129
|
def __init__(
|
|
105
130
|
self,
|
|
131
|
+
console: Console,
|
|
106
132
|
mdargs: dict[str, Any] | None = None,
|
|
107
133
|
theme: Theme | None = None,
|
|
108
|
-
|
|
109
|
-
spinner: Spinner | None = None,
|
|
134
|
+
live_sink: Callable[[RenderableType | None], None] | None = None,
|
|
110
135
|
mark: str | None = None,
|
|
111
136
|
mark_style: StyleType | None = None,
|
|
112
137
|
left_margin: int = 0,
|
|
@@ -125,24 +150,23 @@ class MarkdownStream:
|
|
|
125
150
|
right_margin (int, optional): Number of columns to reserve on the right side
|
|
126
151
|
markdown_class: Markdown class to use for rendering (defaults to NoInsetMarkdown)
|
|
127
152
|
"""
|
|
128
|
-
self.
|
|
153
|
+
self._stable_rendered_lines: list[str] = []
|
|
154
|
+
self._stable_source_line_count: int = 0
|
|
129
155
|
|
|
130
156
|
if mdargs:
|
|
131
157
|
self.mdargs: dict[str, Any] = mdargs
|
|
132
158
|
else:
|
|
133
159
|
self.mdargs = {}
|
|
134
160
|
|
|
135
|
-
|
|
136
|
-
self.live: Live | None = None
|
|
161
|
+
self._live_sink = live_sink
|
|
137
162
|
|
|
138
163
|
# Streaming control
|
|
139
164
|
self.when: float = 0.0 # Timestamp of last update
|
|
140
165
|
self.min_delay: float = 1.0 / 20 # Minimum time between updates (20fps)
|
|
141
|
-
self.
|
|
166
|
+
self._parser: MarkdownIt = MarkdownIt("commonmark")
|
|
142
167
|
|
|
143
168
|
self.theme = theme
|
|
144
169
|
self.console = console
|
|
145
|
-
self.spinner: Spinner | None = spinner
|
|
146
170
|
self.mark: str | None = mark
|
|
147
171
|
self.mark_style: StyleType | None = mark_style
|
|
148
172
|
|
|
@@ -154,9 +178,117 @@ class MarkdownStream:
|
|
|
154
178
|
@property
|
|
155
179
|
def _live_started(self) -> bool:
|
|
156
180
|
"""Check if Live display has been started (derived from self.live)."""
|
|
157
|
-
return self.
|
|
181
|
+
return self._live_sink is not None
|
|
182
|
+
|
|
183
|
+
def _get_base_width(self) -> int:
|
|
184
|
+
return self.console.options.max_width
|
|
185
|
+
|
|
186
|
+
def compute_candidate_stable_line(self, text: str) -> int:
|
|
187
|
+
"""Return the start line of the last top-level block, or 0.
|
|
188
|
+
|
|
189
|
+
This value is not monotonic; callers should clamp it (e.g. with the
|
|
190
|
+
previous stable line) before using it to advance state.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
tokens = self._parser.parse(text)
|
|
195
|
+
except Exception:
|
|
196
|
+
return 0
|
|
197
|
+
|
|
198
|
+
top_level: list[Token] = [token for token in tokens if token.level == 0 and token.map is not None]
|
|
199
|
+
if len(top_level) < 2:
|
|
200
|
+
return 0
|
|
201
|
+
|
|
202
|
+
last = top_level[-1]
|
|
203
|
+
assert last.map is not None
|
|
204
|
+
start_line = last.map[0]
|
|
205
|
+
return max(start_line, 0)
|
|
206
|
+
|
|
207
|
+
def split_blocks(self, text: str, *, min_stable_line: int = 0, final: bool = False) -> tuple[str, str, int]:
|
|
208
|
+
"""Split full markdown into stable and live sources.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
stable_source: Completed blocks (append-only)
|
|
212
|
+
live_source: Last (possibly incomplete) block
|
|
213
|
+
stable_line: Line index where live starts
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
lines = text.splitlines(keepends=True)
|
|
217
|
+
line_count = len(lines)
|
|
218
|
+
|
|
219
|
+
stable_line = line_count if final else self.compute_candidate_stable_line(text)
|
|
220
|
+
|
|
221
|
+
stable_line = min(stable_line, line_count)
|
|
222
|
+
stable_line = max(stable_line, min_stable_line)
|
|
223
|
+
|
|
224
|
+
stable_source = "".join(lines[:stable_line])
|
|
225
|
+
live_source = "".join(lines[stable_line:])
|
|
226
|
+
return stable_source, live_source, stable_line
|
|
227
|
+
|
|
228
|
+
def render_ansi(self, text: str, *, apply_mark: bool) -> str:
|
|
229
|
+
"""Render markdown source to an ANSI string.
|
|
230
|
+
|
|
231
|
+
This is primarily intended for internal debugging and tests.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
return "".join(self._render_markdown_to_lines(text, apply_mark=apply_mark))
|
|
235
|
+
|
|
236
|
+
def render_stable_ansi(self, stable_source: str, *, has_live_suffix: bool, final: bool) -> str:
|
|
237
|
+
"""Render stable prefix to ANSI, preserving inter-block spacing."""
|
|
238
|
+
|
|
239
|
+
if not stable_source:
|
|
240
|
+
return ""
|
|
241
|
+
|
|
242
|
+
render_source = stable_source
|
|
243
|
+
if not final and has_live_suffix:
|
|
244
|
+
render_source = self._append_nonfinal_sentinel(stable_source)
|
|
158
245
|
|
|
159
|
-
|
|
246
|
+
return self.render_ansi(render_source, apply_mark=True)
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def normalize_live_ansi_for_boundary(*, stable_ansi: str, live_ansi: str) -> str:
|
|
250
|
+
"""Normalize whitespace at the stable/live boundary.
|
|
251
|
+
|
|
252
|
+
Some Rich Markdown blocks (e.g. lists) render with a leading blank line.
|
|
253
|
+
If the stable prefix already renders a trailing blank line, rendering the
|
|
254
|
+
live suffix separately may introduce an extra blank line that wouldn't
|
|
255
|
+
appear when rendering the full document.
|
|
256
|
+
|
|
257
|
+
This function removes leading blank lines from the live ANSI when the
|
|
258
|
+
stable ANSI already ends with a blank line.
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
stable_lines = stable_ansi.splitlines(keepends=True)
|
|
262
|
+
stable_ends_blank = bool(stable_lines) and not stable_lines[-1].strip()
|
|
263
|
+
if not stable_ends_blank:
|
|
264
|
+
return live_ansi
|
|
265
|
+
|
|
266
|
+
live_lines = live_ansi.splitlines(keepends=True)
|
|
267
|
+
while live_lines and not live_lines[0].strip():
|
|
268
|
+
live_lines.pop(0)
|
|
269
|
+
return "".join(live_lines)
|
|
270
|
+
|
|
271
|
+
def _append_nonfinal_sentinel(self, stable_source: str) -> str:
|
|
272
|
+
"""Make Rich render stable content as if it isn't the last block.
|
|
273
|
+
|
|
274
|
+
Rich Markdown may omit trailing spacing for the last block in a document.
|
|
275
|
+
When we render only the stable prefix (without the live suffix), we still
|
|
276
|
+
need the *inter-block* spacing to match the full document.
|
|
277
|
+
|
|
278
|
+
A harmless HTML comment block causes Rich Markdown to emit the expected
|
|
279
|
+
spacing while rendering no visible content.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
if not stable_source:
|
|
283
|
+
return stable_source
|
|
284
|
+
|
|
285
|
+
if stable_source.endswith("\n\n"):
|
|
286
|
+
return stable_source + "<!-- -->"
|
|
287
|
+
if stable_source.endswith("\n"):
|
|
288
|
+
return stable_source + "\n<!-- -->"
|
|
289
|
+
return stable_source + "\n\n<!-- -->"
|
|
290
|
+
|
|
291
|
+
def _render_markdown_to_lines(self, text: str, *, apply_mark: bool) -> list[str]:
|
|
160
292
|
"""Render markdown text to a list of lines.
|
|
161
293
|
|
|
162
294
|
Args:
|
|
@@ -168,13 +300,8 @@ class MarkdownStream:
|
|
|
168
300
|
# Render the markdown to a string buffer
|
|
169
301
|
string_io = io.StringIO()
|
|
170
302
|
|
|
171
|
-
#
|
|
172
|
-
|
|
173
|
-
if self.console is not None:
|
|
174
|
-
base_width = self.console.options.max_width
|
|
175
|
-
else:
|
|
176
|
-
probe_console = Console(theme=self.theme)
|
|
177
|
-
base_width = probe_console.options.max_width
|
|
303
|
+
# Keep width stable across frames to prevent reflow/jitter.
|
|
304
|
+
base_width = self._get_base_width()
|
|
178
305
|
|
|
179
306
|
effective_width = max(base_width - self.left_margin - self.right_margin, 1)
|
|
180
307
|
|
|
@@ -195,7 +322,7 @@ class MarkdownStream:
|
|
|
195
322
|
indent_prefix = " " * self.left_margin if self.left_margin > 0 else ""
|
|
196
323
|
processed_lines: list[str] = []
|
|
197
324
|
mark_applied = False
|
|
198
|
-
use_mark = bool(self.mark) and self.left_margin >= 2
|
|
325
|
+
use_mark = apply_mark and bool(self.mark) and self.left_margin >= 2
|
|
199
326
|
|
|
200
327
|
# Pre-render styled mark if needed
|
|
201
328
|
styled_mark: str | None = None
|
|
@@ -227,102 +354,58 @@ class MarkdownStream:
|
|
|
227
354
|
|
|
228
355
|
def __del__(self) -> None:
|
|
229
356
|
"""Destructor to ensure Live display is properly cleaned up."""
|
|
230
|
-
if self.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
357
|
+
if self._live_sink is None:
|
|
358
|
+
return
|
|
359
|
+
with contextlib.suppress(Exception):
|
|
360
|
+
self._live_sink(None)
|
|
234
361
|
|
|
235
362
|
def update(self, text: str, final: bool = False) -> None:
|
|
236
|
-
"""Update the
|
|
237
|
-
|
|
238
|
-
Args:
|
|
239
|
-
text (str): The markdown text received so far
|
|
240
|
-
final (bool): If True, this is the final update and we should clean up
|
|
241
|
-
|
|
242
|
-
Splits the output into "stable" older lines and the "last few" lines
|
|
243
|
-
which aren't considered stable. They may shift around as new chunks
|
|
244
|
-
are appended to the markdown text.
|
|
245
|
-
|
|
246
|
-
The stable lines emit to the console above the Live window.
|
|
247
|
-
The unstable lines emit into the Live window so they can be repainted.
|
|
248
|
-
|
|
249
|
-
Markdown going to the console works better in terminal scrollback buffers.
|
|
250
|
-
The live window doesn't play nice with terminal scrollback.
|
|
251
|
-
"""
|
|
252
|
-
if not self._live_started:
|
|
253
|
-
initial_content = self._live_renderable(Text(""), final=False)
|
|
254
|
-
# transient=False keeps final frame on screen after stop()
|
|
255
|
-
self.live = Live(
|
|
256
|
-
initial_content,
|
|
257
|
-
refresh_per_second=1.0 / self.min_delay,
|
|
258
|
-
console=self.console,
|
|
259
|
-
)
|
|
260
|
-
self.live.start()
|
|
261
|
-
|
|
262
|
-
if self.live is None:
|
|
263
|
-
return
|
|
363
|
+
"""Update the display with the latest full markdown buffer."""
|
|
264
364
|
|
|
265
365
|
now = time.time()
|
|
266
|
-
# Throttle updates to maintain smooth rendering
|
|
267
366
|
if not final and now - self.when < self.min_delay:
|
|
268
367
|
return
|
|
269
368
|
self.when = now
|
|
270
369
|
|
|
271
|
-
|
|
272
|
-
start = time.time()
|
|
273
|
-
lines = self._render_markdown_to_lines(text)
|
|
274
|
-
render_time = time.time() - start
|
|
275
|
-
|
|
276
|
-
# Set min_delay to render time plus a small buffer
|
|
277
|
-
self.min_delay = min(max(render_time * 10, 1.0 / 20), 2)
|
|
370
|
+
previous_stable_line = self._stable_source_line_count
|
|
278
371
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
# Print new stable lines above Live window
|
|
285
|
-
if num_lines > 0:
|
|
286
|
-
num_printed = len(self.printed)
|
|
287
|
-
to_append_count = num_lines - num_printed
|
|
372
|
+
stable_source, live_source, stable_line = self.split_blocks(
|
|
373
|
+
text,
|
|
374
|
+
min_stable_line=previous_stable_line,
|
|
375
|
+
final=final,
|
|
376
|
+
)
|
|
288
377
|
|
|
289
|
-
|
|
290
|
-
append_chunk = lines[num_printed:num_lines]
|
|
291
|
-
append_chunk_text = Text.from_ansi("".join(append_chunk))
|
|
292
|
-
live = self.live
|
|
293
|
-
assert live is not None
|
|
294
|
-
live.console.print(append_chunk_text)
|
|
295
|
-
self.printed = lines[:num_lines]
|
|
378
|
+
start = time.time()
|
|
296
379
|
|
|
297
|
-
|
|
380
|
+
stable_changed = final or stable_line > self._stable_source_line_count
|
|
381
|
+
if stable_changed and stable_source:
|
|
382
|
+
stable_ansi = self.render_stable_ansi(stable_source, has_live_suffix=bool(live_source), final=final)
|
|
383
|
+
stable_lines = stable_ansi.splitlines(keepends=True)
|
|
384
|
+
new_lines = stable_lines[len(self._stable_rendered_lines) :]
|
|
385
|
+
if new_lines:
|
|
386
|
+
stable_chunk = "".join(new_lines)
|
|
387
|
+
self.console.print(Text.from_ansi(stable_chunk), end="\n")
|
|
388
|
+
self._stable_rendered_lines = stable_lines
|
|
389
|
+
self._stable_source_line_count = stable_line
|
|
390
|
+
elif final and not stable_source:
|
|
391
|
+
self._stable_rendered_lines = []
|
|
392
|
+
self._stable_source_line_count = stable_line
|
|
298
393
|
|
|
299
|
-
# Final: render remaining lines without spinner, then stop Live
|
|
300
394
|
if final:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
rest = "".join(rest_lines)
|
|
304
|
-
rest_text = Text.from_ansi(rest)
|
|
305
|
-
final_renderable = self._live_renderable(rest_text, final=True)
|
|
306
|
-
live.update(final_renderable)
|
|
307
|
-
live.stop()
|
|
308
|
-
self.live = None
|
|
395
|
+
if self._live_sink is not None:
|
|
396
|
+
self._live_sink(None)
|
|
309
397
|
return
|
|
310
398
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
assert live is not None
|
|
315
|
-
live_renderable = self._live_renderable(rest, final)
|
|
316
|
-
live.update(live_renderable)
|
|
399
|
+
if const.MARKDOWN_STREAM_LIVE_REPAINT_ENABLED and self._live_sink is not None:
|
|
400
|
+
apply_mark_live = self._stable_source_line_count == 0
|
|
401
|
+
live_lines = self._render_markdown_to_lines(live_source, apply_mark=apply_mark_live)
|
|
317
402
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
else:
|
|
322
|
-
return Group(rest, Text(), self.spinner)
|
|
403
|
+
if self._stable_rendered_lines and not self._stable_rendered_lines[-1].strip():
|
|
404
|
+
while live_lines and not live_lines[0].strip():
|
|
405
|
+
live_lines.pop(0)
|
|
323
406
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
407
|
+
live_text = Text.from_ansi("".join(live_lines))
|
|
408
|
+
self._live_sink(live_text)
|
|
409
|
+
|
|
410
|
+
elapsed = time.time() - start
|
|
411
|
+
self.min_delay = min(max(elapsed * 6, 1.0 / 30), 0.5)
|
klaude_code/ui/rich/status.py
CHANGED
|
@@ -22,7 +22,7 @@ BREATHING_SPINNER_NAME = "dots"
|
|
|
22
22
|
|
|
23
23
|
# Alternating glyphs for the breathing spinner - switches at each "transparent" point
|
|
24
24
|
_BREATHING_SPINNER_GLYPHS_BASE = [
|
|
25
|
-
"
|
|
25
|
+
"✦",
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
# Shuffle glyphs on module load for variety across sessions
|
|
@@ -56,18 +56,12 @@ def _shimmer_profile(main_text: str) -> list[tuple[str, float]]:
|
|
|
56
56
|
char_count = len(chars)
|
|
57
57
|
period = char_count + padding * 2
|
|
58
58
|
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
# baseline text length and the configured sweep duration.
|
|
62
|
-
# The baseline is chosen to be close to the default
|
|
63
|
-
# "Thinking … (esc to interrupt)" status line.
|
|
64
|
-
baseline_chars = 30
|
|
65
|
-
base_period = baseline_chars + padding * 2
|
|
66
|
-
sweep_seconds = const.STATUS_SHIMMER_SWEEP_SECONDS
|
|
67
|
-
char_speed = base_period / sweep_seconds if sweep_seconds > 0 else base_period
|
|
59
|
+
# Use same period as breathing spinner for visual consistency
|
|
60
|
+
sweep_seconds = max(const.SPINNER_BREATH_PERIOD_SECONDS, 0.1)
|
|
68
61
|
|
|
69
62
|
elapsed = _elapsed_since_start()
|
|
70
|
-
|
|
63
|
+
# Complete one full sweep in sweep_seconds, regardless of text length
|
|
64
|
+
pos_f = (elapsed / sweep_seconds % 1.0) * period
|
|
71
65
|
pos = int(pos_f)
|
|
72
66
|
band_half_width = const.STATUS_SHIMMER_BAND_HALF_WIDTH
|
|
73
67
|
|
klaude_code/ui/rich/theme.py
CHANGED
|
@@ -291,7 +291,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
291
291
|
"markdown.code.border": palette.grey3,
|
|
292
292
|
"markdown.h1": "bold reverse",
|
|
293
293
|
"markdown.h1.border": palette.grey3,
|
|
294
|
-
"markdown.h2
|
|
294
|
+
"markdown.h2": "bold underline",
|
|
295
295
|
"markdown.h3": "bold " + palette.grey1,
|
|
296
296
|
"markdown.h4": "bold " + palette.grey2,
|
|
297
297
|
"markdown.hr": palette.grey3,
|
|
@@ -311,7 +311,6 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
311
311
|
"markdown.code.border": palette.grey3,
|
|
312
312
|
"markdown.h1": "bold reverse",
|
|
313
313
|
"markdown.h1.border": palette.grey3,
|
|
314
|
-
"markdown.h2.border": palette.grey3,
|
|
315
314
|
"markdown.h3": "bold " + palette.grey1,
|
|
316
315
|
"markdown.h4": "bold " + palette.grey2,
|
|
317
316
|
"markdown.hr": palette.grey3,
|
|
@@ -329,7 +328,6 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
329
328
|
Style(color=palette.blue),
|
|
330
329
|
Style(color=palette.purple),
|
|
331
330
|
Style(color=palette.orange),
|
|
332
|
-
Style(color=palette.red),
|
|
333
331
|
Style(color=palette.grey1),
|
|
334
332
|
Style(color=palette.yellow),
|
|
335
333
|
],
|
|
@@ -339,7 +337,6 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
339
337
|
Style(bgcolor=palette.blue_sub_background),
|
|
340
338
|
Style(bgcolor=palette.purple_background),
|
|
341
339
|
Style(bgcolor=palette.orange_background),
|
|
342
|
-
Style(bgcolor=palette.red_background),
|
|
343
340
|
Style(bgcolor=palette.grey_background),
|
|
344
341
|
Style(bgcolor=palette.yellow_background),
|
|
345
342
|
],
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.26
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
6
|
Requires-Dist: chardet>=5.2.0
|
|
7
7
|
Requires-Dist: ddgs>=9.9.3
|
|
8
8
|
Requires-Dist: diff-match-patch>=20241021
|
|
9
|
+
Requires-Dist: markdown-it-py>=4.0.0
|
|
9
10
|
Requires-Dist: openai>=1.102.0
|
|
10
11
|
Requires-Dist: pillow>=12.0.0
|
|
11
12
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
@@ -11,7 +11,7 @@ klaude_code/cli/config_cmd.py,sha256=SBFmBnHvkf5IJtpsDDuHsHWQCmYd2i4PtIMBOKpxmOM
|
|
|
11
11
|
klaude_code/cli/debug.py,sha256=vizBXc3648vBZQonreMqvv_b5UdRgcQoOIT-iEIx1G4,2318
|
|
12
12
|
klaude_code/cli/list_model.py,sha256=9YOxhWE0J59NaY-SrgPA9_jA1A8rlOGwWmzK0TRuos4,8011
|
|
13
13
|
klaude_code/cli/main.py,sha256=pyU2W2X3lg7Z-4adiOzA9_2l-5QSejYm68HrhAiu470,10469
|
|
14
|
-
klaude_code/cli/runtime.py,sha256=
|
|
14
|
+
klaude_code/cli/runtime.py,sha256=XqF1d53UnIgnmnlqKGR3YBdBNlBFuoyc7BvUfr22CGw,14824
|
|
15
15
|
klaude_code/cli/self_update.py,sha256=fekLNRm3ivZ-Xbc-79rcgDBXbq-Zb-BkSQOGMRLeTAs,7986
|
|
16
16
|
klaude_code/cli/session_cmd.py,sha256=jAopkqq_DGgoDIcGxT-RSzn9R4yqBC8NCaNgK1GLqnQ,2634
|
|
17
17
|
klaude_code/command/__init__.py,sha256=B39fxrrvxb51B6qeQJoh3lXWCsPoI81BJvdSLb-8CYg,3117
|
|
@@ -30,14 +30,14 @@ klaude_code/command/registry.py,sha256=avTjsoyLv11SsLsY_qb3OpsRjsSyxIlu7uwJI0Nq6
|
|
|
30
30
|
klaude_code/command/release_notes_cmd.py,sha256=FIrBRfKTlXEp8mBh15buNjgOrl_GMX7FeeMWxYYBn1o,2674
|
|
31
31
|
klaude_code/command/status_cmd.py,sha256=sYmzfex7RVhgrBCjRyD8fsZ6ioZvjVzQ_-FvmcsA7fo,5365
|
|
32
32
|
klaude_code/command/terminal_setup_cmd.py,sha256=SivM1gX_anGY_8DCQNFZ5VblFqt4sVgCMEWPRlo6K5w,10911
|
|
33
|
-
klaude_code/command/thinking_cmd.py,sha256=
|
|
33
|
+
klaude_code/command/thinking_cmd.py,sha256=8EdSN6huXihM5NHJEryZLA7CkgRT7mZgMVTJsT1-x8U,9108
|
|
34
34
|
klaude_code/config/__init__.py,sha256=Qrqvi8nizkj6N77h2vDj0r4rbgCiqxvz2HLBPFuWulA,120
|
|
35
35
|
klaude_code/config/config.py,sha256=2jvM6a8zoC-TdRFaLIw3OW5paxxeXC6l-o05ds4RysA,7263
|
|
36
36
|
klaude_code/config/select_model.py,sha256=KCdFjaoHXyO9QidNna_OGdDrvlEXtRUXKfG-F8kdNLk,5188
|
|
37
|
-
klaude_code/const.py,sha256=
|
|
37
|
+
klaude_code/const.py,sha256=Xc6UKku2sGQE05mvPNCpBbKK205vJrS9CaNAeKvu1AA,4612
|
|
38
38
|
klaude_code/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
39
|
klaude_code/core/agent.py,sha256=bWm-UFX_0-KAy5j_YHH8X8o3MJT4-40Ni2EaDP2SL5k,5819
|
|
40
|
-
klaude_code/core/executor.py,sha256=
|
|
40
|
+
klaude_code/core/executor.py,sha256=MkfKPOZWknjyQzSUl_qCGRxsyYY9zetQVnerO_dWqx4,26827
|
|
41
41
|
klaude_code/core/manager/__init__.py,sha256=hdIbpnYj6i18byiWjtJIm5l7NYYDQMvafw8fePVPydc,562
|
|
42
42
|
klaude_code/core/manager/llm_clients.py,sha256=X2oMFWgJcP0tK8GEtMMDYR3HyR6_H8FuyCqpzWF5x2k,871
|
|
43
43
|
klaude_code/core/manager/llm_clients_builder.py,sha256=pPZ_xBh-_ipV66L-9a1fnwNos4iik82Zkq0E0y3WrfI,1521
|
|
@@ -60,7 +60,7 @@ klaude_code/core/tool/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
60
60
|
klaude_code/core/tool/file/_utils.py,sha256=OG4BE9WyJqzH8ilVCL3D9yvAcHk-r-L9snd-E8gO_io,967
|
|
61
61
|
klaude_code/core/tool/file/apply_patch.py,sha256=LZd3pYQ9ow_TxiFnqYuzD216HmvkLX6lW6BoMd9iQRs,17080
|
|
62
62
|
klaude_code/core/tool/file/apply_patch_tool.md,sha256=KVDsjUiLDa97gym0NrZNVG4jA1_zN-2i-B3upVQyOhU,59
|
|
63
|
-
klaude_code/core/tool/file/apply_patch_tool.py,sha256=
|
|
63
|
+
klaude_code/core/tool/file/apply_patch_tool.py,sha256=LGH0vfKAEGy91Q-quo75JQERUGeoojOao7NvEdQONTI,8059
|
|
64
64
|
klaude_code/core/tool/file/diff_builder.py,sha256=b53S4Ebw7aPmd9AZ97Pxu6MRcnUIdVD36MJ2yTDrAjI,5872
|
|
65
65
|
klaude_code/core/tool/file/edit_tool.md,sha256=rEcUjJuPC46t1nXWjTDxplDcxWDbzTWsr6_bYt5_aRI,1110
|
|
66
66
|
klaude_code/core/tool/file/edit_tool.py,sha256=wuXISJRTWobNCKjkQ01UpoA13I0Zxcg1r7hXErAQxnQ,10897
|
|
@@ -122,9 +122,9 @@ klaude_code/protocol/__init__.py,sha256=aGUgzhYqvhuT3Mk2vj7lrHGriH4h9TSbqV1RsRFA
|
|
|
122
122
|
klaude_code/protocol/commands.py,sha256=GN6GX9fo7YYtfumrBTpOmOvZofsnzZN2SAxP2X0BjTk,644
|
|
123
123
|
klaude_code/protocol/events.py,sha256=KUMf1rLNdHQO9cZiQ9Pa1VsKkP1PTMbUkp18bu_jGy8,3935
|
|
124
124
|
klaude_code/protocol/llm_param.py,sha256=cb4ubLq21PIsMOC8WJb0aid12z_sT1b7FsbNJMr-jLg,4255
|
|
125
|
-
klaude_code/protocol/model.py,sha256=
|
|
126
|
-
klaude_code/protocol/op.py,sha256=
|
|
127
|
-
klaude_code/protocol/op_handler.py,sha256=
|
|
125
|
+
klaude_code/protocol/model.py,sha256=bHTSVKwkEBlMELtPEANzxu2c_GvOSZOCk9AP6mpronY,14128
|
|
126
|
+
klaude_code/protocol/op.py,sha256=zG8AGFcTx1vIZFN0lNZjIjucjmDYM4eVOR7tRiLofF4,4589
|
|
127
|
+
klaude_code/protocol/op_handler.py,sha256=feTMdrz2QBwnjdv6ndizTinbBA9HFeH4oiBDeQBRKoY,1749
|
|
128
128
|
klaude_code/protocol/sub_agent/__init__.py,sha256=Abap5lPLgnSCQsVD3axfeqnj2UtxOcDLGX8e9HugfSU,3964
|
|
129
129
|
klaude_code/protocol/sub_agent/explore.py,sha256=Z4M7i98XBLew38ClXiW-hJteSYjMUu2b548rkR7JW3A,2579
|
|
130
130
|
klaude_code/protocol/sub_agent/oracle.py,sha256=0cbuutKQcvwaM--Q15mbkCdbpZMF4YjxDN1jkuGVKp4,3344
|
|
@@ -162,11 +162,11 @@ klaude_code/ui/modes/exec/display.py,sha256=m2kkgaUoGD9rEVUmcm7Vs_PyAI2iruKCJYRh
|
|
|
162
162
|
klaude_code/ui/modes/repl/__init__.py,sha256=35a6SUiL1SDi2i43X2VjHQw97rR7yhbLBzkGI5aC6Bc,1526
|
|
163
163
|
klaude_code/ui/modes/repl/clipboard.py,sha256=ZCpk7kRSXGhh0Q_BWtUUuSYT7ZOqRjAoRcg9T9n48Wo,5137
|
|
164
164
|
klaude_code/ui/modes/repl/completers.py,sha256=GIvUS9TAFMMPDpoXLuIupEccoqIMEpSEw4IZmKjVo4c,28560
|
|
165
|
-
klaude_code/ui/modes/repl/display.py,sha256=
|
|
166
|
-
klaude_code/ui/modes/repl/event_handler.py,sha256=
|
|
167
|
-
klaude_code/ui/modes/repl/input_prompt_toolkit.py,sha256=
|
|
165
|
+
klaude_code/ui/modes/repl/display.py,sha256=06wawOHWO2ItEA9EIEh97p3GDID7TJhAtpaA03nPQXs,2335
|
|
166
|
+
klaude_code/ui/modes/repl/event_handler.py,sha256=bX8QFE9nccpe6DP8bGXUi9gcSxcYHK5cb6hliwg_hyM,25530
|
|
167
|
+
klaude_code/ui/modes/repl/input_prompt_toolkit.py,sha256=F1p4JZp-KjDvTEZVR6bC0nb4ayd2VaEYsLmEA0KJOUM,9054
|
|
168
168
|
klaude_code/ui/modes/repl/key_bindings.py,sha256=Fxz9Ey2SnOHvfleMeSYVduxuofY0Yo-97hMRs-OMe-o,7800
|
|
169
|
-
klaude_code/ui/modes/repl/renderer.py,sha256=
|
|
169
|
+
klaude_code/ui/modes/repl/renderer.py,sha256=XcBztFI3ZfbuzWuqjRTVzAWbGKSY8F6uCmzZblU3vPM,15981
|
|
170
170
|
klaude_code/ui/renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
171
171
|
klaude_code/ui/renderers/assistant.py,sha256=nO_QNJ2e9TwtU2IlojO9lCMWNFUNmcE9Ezz-WW748_w,743
|
|
172
172
|
klaude_code/ui/renderers/common.py,sha256=5RdXC3ngtlhfmxRlbwOPtHtbXhAoWwbHoGX78tZcaTc,2284
|
|
@@ -174,19 +174,19 @@ klaude_code/ui/renderers/developer.py,sha256=CawltuPSt_8s5lLoYKWYD9htwORbjgHeb4A
|
|
|
174
174
|
klaude_code/ui/renderers/diffs.py,sha256=A37Wuz1_UIg0-st5le68-DRGz4-kot6N-wy9xEMKdz0,10403
|
|
175
175
|
klaude_code/ui/renderers/errors.py,sha256=o9pOHRqZ0nxMPwkmV4CG81X7taW1-izvga6RY3fFRMI,516
|
|
176
176
|
klaude_code/ui/renderers/metadata.py,sha256=b1aNqLjyaqErP4O5YSEXg7ychJcNTKpZ6hTmdcNJX84,8190
|
|
177
|
-
klaude_code/ui/renderers/sub_agent.py,sha256
|
|
178
|
-
klaude_code/ui/renderers/thinking.py,sha256=
|
|
179
|
-
klaude_code/ui/renderers/tools.py,sha256=
|
|
177
|
+
klaude_code/ui/renderers/sub_agent.py,sha256=g8QCFXTtFX_w8oTaGMYGuy6u5KqbFMlvzWofER0hGKk,5946
|
|
178
|
+
klaude_code/ui/renderers/thinking.py,sha256=nwKotPFS3WInsdrHFquqOHyvR8dTEKZ0JS5SMKJ_GgA,1946
|
|
179
|
+
klaude_code/ui/renderers/tools.py,sha256=x4L9880HZAwXnr89M4GxbMybhwXjs6jWA7QVcI3n4_c,23524
|
|
180
180
|
klaude_code/ui/renderers/user_input.py,sha256=e2hZS7UUnzQuQ6UqzSKRDkFJMkKTLUoub1JclHMX40g,3941
|
|
181
181
|
klaude_code/ui/rich/__init__.py,sha256=zEZjnHR3Fnv_sFMxwIMjoJfwDoC4GRGv3lHJzAGRq_o,236
|
|
182
182
|
klaude_code/ui/rich/cjk_wrap.py,sha256=ncmifgTwF6q95iayHQyazGbntt7BRQb_Ed7aXc8JU6Y,7551
|
|
183
|
-
klaude_code/ui/rich/code_panel.py,sha256=
|
|
184
|
-
klaude_code/ui/rich/live.py,sha256=
|
|
185
|
-
klaude_code/ui/rich/markdown.py,sha256=
|
|
183
|
+
klaude_code/ui/rich/code_panel.py,sha256=ZKuJHh-kh-hIkBXSGLERLaDbJ7I9hvtvmYKocJn39_w,4744
|
|
184
|
+
klaude_code/ui/rich/live.py,sha256=qiBLPSE4KW_Dpemy5MZ5BKhkFWEN2fjXBiQHmhJrPSM,2722
|
|
185
|
+
klaude_code/ui/rich/markdown.py,sha256=bZqnhJ_NvJ4d4G4IfXMn8XacJ_cPoRC-qhRvFaimZn8,15297
|
|
186
186
|
klaude_code/ui/rich/quote.py,sha256=tZcxN73SfDBHF_qk0Jkh9gWBqPBn8VLp9RF36YRdKEM,1123
|
|
187
187
|
klaude_code/ui/rich/searchable_text.py,sha256=DCVZgEFv7_ergAvT2v7XrfQAUXUzhmAwuVAchlIx8RY,2448
|
|
188
|
-
klaude_code/ui/rich/status.py,sha256=
|
|
189
|
-
klaude_code/ui/rich/theme.py,sha256=
|
|
188
|
+
klaude_code/ui/rich/status.py,sha256=QHg4oWmPSQH19H81vOFpImEqWyDtAbIXjuCGsuDjBPA,9278
|
|
189
|
+
klaude_code/ui/rich/theme.py,sha256=AKgl84dsn-xV8R7V6_lXllf2HAoRenc6SgM_sdbvjxk,13145
|
|
190
190
|
klaude_code/ui/terminal/__init__.py,sha256=GIMnsEcIAGT_vBHvTlWEdyNmAEpruyscUA6M_j3GQZU,1412
|
|
191
191
|
klaude_code/ui/terminal/color.py,sha256=M-i09DVlLAhAyhQjfeAi7OipoGi1p_OVkaZxeRfykY0,7135
|
|
192
192
|
klaude_code/ui/terminal/control.py,sha256=6SGNwxorP3jMW9xqnZy2BC0OsJd4DSrS13O3t6YlZzs,4916
|
|
@@ -194,7 +194,7 @@ klaude_code/ui/terminal/notifier.py,sha256=wkRM66d98Oh6PujnN4bB7NiQxIYEHqQXverMK
|
|
|
194
194
|
klaude_code/ui/terminal/progress_bar.py,sha256=MDnhPbqCnN4GDgLOlxxOEVZPDwVC_XL2NM5sl1MFNcQ,2133
|
|
195
195
|
klaude_code/ui/utils/__init__.py,sha256=YEsCLjbCPaPza-UXTPUMTJTrc9BmNBUP5CbFWlshyOQ,15
|
|
196
196
|
klaude_code/ui/utils/common.py,sha256=tqHqwgLtAyP805kwRFyoAL4EgMutcNb3Y-GAXJ4IeuM,2263
|
|
197
|
-
klaude_code-1.2.
|
|
198
|
-
klaude_code-1.2.
|
|
199
|
-
klaude_code-1.2.
|
|
200
|
-
klaude_code-1.2.
|
|
197
|
+
klaude_code-1.2.26.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
198
|
+
klaude_code-1.2.26.dist-info/entry_points.txt,sha256=7CWKjolvs6dZiYHpelhA_FRJ-sVDh43eu3iWuOhKc_w,53
|
|
199
|
+
klaude_code-1.2.26.dist-info/METADATA,sha256=CI9hiFD-OVRxM4BY8dohWKSA-skNFZ4bfpW5jpdzwwQ,7762
|
|
200
|
+
klaude_code-1.2.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|