codebind 0.6.8__tar.gz → 0.6.10__tar.gz
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.
- {codebind-0.6.8 → codebind-0.6.10}/PKG-INFO +2 -2
- {codebind-0.6.8 → codebind-0.6.10}/README.md +1 -1
- {codebind-0.6.8 → codebind-0.6.10}/pyproject.toml +1 -1
- {codebind-0.6.8 → codebind-0.6.10}/pyproject.toml.orig +1 -1
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/execution.py +2 -2
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/session.py +35 -9
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/terminal.py +70 -48
- {codebind-0.6.8 → codebind-0.6.10}/LICENSE +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/install.json +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/package.json +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/static/590.dae4561c5427a855.js +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/static/remoteEntry.c10098fb6e87897c.js +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/static/style.js +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/static/third-party-licenses.json +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/jupyterlab/.yarnrc.yml +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/jupyterlab/install.json +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/jupyterlab/package.json +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/jupyterlab/src/index.ts +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/jupyterlab/tsconfig.json +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/jupyterlab/yarn.lock +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/__init__.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/cli.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/configuration.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/conversation.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/display.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/extension.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/images.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/instructions.md +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/jupyter.py +0 -0
- {codebind-0.6.8 → codebind-0.6.10}/src/codebind/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebind
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.10
|
|
4
4
|
Summary: A frontend-neutral model loop for persistent IPython sessions.
|
|
5
5
|
Keywords: ai,ipython,llm,repl,agents
|
|
6
6
|
Author: Giovanni Gravili
|
|
@@ -75,7 +75,7 @@ Inspect this project and tell me what to implement first.
|
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
Terminal conversations remain live for the current IPython process. Previous terminal inputs and outputs, including Python cells you ran yourself, enter the model's context when you ask a question. Durable automatic resumption belongs to notebooks, where the notebook file provides an unambiguous conversation identity.
|
|
78
|
-
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. To keep terminal scrollback usable, Codebind
|
|
78
|
+
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. Assistant Markdown appears before the following tool cell, and tool output appears as it is written. To keep terminal scrollback usable, Codebind previews at most three gray output lines, shortening each line after 60 characters with `...`. Further lines appear as `… N more output lines.` The complete output remains available to the model and in IPython history; `%output 8` opens the full text of cell 8 in IPython's pager. User-entered Python cells keep IPython's normal display behavior. Assistant Markdown is rendered through IPython's terminal MIME renderer. A terminal is a stream rather than a notebook document, so these displays are not editable notebook cells.
|
|
79
79
|
|
|
80
80
|
## JupyterLab
|
|
81
81
|
|
|
@@ -47,7 +47,7 @@ Inspect this project and tell me what to implement first.
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
Terminal conversations remain live for the current IPython process. Previous terminal inputs and outputs, including Python cells you ran yourself, enter the model's context when you ask a question. Durable automatic resumption belongs to notebooks, where the notebook file provides an unambiguous conversation identity.
|
|
50
|
-
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. To keep terminal scrollback usable, Codebind
|
|
50
|
+
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. Assistant Markdown appears before the following tool cell, and tool output appears as it is written. To keep terminal scrollback usable, Codebind previews at most three gray output lines, shortening each line after 60 characters with `...`. Further lines appear as `… N more output lines.` The complete output remains available to the model and in IPython history; `%output 8` opens the full text of cell 8 in IPython's pager. User-entered Python cells keep IPython's normal display behavior. Assistant Markdown is rendered through IPython's terminal MIME renderer. A terminal is a stream rather than a notebook document, so these displays are not editable notebook cells.
|
|
51
51
|
|
|
52
52
|
## JupyterLab
|
|
53
53
|
|
|
@@ -222,7 +222,7 @@ class IPythonExecutor:
|
|
|
222
222
|
with preview_tool_output() as preview, record_tool_output(self.shell):
|
|
223
223
|
result = self.shell.run_cell(cell, store_history=True)
|
|
224
224
|
report = self._native_report(result)
|
|
225
|
-
preview.show_notice(
|
|
225
|
+
preview.show_notice(report.error)
|
|
226
226
|
return report
|
|
227
227
|
|
|
228
228
|
bridge = self.bridge
|
|
@@ -277,7 +277,7 @@ class IPythonExecutor:
|
|
|
277
277
|
self.shell.events.trigger("post_execute")
|
|
278
278
|
self.shell.events.trigger("post_run_cell", result)
|
|
279
279
|
report = self._native_report(result)
|
|
280
|
-
preview.show_notice(
|
|
280
|
+
preview.show_notice(report.error)
|
|
281
281
|
return report
|
|
282
282
|
|
|
283
283
|
bridge = self.bridge
|
|
@@ -274,8 +274,24 @@ class Session:
|
|
|
274
274
|
for identifier in self._pending_calls(turn_id):
|
|
275
275
|
self._append_message_sync(self._interrupted_tool_message(identifier), turn_id)
|
|
276
276
|
|
|
277
|
-
def _update_assistant_stream(
|
|
278
|
-
|
|
277
|
+
def _update_assistant_stream(
|
|
278
|
+
self,
|
|
279
|
+
cell_id: str | None,
|
|
280
|
+
text: str,
|
|
281
|
+
shown: str,
|
|
282
|
+
terminal_displayed: list[str] | None = None,
|
|
283
|
+
) -> str | None:
|
|
284
|
+
if not text:
|
|
285
|
+
return cell_id
|
|
286
|
+
if self.bridge is None:
|
|
287
|
+
previous = terminal_displayed[0] if terminal_displayed is not None else shown
|
|
288
|
+
if previous.startswith(text):
|
|
289
|
+
return cell_id
|
|
290
|
+
addition = text[len(previous) :] if text.startswith(previous) else text
|
|
291
|
+
if addition:
|
|
292
|
+
self._display_assistant(addition)
|
|
293
|
+
if terminal_displayed is not None:
|
|
294
|
+
terminal_displayed[0] = text
|
|
279
295
|
return cell_id
|
|
280
296
|
if cell_id is None:
|
|
281
297
|
return self.bridge.start_markdown_cell(text)
|
|
@@ -284,7 +300,9 @@ class Session:
|
|
|
284
300
|
|
|
285
301
|
def _finish_assistant_stream(self, cell_id: str | None, response: AIMessage) -> None:
|
|
286
302
|
answer = _message_text(response)
|
|
287
|
-
if
|
|
303
|
+
if self.bridge is None:
|
|
304
|
+
return
|
|
305
|
+
if cell_id is not None:
|
|
288
306
|
self.bridge.finish_markdown_cell(cell_id, answer)
|
|
289
307
|
elif answer:
|
|
290
308
|
self._display_assistant(answer)
|
|
@@ -302,9 +320,10 @@ class Session:
|
|
|
302
320
|
self,
|
|
303
321
|
model: BaseChatModel,
|
|
304
322
|
) -> tuple[AIMessage, str | None]:
|
|
323
|
+
terminal_displayed = [""]
|
|
305
324
|
for retry in range(len(_MODEL_RETRY_DELAYS) + 1):
|
|
306
325
|
try:
|
|
307
|
-
return self._stream_model(self._bind(model), model)
|
|
326
|
+
return self._stream_model(self._bind(model), model, terminal_displayed)
|
|
308
327
|
except TransientProviderError as error:
|
|
309
328
|
if retry == len(_MODEL_RETRY_DELAYS):
|
|
310
329
|
raise
|
|
@@ -316,9 +335,10 @@ class Session:
|
|
|
316
335
|
self,
|
|
317
336
|
model: BaseChatModel,
|
|
318
337
|
) -> tuple[AIMessage, str | None]:
|
|
338
|
+
terminal_displayed = [""]
|
|
319
339
|
for retry in range(len(_MODEL_RETRY_DELAYS) + 1):
|
|
320
340
|
try:
|
|
321
|
-
return await self._astream_model(self._bind(model), model)
|
|
341
|
+
return await self._astream_model(self._bind(model), model, terminal_displayed)
|
|
322
342
|
except TransientProviderError as error:
|
|
323
343
|
if retry == len(_MODEL_RETRY_DELAYS):
|
|
324
344
|
raise
|
|
@@ -344,6 +364,7 @@ class Session:
|
|
|
344
364
|
self,
|
|
345
365
|
bound_model: Runnable[Any, BaseMessage],
|
|
346
366
|
model: BaseChatModel,
|
|
367
|
+
terminal_displayed: list[str] | None = None,
|
|
347
368
|
) -> tuple[AIMessage, str | None]:
|
|
348
369
|
aggregate: AIMessageChunk | None = None
|
|
349
370
|
response: AIMessage | None = None
|
|
@@ -366,12 +387,14 @@ class Session:
|
|
|
366
387
|
raise TypeError("model stream must return AIMessage chunks")
|
|
367
388
|
text = _message_text(current)
|
|
368
389
|
if tool_call_started and text != shown:
|
|
369
|
-
cell_id = self._update_assistant_stream(
|
|
390
|
+
cell_id = self._update_assistant_stream(
|
|
391
|
+
cell_id, text, shown, terminal_displayed
|
|
392
|
+
)
|
|
370
393
|
shown = text
|
|
371
394
|
completed = self._completed_stream_message(aggregate, response)
|
|
372
395
|
answer = _message_text(completed)
|
|
373
396
|
if answer != shown:
|
|
374
|
-
cell_id = self._update_assistant_stream(cell_id, answer)
|
|
397
|
+
cell_id = self._update_assistant_stream(cell_id, answer, shown, terminal_displayed)
|
|
375
398
|
return completed, cell_id
|
|
376
399
|
except BaseException:
|
|
377
400
|
self._cancel_assistant_stream(cell_id)
|
|
@@ -381,6 +404,7 @@ class Session:
|
|
|
381
404
|
self,
|
|
382
405
|
bound_model: Runnable[Any, BaseMessage],
|
|
383
406
|
model: BaseChatModel,
|
|
407
|
+
terminal_displayed: list[str] | None = None,
|
|
384
408
|
) -> tuple[AIMessage, str | None]:
|
|
385
409
|
aggregate: AIMessageChunk | None = None
|
|
386
410
|
response: AIMessage | None = None
|
|
@@ -403,12 +427,14 @@ class Session:
|
|
|
403
427
|
raise TypeError("model stream must return AIMessage chunks")
|
|
404
428
|
text = _message_text(current)
|
|
405
429
|
if tool_call_started and text != shown:
|
|
406
|
-
cell_id = self._update_assistant_stream(
|
|
430
|
+
cell_id = self._update_assistant_stream(
|
|
431
|
+
cell_id, text, shown, terminal_displayed
|
|
432
|
+
)
|
|
407
433
|
shown = text
|
|
408
434
|
completed = self._completed_stream_message(aggregate, response)
|
|
409
435
|
answer = _message_text(completed)
|
|
410
436
|
if answer != shown:
|
|
411
|
-
cell_id = self._update_assistant_stream(cell_id, answer)
|
|
437
|
+
cell_id = self._update_assistant_stream(cell_id, answer, shown, terminal_displayed)
|
|
412
438
|
return completed, cell_id
|
|
413
439
|
except BaseException:
|
|
414
440
|
self._cancel_assistant_stream(cell_id)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import ast
|
|
6
|
-
import shutil
|
|
7
6
|
import sys
|
|
8
7
|
from collections.abc import Callable, Iterator
|
|
9
8
|
from contextlib import contextmanager
|
|
@@ -23,13 +22,28 @@ from rich.markdown import Markdown
|
|
|
23
22
|
|
|
24
23
|
_MISSING = object()
|
|
25
24
|
_IMAGE_FORMATS = ("image/png", "image/jpeg", "image/svg+xml")
|
|
26
|
-
_MAXIMUM_VISIBLE_OUTPUT_LINES =
|
|
25
|
+
_MAXIMUM_VISIBLE_OUTPUT_LINES = 3
|
|
26
|
+
_MAXIMUM_VISIBLE_OUTPUT_CHARACTERS_PER_LINE = 80
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def _plain_ansi(value: str) -> str:
|
|
30
30
|
return "".join(text for _, text in to_formatted_text(ANSI(value)))
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _split_incomplete_ansi(value: str) -> tuple[str, str]:
|
|
34
|
+
last_escape = value.rfind("\x1b")
|
|
35
|
+
if last_escape < 0:
|
|
36
|
+
return value, ""
|
|
37
|
+
tail = value[last_escape:]
|
|
38
|
+
if tail == "\x1b":
|
|
39
|
+
return value[:last_escape], tail
|
|
40
|
+
if tail.startswith("\x1b[") and not any("@" <= char <= "~" for char in tail[2:]):
|
|
41
|
+
return value[:last_escape], tail
|
|
42
|
+
if tail.startswith("\x1b]") and "\x07" not in tail and "\x1b\\" not in tail:
|
|
43
|
+
return value[:last_escape], tail
|
|
44
|
+
return value, ""
|
|
45
|
+
|
|
46
|
+
|
|
33
47
|
def _magic_input(transformed: str) -> tuple[str, str] | None:
|
|
34
48
|
"""Recognize a complete IPython magic call in transformed history."""
|
|
35
49
|
try:
|
|
@@ -178,65 +192,80 @@ class TerminalCellHistory:
|
|
|
178
192
|
class _OutputPreview:
|
|
179
193
|
"""Limit only terminal painting; IPython still records every written byte."""
|
|
180
194
|
|
|
181
|
-
def __init__(self, maximum_lines: int,
|
|
195
|
+
def __init__(self, maximum_lines: int, maximum_characters_per_line: int) -> None:
|
|
182
196
|
self.maximum_lines = maximum_lines
|
|
183
|
-
self.
|
|
197
|
+
self.maximum_characters_per_line = maximum_characters_per_line
|
|
184
198
|
self.visible_lines = 0
|
|
199
|
+
self.characters_on_line = 0
|
|
200
|
+
self.line_truncated = False
|
|
185
201
|
self.visible_characters = 0
|
|
186
202
|
self.hidden_line_breaks = 0
|
|
187
203
|
self.hidden_has_tail = False
|
|
188
204
|
self.hidden_characters = 0
|
|
189
|
-
self.truncated = False
|
|
190
205
|
self.visible_ends_line = True
|
|
191
206
|
|
|
192
207
|
def _hide(self, data: str) -> None:
|
|
193
|
-
self.truncated = True
|
|
194
208
|
self.hidden_characters += len(data)
|
|
195
209
|
self.hidden_line_breaks += data.count("\n") + data.count("\r") - data.count("\r\n")
|
|
196
210
|
self.hidden_has_tail = not data.endswith(("\n", "\r"))
|
|
197
211
|
|
|
212
|
+
def _paint(self, stream, text: str) -> None:
|
|
213
|
+
if not text:
|
|
214
|
+
return
|
|
215
|
+
if stream.isatty():
|
|
216
|
+
stream.write("\x1b[90m")
|
|
217
|
+
stream.write(text)
|
|
218
|
+
if stream.isatty():
|
|
219
|
+
stream.write("\x1b[0m")
|
|
220
|
+
self.visible_characters += len(text)
|
|
221
|
+
|
|
198
222
|
def write(self, stream, data: str) -> int:
|
|
199
223
|
for piece in data.splitlines(keepends=True):
|
|
200
|
-
|
|
201
|
-
if self.truncated or self.visible_lines >= self.maximum_lines or remaining <= 0:
|
|
224
|
+
if self.visible_lines >= self.maximum_lines:
|
|
202
225
|
self._hide(piece)
|
|
203
226
|
continue
|
|
204
|
-
|
|
205
|
-
if
|
|
206
|
-
|
|
207
|
-
|
|
227
|
+
|
|
228
|
+
if piece.endswith("\r\n"):
|
|
229
|
+
body, ending = piece[:-2], "\r\n"
|
|
230
|
+
elif piece.endswith(("\n", "\r")):
|
|
231
|
+
body, ending = piece[:-1], piece[-1]
|
|
232
|
+
else:
|
|
233
|
+
body, ending = piece, ""
|
|
234
|
+
|
|
235
|
+
visible = ""
|
|
236
|
+
if not self.line_truncated:
|
|
237
|
+
remaining = self.maximum_characters_per_line - self.characters_on_line
|
|
238
|
+
visible = body[:remaining]
|
|
239
|
+
self.characters_on_line += len(visible)
|
|
240
|
+
if len(body) > remaining:
|
|
241
|
+
visible += "…"
|
|
242
|
+
self.line_truncated = True
|
|
243
|
+
if ending:
|
|
244
|
+
visible += ending
|
|
245
|
+
self.visible_lines += 1
|
|
246
|
+
self.characters_on_line = 0
|
|
247
|
+
self.line_truncated = False
|
|
208
248
|
if visible:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
stream.write(visible)
|
|
212
|
-
if stream.isatty():
|
|
213
|
-
stream.write("\x1b[0m")
|
|
214
|
-
self.visible_characters += len(visible)
|
|
215
|
-
self.visible_ends_line = visible.endswith(("\n", "\r"))
|
|
216
|
-
if self.visible_ends_line:
|
|
217
|
-
self.visible_lines += 1
|
|
218
|
-
if len(visible) < len(piece):
|
|
219
|
-
self._hide(piece[len(visible) :])
|
|
249
|
+
self._paint(stream, visible)
|
|
250
|
+
self.visible_ends_line = bool(ending)
|
|
220
251
|
return len(data)
|
|
221
252
|
|
|
222
|
-
def show_notice(self,
|
|
253
|
+
def show_notice(self, error: dict[str, str] | None = None) -> None:
|
|
254
|
+
if self.visible_characters and not self.visible_ends_line:
|
|
255
|
+
sys.stdout.write("\n")
|
|
256
|
+
self.visible_ends_line = True
|
|
223
257
|
if not self.hidden_characters:
|
|
224
258
|
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
sys.stdout.write("\n")
|
|
259
|
+
terminal = sys.stdout.isatty()
|
|
260
|
+
if terminal:
|
|
261
|
+
sys.stdout.write("\x1b[90m")
|
|
229
262
|
hidden_lines = self.hidden_line_breaks + int(self.hidden_has_tail)
|
|
230
263
|
label = "line" if hidden_lines == 1 else "lines"
|
|
231
|
-
sys.stdout.write(
|
|
232
|
-
f"… {hidden_lines:,} more output {label}. Run %output {number} to view all.\n"
|
|
233
|
-
)
|
|
264
|
+
sys.stdout.write(f"… {hidden_lines:,} more output {label}\n")
|
|
234
265
|
if error is not None:
|
|
235
|
-
if sys.stdout.isatty():
|
|
236
|
-
sys.stdout.write("\x1b[90m")
|
|
237
266
|
sys.stdout.write(f"{error['type']}: {error['message']}\n")
|
|
238
|
-
|
|
239
|
-
|
|
267
|
+
if terminal:
|
|
268
|
+
sys.stdout.write("\x1b[0m")
|
|
240
269
|
|
|
241
270
|
|
|
242
271
|
class _PreviewStream:
|
|
@@ -246,20 +275,12 @@ class _PreviewStream:
|
|
|
246
275
|
self.pending = ""
|
|
247
276
|
|
|
248
277
|
def write(self, data: str) -> int:
|
|
249
|
-
self.pending
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
self.pending = lines.pop()
|
|
253
|
-
else:
|
|
254
|
-
self.pending = ""
|
|
255
|
-
for line in lines:
|
|
256
|
-
self.preview.write(self.stream, _plain_ansi(line))
|
|
278
|
+
complete, self.pending = _split_incomplete_ansi(self.pending + data)
|
|
279
|
+
if complete:
|
|
280
|
+
self.preview.write(self.stream, _plain_ansi(complete))
|
|
257
281
|
return len(data)
|
|
258
282
|
|
|
259
283
|
def flush(self) -> None:
|
|
260
|
-
if self.pending:
|
|
261
|
-
self.preview.write(self.stream, _plain_ansi(self.pending))
|
|
262
|
-
self.pending = ""
|
|
263
284
|
self.stream.flush()
|
|
264
285
|
|
|
265
286
|
def __getattr__(self, name: str):
|
|
@@ -269,10 +290,10 @@ class _PreviewStream:
|
|
|
269
290
|
@contextmanager
|
|
270
291
|
def preview_tool_output(
|
|
271
292
|
maximum_lines: int = _MAXIMUM_VISIBLE_OUTPUT_LINES,
|
|
293
|
+
maximum_characters_per_line: int = _MAXIMUM_VISIBLE_OUTPUT_CHARACTERS_PER_LINE,
|
|
272
294
|
) -> Iterator[_OutputPreview]:
|
|
273
295
|
"""Show a compact preview while keeping the tool cell's complete native output."""
|
|
274
|
-
|
|
275
|
-
preview = _OutputPreview(maximum_lines, maximum_lines * max(20, columns))
|
|
296
|
+
preview = _OutputPreview(maximum_lines, maximum_characters_per_line)
|
|
276
297
|
stdout, stderr = sys.stdout, sys.stderr
|
|
277
298
|
preview_stdout = _PreviewStream(stdout, preview)
|
|
278
299
|
preview_stderr = _PreviewStream(stderr, preview)
|
|
@@ -399,6 +420,7 @@ def install_markdown_renderer(shell: TerminalInteractiveShell) -> Callable[[], N
|
|
|
399
420
|
previous_renderer = shell.mime_renderers.get(mime, _MISSING)
|
|
400
421
|
|
|
401
422
|
def render(text: str, _metadata: object) -> None:
|
|
423
|
+
sys.stdout.write("\n")
|
|
402
424
|
Console(file=sys.stdout, soft_wrap=True, no_color=shell.colors.lower() == "nocolor").print(
|
|
403
425
|
Markdown(text)
|
|
404
426
|
)
|
|
File without changes
|
{codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/install.json
RENAMED
|
File without changes
|
{codebind-0.6.8 → codebind-0.6.10}/data/share/jupyter/labextensions/codebind-jupyterlab/package.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|