nbcat 0.12.1__py3-none-any.whl → 0.13.0__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.
nbcat/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.12.1"
1
+ __version__ = "0.13.0"
nbcat/main.py CHANGED
@@ -22,6 +22,7 @@ from .exceptions import (
22
22
  )
23
23
  from .image import Image
24
24
  from .markdown import Markdown
25
+ from .pager import Pager
25
26
  from .schemas import Cell, Notebook
26
27
 
27
28
  console = Console()
@@ -135,20 +136,26 @@ def render_cell(cell: Cell) -> RenderableType:
135
136
  return Group(*rows)
136
137
 
137
138
 
138
- def print_notebook(nb: Notebook):
139
+ def render_notebook(nb: Notebook) -> list[RenderableType]:
139
140
  """
140
- Print the notebook to the console with formatted cell inputs and outputs.
141
+ Convert a Notebook object into a list of rich renderables for terminal display.
142
+
143
+ Each cell in the notebook is processed and rendered using `render_cell`,
144
+ producing a sequence of styled input/output blocks suitable for use in a
145
+ Textual or Rich-based terminal UI.
141
146
 
142
147
  Args:
143
- nb (Notebook): A Notebook object containing a list of cells.
144
- """
145
- if not nb.cells:
146
- console.print("[bold red]Notebook contains no cells.")
147
- return
148
+ nb (Notebook): The notebook object containing parsed cells (e.g., from a .ipynb file).
148
149
 
150
+ Returns
151
+ -------
152
+ list[RenderableType]: A list of rich renderable objects representing the notebook cells.
153
+ Returns an empty list if the notebook has no cells.
154
+ """
155
+ rendered: list[RenderableType] = []
149
156
  for cell in nb.cells:
150
- rendered = render_cell(cell)
151
- console.print(rendered)
157
+ rendered.append(render_cell(cell))
158
+ return rendered
152
159
 
153
160
 
154
161
  def main():
@@ -160,20 +167,38 @@ def main():
160
167
  "file", help="Path or URL to a .ipynb notebook", type=str
161
168
  ).completer = FilesCompleter()
162
169
  parser.add_argument(
170
+ "-v",
163
171
  "--version",
164
172
  help="print version information and quite",
165
173
  action="version",
166
174
  version=__version__,
167
175
  )
168
176
  parser.add_argument(
169
- "--debug", help="enable extended error output", action="store_true", default=False
177
+ "-d", "--debug", help="enable extended error output", action="store_true", default=False
178
+ )
179
+ parser.add_argument(
180
+ "-p",
181
+ "--page",
182
+ help="enable paginated view mode (similar to less)",
183
+ action="store_true",
184
+ default=False,
170
185
  )
171
186
 
172
187
  try:
173
188
  argcomplete.autocomplete(parser)
174
189
  args = parser.parse_args()
190
+
175
191
  notebook = read_notebook(args.file, debug=args.debug)
176
- print_notebook(notebook)
192
+ objects = render_notebook(notebook)
193
+
194
+ if not objects:
195
+ console.print("[bold red]Notebook contains no cells.")
196
+ return
197
+
198
+ if args.page:
199
+ Pager(objects).run()
200
+ else:
201
+ console.print(*objects)
177
202
  except Exception as e:
178
203
  sys.exit(f"nbcat: {e}")
179
204
 
nbcat/pager.py ADDED
@@ -0,0 +1,82 @@
1
+ from rich.console import RenderableType
2
+ from textual.app import App, ComposeResult
3
+ from textual.binding import Binding
4
+ from textual.containers import VerticalScroll
5
+ from textual.widgets import Static
6
+
7
+
8
+ class Pager(App):
9
+ BINDINGS = [
10
+ # Exit
11
+ Binding("q", "quit", "Quit"),
12
+ Binding(":q", "quit", "Quit"),
13
+ Binding("Q", "quit", "Quit"),
14
+ Binding(":Q", "quit", "Quit"),
15
+ Binding("ZZ", "quit", "Quit"),
16
+ # One line
17
+ Binding("j", "scroll_down", "Down"),
18
+ Binding("e", "scroll_down", "Down"),
19
+ Binding("^e", "scroll_down", "Down"),
20
+ Binding("^n", "scroll_down", "Down"),
21
+ Binding("cr", "scroll_down", "Down"), # carriage return = Enter
22
+ Binding("k", "scroll_up", "Up"),
23
+ Binding("y", "scroll_up", "Up"),
24
+ Binding("ctrl+y", "scroll_up", "Up"),
25
+ Binding("ctrl+k", "scroll_up", "Up"),
26
+ Binding("ctrl+p", "scroll_up", "Up"),
27
+ # One window
28
+ Binding("f", "page_down", "Page Down"),
29
+ Binding("ctrl+f", "page_down", "Page Down"),
30
+ Binding("ctrl+v", "page_down", "Page Down"),
31
+ Binding("space", "page_down", "Page Down"),
32
+ Binding("b", "page_up", "Page Up"),
33
+ Binding("ctrl+b", "page_up", "Page Up"),
34
+ Binding("escape+v", "page_up", "Page Up"),
35
+ # Extended window control
36
+ Binding("z", "page_down", "Window Down (set N)"),
37
+ Binding("w", "page_up", "Window Up (set N)"),
38
+ Binding("escape+space", "page_down", "Forward one window, no EOF stop"),
39
+ Binding("d", "half_page_down", "Half Page Down"),
40
+ Binding("ctrl+d", "half_page_down", "Half Page Down"),
41
+ # Jumping
42
+ Binding("g", "go_to_top", "Top of File"),
43
+ Binding("<", "go_to_top", "Top of File"),
44
+ Binding("escape+<", "go_to_top", "Top of File"),
45
+ Binding("G", "go_to_bottom", "Bottom of File"),
46
+ Binding(">", "go_to_bottom", "Bottom of File"),
47
+ Binding("escape+>", "go_to_bottom", "Bottom of File"),
48
+ ]
49
+
50
+ def __init__(self, objects: list[RenderableType]):
51
+ super().__init__()
52
+ self._objects = objects
53
+
54
+ def compose(self) -> ComposeResult:
55
+ with VerticalScroll():
56
+ for obj in self._objects:
57
+ yield Static(obj)
58
+
59
+ def on_mount(self) -> None:
60
+ self.theme = "textual-ansi"
61
+ self.viewer = self.query_one(VerticalScroll)
62
+
63
+ def action_scroll_down(self) -> None:
64
+ self.viewer.scroll_to(y=self.viewer.scroll_y + 1)
65
+
66
+ def action_scroll_up(self) -> None:
67
+ self.viewer.scroll_to(y=self.viewer.scroll_y - 1)
68
+
69
+ def action_page_down(self) -> None:
70
+ self.viewer.scroll_to(y=self.viewer.scroll_y + self.viewer.virtual_size.height)
71
+
72
+ def action_page_up(self) -> None:
73
+ self.viewer.scroll_to(y=max(self.viewer.scroll_y - self.viewer.virtual_size.height, 0))
74
+
75
+ def action_half_page_down(self) -> None:
76
+ self.viewer.scroll_to(y=self.viewer.scroll_y + (self.viewer.virtual_size.height / 2))
77
+
78
+ def action_go_to_top(self) -> None:
79
+ self.viewer.scroll_to(y=0)
80
+
81
+ def action_go_to_bottom(self) -> None:
82
+ self.viewer.scroll_to(y=self.viewer.virtual_size.height)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbcat
3
- Version: 0.12.1
3
+ Version: 0.13.0
4
4
  Summary: cat for jupyter notebooks
5
5
  Project-URL: Homepage, https://github.com/akopdev/nbcat
6
6
  Project-URL: Repository, https://github.com/akopdev/nbcat
@@ -33,6 +33,7 @@ Requires-Dist: argcomplete
33
33
  Requires-Dist: pydantic
34
34
  Requires-Dist: requests
35
35
  Requires-Dist: rich
36
+ Requires-Dist: textual
36
37
  Requires-Dist: timg
37
38
  Provides-Extra: dev
38
39
  Requires-Dist: pytest; extra == 'dev'
@@ -52,9 +53,10 @@ Description-Content-Type: text/markdown
52
53
 
53
54
  ## Features
54
55
 
55
- - Very fast and lightweight with minimal dependencies
56
- - Preview remote notebooks without downloading them
57
- - Supports for all Jupyter notebook versions, including old legacy formats
56
+ - Very fast and lightweight with minimal dependencies.
57
+ - Preview remote notebooks without downloading them.
58
+ - Enable paginated view mode with keyboard navigation (similar to `less`).
59
+ - Supports for all Jupyter notebook versions, including old legacy formats.
58
60
 
59
61
  ## Motivation
60
62
 
@@ -0,0 +1,14 @@
1
+ nbcat/__init__.py,sha256=DgpLNbv0e1LIEOOe54Db8_390i9pelMEFEnsBsNmyhA,23
2
+ nbcat/enums.py,sha256=Fn8PIcLl_uY4nQIs1EUvmKTwfhNUIZgmhRFiCSJk9wk,411
3
+ nbcat/exceptions.py,sha256=Ho7LQz9K70VtIMDNtAwuAtGmb-lFKxGxSj7MN3-EpDA,321
4
+ nbcat/image.py,sha256=hGZZK5mtij7ckWAvQwzim3thqGC92pLW5ZU5CYbRv_Q,995
5
+ nbcat/main.py,sha256=FSXD7SobpjhwDvOKAS1C-Lntmv9mPJhDUugjq8dBzAA,6678
6
+ nbcat/markdown.py,sha256=GkPNfzBhNLMl89pVcj14U6ytYUVk3yNB0G1zGYRRJ04,2962
7
+ nbcat/pager.py,sha256=Yu0XIh5MvhvT-cmULYVxo6s2Ufjf2CQ-NDS5fcMQ-IM,3184
8
+ nbcat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ nbcat/schemas.py,sha256=a7GoKgPTHgun199-J-sZq-ahkdQwvyRaCdQVg1gC798,3135
10
+ nbcat-0.13.0.dist-info/METADATA,sha256=rspE5gM8qznr1ZNL9REUdPBJho1QRlDRcXSTl2ngSH8,4265
11
+ nbcat-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ nbcat-0.13.0.dist-info/entry_points.txt,sha256=io_GRDsecAkYuCZALsjyea3VBq91VCoSznqlZEAJshY,42
13
+ nbcat-0.13.0.dist-info/licenses/LICENSE,sha256=7GjUnahXdd5opdvlpJdb1BisLbiXt2iOFhzIUduhdkE,1072
14
+ nbcat-0.13.0.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- nbcat/__init__.py,sha256=PAuBI8I6F9Yu_86XjI2yaWn8QmCd9ZvK7tkZLWvEg-Q,23
2
- nbcat/enums.py,sha256=Fn8PIcLl_uY4nQIs1EUvmKTwfhNUIZgmhRFiCSJk9wk,411
3
- nbcat/exceptions.py,sha256=Ho7LQz9K70VtIMDNtAwuAtGmb-lFKxGxSj7MN3-EpDA,321
4
- nbcat/image.py,sha256=hGZZK5mtij7ckWAvQwzim3thqGC92pLW5ZU5CYbRv_Q,995
5
- nbcat/main.py,sha256=gZ8mM2louxU8GgB40F7DE7pc7MAidDHatiNjkLUzeug,5839
6
- nbcat/markdown.py,sha256=GkPNfzBhNLMl89pVcj14U6ytYUVk3yNB0G1zGYRRJ04,2962
7
- nbcat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- nbcat/schemas.py,sha256=a7GoKgPTHgun199-J-sZq-ahkdQwvyRaCdQVg1gC798,3135
9
- nbcat-0.12.1.dist-info/METADATA,sha256=Jq7TL6gCnqsAn_hm5eTHHK-DRGbp3nXn4aq6-w_jv2Y,4164
10
- nbcat-0.12.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- nbcat-0.12.1.dist-info/entry_points.txt,sha256=io_GRDsecAkYuCZALsjyea3VBq91VCoSznqlZEAJshY,42
12
- nbcat-0.12.1.dist-info/licenses/LICENSE,sha256=7GjUnahXdd5opdvlpJdb1BisLbiXt2iOFhzIUduhdkE,1072
13
- nbcat-0.12.1.dist-info/RECORD,,
File without changes