euporie 2.8.0__py3-none-any.whl → 2.8.5__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.
Files changed (129) hide show
  1. euporie/console/_commands.py +143 -0
  2. euporie/console/_settings.py +58 -0
  3. euporie/console/app.py +25 -71
  4. euporie/console/tabs/console.py +267 -147
  5. euporie/core/__init__.py +1 -9
  6. euporie/core/__main__.py +31 -5
  7. euporie/core/_settings.py +104 -0
  8. euporie/core/app/__init__.py +3 -0
  9. euporie/core/app/_commands.py +70 -0
  10. euporie/core/app/_settings.py +427 -0
  11. euporie/core/{app.py → app/app.py} +214 -572
  12. euporie/core/app/base.py +51 -0
  13. euporie/core/{current.py → app/current.py} +13 -4
  14. euporie/core/app/cursor.py +35 -0
  15. euporie/core/app/dummy.py +12 -0
  16. euporie/core/app/launch.py +28 -0
  17. euporie/core/bars/__init__.py +11 -0
  18. euporie/core/bars/command.py +182 -0
  19. euporie/core/bars/menu.py +258 -0
  20. euporie/core/{widgets → bars}/search.py +154 -57
  21. euporie/core/{widgets → bars}/status.py +9 -26
  22. euporie/core/clipboard.py +19 -80
  23. euporie/core/comm/base.py +8 -6
  24. euporie/core/comm/ipywidgets.py +21 -12
  25. euporie/core/comm/registry.py +2 -1
  26. euporie/core/commands.py +11 -5
  27. euporie/core/completion.py +3 -2
  28. euporie/core/config.py +368 -341
  29. euporie/core/convert/__init__.py +0 -30
  30. euporie/core/convert/datum.py +131 -60
  31. euporie/core/convert/formats/__init__.py +31 -0
  32. euporie/core/convert/formats/ansi.py +46 -30
  33. euporie/core/convert/formats/common.py +11 -23
  34. euporie/core/convert/formats/html.py +45 -40
  35. euporie/core/convert/formats/pil.py +1 -1
  36. euporie/core/convert/formats/png.py +3 -5
  37. euporie/core/convert/formats/sixel.py +3 -3
  38. euporie/core/convert/registry.py +11 -8
  39. euporie/core/convert/utils.py +50 -23
  40. euporie/core/diagnostics.py +2 -2
  41. euporie/core/filters.py +72 -82
  42. euporie/core/format.py +13 -2
  43. euporie/core/ft/ansi.py +1 -1
  44. euporie/core/ft/html.py +36 -36
  45. euporie/core/ft/table.py +1 -3
  46. euporie/core/ft/utils.py +4 -1
  47. euporie/core/graphics.py +216 -124
  48. euporie/core/history.py +2 -2
  49. euporie/core/inspection.py +3 -2
  50. euporie/core/io.py +207 -28
  51. euporie/core/kernel/__init__.py +1 -0
  52. euporie/core/{kernel.py → kernel/client.py} +100 -139
  53. euporie/core/kernel/manager.py +114 -0
  54. euporie/core/key_binding/bindings/__init__.py +2 -8
  55. euporie/core/key_binding/bindings/basic.py +47 -7
  56. euporie/core/key_binding/bindings/completion.py +3 -8
  57. euporie/core/key_binding/bindings/micro.py +5 -7
  58. euporie/core/key_binding/bindings/mouse.py +26 -24
  59. euporie/core/key_binding/bindings/terminal.py +193 -0
  60. euporie/core/key_binding/bindings/vi.py +46 -0
  61. euporie/core/key_binding/key_processor.py +43 -2
  62. euporie/core/key_binding/registry.py +2 -0
  63. euporie/core/key_binding/utils.py +22 -2
  64. euporie/core/keys.py +7156 -92
  65. euporie/core/layout/cache.py +35 -25
  66. euporie/core/layout/containers.py +280 -74
  67. euporie/core/layout/decor.py +5 -5
  68. euporie/core/layout/mouse.py +1 -1
  69. euporie/core/layout/print.py +16 -3
  70. euporie/core/layout/scroll.py +26 -28
  71. euporie/core/log.py +75 -60
  72. euporie/core/lsp.py +118 -24
  73. euporie/core/margins.py +60 -31
  74. euporie/core/path.py +2 -1
  75. euporie/core/renderer.py +58 -17
  76. euporie/core/style.py +60 -40
  77. euporie/core/suggest.py +103 -85
  78. euporie/core/tabs/__init__.py +34 -0
  79. euporie/core/tabs/_settings.py +113 -0
  80. euporie/core/tabs/base.py +11 -435
  81. euporie/core/tabs/kernel.py +420 -0
  82. euporie/core/tabs/notebook.py +20 -54
  83. euporie/core/utils.py +98 -6
  84. euporie/core/validation.py +1 -1
  85. euporie/core/widgets/_settings.py +188 -0
  86. euporie/core/widgets/cell.py +90 -158
  87. euporie/core/widgets/cell_outputs.py +26 -37
  88. euporie/core/widgets/decor.py +11 -41
  89. euporie/core/widgets/dialog.py +55 -44
  90. euporie/core/widgets/display.py +27 -24
  91. euporie/core/widgets/file_browser.py +5 -26
  92. euporie/core/widgets/forms.py +16 -12
  93. euporie/core/widgets/inputs.py +37 -81
  94. euporie/core/widgets/layout.py +7 -6
  95. euporie/core/widgets/logo.py +49 -0
  96. euporie/core/widgets/menu.py +13 -11
  97. euporie/core/widgets/pager.py +9 -11
  98. euporie/core/widgets/palette.py +6 -6
  99. euporie/hub/app.py +52 -31
  100. euporie/notebook/_commands.py +24 -0
  101. euporie/notebook/_settings.py +107 -0
  102. euporie/notebook/app.py +109 -210
  103. euporie/notebook/filters.py +1 -1
  104. euporie/notebook/tabs/__init__.py +46 -7
  105. euporie/notebook/tabs/_commands.py +714 -0
  106. euporie/notebook/tabs/_settings.py +32 -0
  107. euporie/notebook/tabs/display.py +2 -2
  108. euporie/notebook/tabs/edit.py +12 -7
  109. euporie/notebook/tabs/json.py +3 -3
  110. euporie/notebook/tabs/log.py +1 -18
  111. euporie/notebook/tabs/notebook.py +21 -674
  112. euporie/notebook/widgets/_commands.py +11 -0
  113. euporie/notebook/widgets/_settings.py +19 -0
  114. euporie/notebook/widgets/side_bar.py +14 -34
  115. euporie/preview/_settings.py +104 -0
  116. euporie/preview/app.py +8 -30
  117. euporie/preview/tabs/notebook.py +15 -86
  118. euporie/web/tabs/web.py +4 -6
  119. euporie/web/widgets/webview.py +5 -12
  120. {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/METADATA +11 -15
  121. euporie-2.8.5.dist-info/RECORD +172 -0
  122. {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/WHEEL +1 -1
  123. {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/entry_points.txt +2 -2
  124. {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/licenses/LICENSE +1 -1
  125. euporie/core/launch.py +0 -59
  126. euporie/core/terminal.py +0 -527
  127. euporie-2.8.0.dist-info/RECORD +0 -146
  128. {euporie-2.8.0.data → euporie-2.8.5.data}/data/share/applications/euporie-console.desktop +0 -0
  129. {euporie-2.8.0.data → euporie-2.8.5.data}/data/share/applications/euporie-notebook.desktop +0 -0
@@ -0,0 +1,188 @@
1
+ """Defines widget settings."""
2
+
3
+ from prompt_toolkit.filters import buffer_has_focus
4
+
5
+ from euporie.core.app.current import get_app
6
+ from euporie.core.config import add_setting
7
+
8
+ # euporie.core.widgets.cell_outputs:CellOutputArea
9
+
10
+ add_setting(
11
+ name="wrap_cell_outputs",
12
+ group="euporie.core.widgets.cell_outputs",
13
+ title="wrap cell outputs",
14
+ flags=["--wrap-cell-outputs"],
15
+ type_=bool,
16
+ help_="Wrap cell output text.",
17
+ default=False,
18
+ schema={"type": "boolean"},
19
+ description="""
20
+ Whether text-based cell outputs should be wrapped.
21
+ """,
22
+ cmd_filter=~buffer_has_focus,
23
+ )
24
+
25
+ # euporie,core.widgets.file_browser:FileBrowser
26
+
27
+ add_setting(
28
+ name="show_file_icons",
29
+ group="euporie.core.widgets.file_browser",
30
+ flags=["--show-file-icons"],
31
+ type_=bool,
32
+ title="File icons",
33
+ help_="Show file icons in the file manager",
34
+ default=False,
35
+ schema={
36
+ "type": "boolean",
37
+ },
38
+ description="""
39
+ Whether file icons should be shown in the file manager.
40
+
41
+ These icons exist in the unicode private use area, and may require custom
42
+ fonts such as ``awesome-terminal-fonts`` or ``nerdfonts`` to be installed.
43
+ """,
44
+ )
45
+
46
+ # euporie.core.widgets.inputs:KernelInput
47
+
48
+ add_setting(
49
+ name="line_numbers",
50
+ group="euporie.core.widgets.inputs",
51
+ flags=["--line-numbers"],
52
+ type_=bool,
53
+ help_="Show or hide line numbers",
54
+ default=True,
55
+ description="""
56
+ Whether line numbers are shown by default.
57
+ """,
58
+ hooks=[lambda x: get_app().refresh()],
59
+ )
60
+
61
+ add_setting(
62
+ name="autoformat",
63
+ group="euporie.core.widgets.inputs",
64
+ flags=["--autoformat"],
65
+ type_=bool,
66
+ help_="Automatically re-format code cells when run",
67
+ default=False,
68
+ description="""
69
+ Whether to automatically reformat code cells before they are run.
70
+ """,
71
+ )
72
+
73
+ add_setting(
74
+ name="autocomplete",
75
+ group="euporie.core.widgets.inputs",
76
+ flags=["--autocomplete"],
77
+ type_=bool,
78
+ help_="Provide completions suggestions automatically",
79
+ default=False,
80
+ description="""
81
+ Whether to automatically suggestion completions while typing in code cells.
82
+ """,
83
+ )
84
+
85
+ add_setting(
86
+ name="autosuggest",
87
+ group="euporie.core.widgets.inputs",
88
+ flags=["--autosuggest"],
89
+ type_=bool,
90
+ help_="Provide line completion suggestions",
91
+ default=True,
92
+ description="""
93
+ Whether to automatically suggestion line content while typing in code cells.
94
+ """,
95
+ )
96
+
97
+ add_setting(
98
+ name="autoinspect",
99
+ group="euporie.core.widgets.inputs",
100
+ flags=["--autoinspect"],
101
+ type_=bool,
102
+ help_="Display contextual help automatically",
103
+ default=False,
104
+ description="""
105
+ Whether to automatically display contextual help when navigating through code cells.
106
+ """,
107
+ )
108
+
109
+
110
+ # euporie.core.bars.status:StatusBar
111
+
112
+ add_setting(
113
+ name="show_status_bar",
114
+ group="euporie.core.bars.status",
115
+ flags=["--show-status-bar"],
116
+ type_=bool,
117
+ title="status bar",
118
+ help_="Show the status bar",
119
+ default=True,
120
+ schema={
121
+ "type": "boolean",
122
+ },
123
+ description="""
124
+ Whether the status bar should be shown at the bottom of the screen.
125
+ """,
126
+ )
127
+
128
+ # euporie.core.widgets.decor:
129
+
130
+ add_setting(
131
+ name="show_shadows",
132
+ group="euporie.core.widgets.decor",
133
+ flags=["--show-shadows"],
134
+ type_=bool,
135
+ help_="Show or hide shadows under menus and dialogs",
136
+ default=True,
137
+ description="""
138
+ Sets whether shadows are shown under dialogs and popup-menus.
139
+ """,
140
+ )
141
+
142
+ # euporie.core.widgets.cell
143
+
144
+ add_setting(
145
+ name="show_cell_borders",
146
+ group="euporie.core.widgets.cell",
147
+ title="cell borders",
148
+ flags=["--show-cell-borders"],
149
+ type_=bool,
150
+ help_="Show or hide cell borders.",
151
+ default=False,
152
+ schema={
153
+ "type": "boolean",
154
+ },
155
+ description="""
156
+ Whether cell borders should be drawn for unselected cells.
157
+ """,
158
+ )
159
+
160
+ add_setting(
161
+ name="external_editor",
162
+ group="euporie.core.widgets.cell",
163
+ flags=["--external-editor"],
164
+ type_=str,
165
+ help_="Set the external editor to use.",
166
+ default=None,
167
+ description="""
168
+ A command to run when editing cells externally. The following strings in
169
+ the command will be replaced with values which locate the cell being
170
+ edited:
171
+
172
+ * ``{top}``
173
+ * ``{left}``
174
+ * ``{bottom}``
175
+ * ``{right}``
176
+ * ``{width}``
177
+ * ``{height}``
178
+
179
+ This is useful if you run euporie inside a tmux session, and wish to launch
180
+ your editor in a pop-up pane. This can be achieved by setting this parameter
181
+ to something like the following:
182
+
183
+ .. code-block::
184
+
185
+ "tmux display-popup -x {left} -y {bottom} -w {width} -h {height} -B -E micro"
186
+
187
+ """,
188
+ )
@@ -17,6 +17,7 @@ from prompt_toolkit.completion.base import (
17
17
  _MergedCompleter,
18
18
  )
19
19
  from prompt_toolkit.document import Document
20
+ from prompt_toolkit.filters.app import is_searching
20
21
  from prompt_toolkit.filters.base import Condition
21
22
  from prompt_toolkit.layout.containers import (
22
23
  ConditionalContainer,
@@ -26,10 +27,9 @@ from prompt_toolkit.layout.controls import FormattedTextControl
26
27
  from prompt_toolkit.layout.dimension import Dimension
27
28
  from prompt_toolkit.utils import Event
28
29
 
30
+ from euporie.core.app.current import get_app
29
31
  from euporie.core.border import NoLine, ThickLine, ThinLine
30
32
  from euporie.core.completion import DeduplicateCompleter, LspCompleter
31
- from euporie.core.config import add_setting
32
- from euporie.core.current import get_app
33
33
  from euporie.core.diagnostics import Report
34
34
  from euporie.core.filters import multiple_cells_selected
35
35
  from euporie.core.format import LspFormatter
@@ -48,7 +48,7 @@ if TYPE_CHECKING:
48
48
 
49
49
  from prompt_toolkit.buffer import Buffer
50
50
  from prompt_toolkit.completion.base import Completer
51
- from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple
51
+ from prompt_toolkit.formatted_text.base import StyleAndTextTuples
52
52
 
53
53
  from euporie.core.format import Formatter
54
54
  from euporie.core.inspection import Inspector
@@ -163,6 +163,11 @@ class Cell:
163
163
  """Respond to cursor movements."""
164
164
  # Tell the scrolling container to scroll the cursor into view on the next render
165
165
  weak_self.kernel_tab.page.scroll_to_cursor = True
166
+ if not is_searching():
167
+ if not self.selected:
168
+ weak_self.kernel_tab.select(self.index, scroll=True)
169
+ if not weak_self.kernel_tab.in_edit_mode():
170
+ weak_self.kernel_tab.enter_edit_mode()
166
171
 
167
172
  # Now we generate the main container used to represent a kernel_tab cell
168
173
 
@@ -213,6 +218,23 @@ class Cell:
213
218
 
214
219
  return _inner
215
220
 
221
+ # @lru_cache(maxsize=None)
222
+ # def _cell_border_char(
223
+ # name: str,
224
+ # show_cell_borders: bool,
225
+ # focused: bool,
226
+ # selected: bool,
227
+ # multiple_cells_selected: bool,
228
+ # ) -> str:
229
+ # if show_cell_borders or selected:
230
+ # if focused and multiple_cells_selected:
231
+ # grid = ThickLine.outer
232
+ # else:
233
+ # grid = ThinLine.outer
234
+ # else:
235
+ # grid = NoLine.grid
236
+ # return getattr(grid, name.upper())
237
+
216
238
  self.control = Window(
217
239
  FormattedTextControl(
218
240
  border_char("TOP_LEFT"),
@@ -228,36 +250,11 @@ class Cell:
228
250
  fill = partial(Window, style="class:border")
229
251
 
230
252
  # Create textbox for standard input
231
- def _send_input(buf: Buffer) -> bool:
232
- return False
233
-
234
253
  self.stdin_box = StdInput(weak_self.kernel_tab)
235
254
 
236
- top_border = VSplit(
237
- [
238
- self.control,
239
- ConditionalContainer(
240
- content=fill(
241
- char=border_char("TOP_MID"),
242
- width=lambda: len(weak_self.prompt),
243
- height=1,
244
- ),
245
- filter=show_prompt,
246
- ),
247
- ConditionalContainer(
248
- content=fill(width=1, height=1, char=border_char("TOP_SPLIT")),
249
- filter=show_prompt,
250
- ),
251
- fill(char=border_char("TOP_MID"), height=1),
252
- fill(width=1, height=1, char=border_char("TOP_RIGHT")),
253
- ],
254
- height=1,
255
- )
256
-
257
255
  input_row = ConditionalContainer(
258
256
  VSplit(
259
257
  [
260
- fill(width=1, char=border_char("MID_LEFT")),
261
258
  ConditionalContainer(
262
259
  content=Window(
263
260
  FormattedTextControl(
@@ -270,63 +267,44 @@ class Cell:
270
267
  ("", "\n ", lambda e: NotImplemented),
271
268
  ]
272
269
  ),
273
- width=lambda: len(weak_self.prompt),
270
+ width=lambda: len(weak_self.prompt) + 1,
274
271
  height=Dimension(preferred=1),
275
272
  style="class:input,prompt",
276
273
  ),
277
274
  filter=show_prompt,
278
275
  ),
279
- ConditionalContainer(
280
- fill(width=1, char=border_char("MID_SPLIT")),
281
- filter=show_prompt,
282
- ),
283
276
  ConditionalContainer(self.input_box, filter=~source_hidden),
284
277
  ConditionalContainer(
285
278
  Window(
286
279
  FormattedTextControl(
287
- [
288
- cast(
289
- "OneStyleAndTextTuple",
280
+ cast(
281
+ "StyleAndTextTuples",
282
+ [
283
+ (
284
+ "class:cell,show,inputs,border",
285
+ "▏",
286
+ on_click(self.show_input),
287
+ ),
290
288
  (
291
289
  "class:cell,show,inputs",
292
- " ",
290
+ "…",
293
291
  on_click(self.show_input),
294
292
  ),
295
- )
296
- ]
293
+ (
294
+ "class:cell,show,inputs,border",
295
+ "▕",
296
+ on_click(self.show_input),
297
+ ),
298
+ ],
299
+ )
297
300
  )
298
301
  ),
299
302
  filter=source_hidden,
300
303
  ),
301
- fill(width=1, char=border_char("MID_RIGHT")),
302
304
  ],
303
305
  ),
304
306
  filter=show_input,
305
307
  )
306
- middle_line = ConditionalContainer(
307
- content=VSplit(
308
- [
309
- fill(width=1, height=1, char=border_char("SPLIT_LEFT")),
310
- ConditionalContainer(
311
- content=fill(
312
- char=border_char("SPLIT_MID"),
313
- width=lambda: len(weak_self.prompt),
314
- ),
315
- filter=show_prompt,
316
- ),
317
- ConditionalContainer(
318
- content=fill(
319
- width=1, height=1, char=border_char("SPLIT_SPLIT")
320
- ),
321
- filter=show_prompt,
322
- ),
323
- fill(char=border_char("SPLIT_MID")),
324
- fill(width=1, height=1, char=border_char("SPLIT_RIGHT")),
325
- ],
326
- height=1,
327
- ),
328
- filter=(show_input & show_output) | self.stdin_box.visible,
329
- )
330
308
 
331
309
  outputs_hidden = Condition(
332
310
  lambda: weak_self.json["metadata"]
@@ -338,7 +316,6 @@ class Cell:
338
316
  output_row = ConditionalContainer(
339
317
  VSplit(
340
318
  [
341
- fill(width=1, char=border_char("MID_LEFT")),
342
319
  ConditionalContainer(
343
320
  content=Window(
344
321
  FormattedTextControl(
@@ -351,20 +328,12 @@ class Cell:
351
328
  ("", "\n ", lambda e: NotImplemented),
352
329
  ],
353
330
  ),
354
- width=lambda: len(weak_self.prompt),
331
+ width=lambda: len(weak_self.prompt) + 1,
355
332
  height=Dimension(preferred=1),
356
333
  style="class:output,prompt",
357
334
  ),
358
335
  filter=show_prompt,
359
336
  ),
360
- ConditionalContainer(
361
- content=fill(width=1, char=border_char("MID_SPLIT")),
362
- filter=show_prompt,
363
- ),
364
- ConditionalContainer(
365
- fill(width=1, char=border_char("MID_MID")),
366
- filter=~show_prompt,
367
- ),
368
337
  HSplit(
369
338
  [
370
339
  ConditionalContainer(
@@ -374,51 +343,38 @@ class Cell:
374
343
  ConditionalContainer(
375
344
  Window(
376
345
  FormattedTextControl(
377
- [
378
- cast(
379
- "OneStyleAndTextTuple",
346
+ cast(
347
+ "StyleAndTextTuples",
348
+ [
349
+ (
350
+ "class:cell,show,outputs,border",
351
+ "▏",
352
+ on_click(self.show_output),
353
+ ),
380
354
  (
381
355
  "class:cell,show,outputs",
382
- " ",
356
+ "…",
357
+ on_click(self.show_output),
358
+ ),
359
+ (
360
+ "class:cell,show,outputs,border",
361
+ "▕",
383
362
  on_click(self.show_output),
384
363
  ),
385
- ),
386
- ]
364
+ ],
365
+ )
387
366
  )
388
367
  ),
389
368
  filter=outputs_hidden,
390
369
  ),
391
370
  self.stdin_box,
392
- ]
371
+ ],
393
372
  ),
394
- ConditionalContainer(
395
- fill(width=1, char=border_char("MID_MID")),
396
- filter=~show_prompt,
397
- ),
398
- fill(width=1, char=border_char("MID_RIGHT")),
399
373
  ],
374
+ width=Dimension(min=1),
400
375
  ),
401
376
  filter=show_output | self.stdin_box.visible,
402
377
  )
403
- bottom_border = VSplit(
404
- [
405
- fill(width=1, height=1, char=border_char("BOTTOM_LEFT")),
406
- ConditionalContainer(
407
- content=fill(
408
- char=border_char("BOTTOM_MID"),
409
- width=lambda: len(weak_self.prompt),
410
- ),
411
- filter=show_prompt,
412
- ),
413
- ConditionalContainer(
414
- content=fill(width=1, height=1, char=border_char("BOTTOM_SPLIT")),
415
- filter=show_prompt,
416
- ),
417
- fill(char=border_char("BOTTOM_MID")),
418
- fill(width=1, height=1, char=border_char("BOTTOM_RIGHT")),
419
- ],
420
- height=1,
421
- )
422
378
 
423
379
  def _style() -> str:
424
380
  """Calculate the cell's style given its state."""
@@ -432,11 +388,32 @@ class Cell:
432
388
 
433
389
  self.container = HSplit(
434
390
  [
435
- top_border,
436
- input_row,
437
- middle_line,
438
- output_row,
439
- bottom_border,
391
+ VSplit(
392
+ [
393
+ self.control,
394
+ fill(char=border_char("TOP_MID"), height=1),
395
+ fill(width=1, height=1, char=border_char("TOP_RIGHT")),
396
+ ],
397
+ height=1,
398
+ ),
399
+ VSplit(
400
+ [
401
+ fill(width=1, char=border_char("MID_LEFT")),
402
+ HSplit(
403
+ [input_row, output_row],
404
+ padding=lambda: 1 if show_input() and show_output() else 0,
405
+ ),
406
+ fill(width=1, char=border_char("MID_RIGHT")),
407
+ ]
408
+ ),
409
+ VSplit(
410
+ [
411
+ fill(width=1, height=1, char=border_char("BOTTOM_LEFT")),
412
+ fill(char=border_char("BOTTOM_MID")),
413
+ fill(width=1, height=1, char=border_char("BOTTOM_RIGHT")),
414
+ ],
415
+ height=1,
416
+ ),
440
417
  ],
441
418
  style=_style,
442
419
  )
@@ -604,7 +581,6 @@ class Cell:
604
581
  """Determine the current cell type."""
605
582
  return self.json.get("cell_type", "code")
606
583
 
607
- @property
608
584
  def suffix(self) -> str:
609
585
  """Return the file suffix matching the current cell type."""
610
586
  cell_type = self.cell_type
@@ -720,6 +696,8 @@ class Cell:
720
696
  self.input_box.control._fragment_cache.clear()
721
697
  # Trigger callbacks
722
698
  self.on_change()
699
+ # Flag notebook as modified
700
+ self.kernel_tab.dirty = True
723
701
 
724
702
  @property
725
703
  def path(self) -> Path:
@@ -787,7 +765,7 @@ class Cell:
787
765
  """Set the execution count of the cell."""
788
766
  self.json["execution_count"] = n
789
767
 
790
- def add_output(self, output_json: dict[str, Any]) -> None:
768
+ def add_output(self, output_json: dict[str, Any], own: bool) -> None:
791
769
  """Add a new output to the cell."""
792
770
  # Clear the output if we were previously asked to
793
771
  if self.clear_outputs_on_output:
@@ -947,49 +925,3 @@ class Cell:
947
925
  def close(self) -> None:
948
926
  """Signal that the cell is no longer present in the notebook."""
949
927
  self.on_close()
950
-
951
- # ################################### Settings ####################################
952
-
953
- add_setting(
954
- name="show_cell_borders",
955
- title="cell borders",
956
- flags=["--show-cell-borders"],
957
- type_=bool,
958
- help_="Show or hide cell borders.",
959
- default=False,
960
- schema={
961
- "type": "boolean",
962
- },
963
- description="""
964
- Whether cell borders should be drawn for unselected cells.
965
- """,
966
- )
967
-
968
- add_setting(
969
- name="external_editor",
970
- flags=["--external-editor"],
971
- type_=str,
972
- help_="Set the external editor to use.",
973
- default=None,
974
- description="""
975
- A command to run when editing cells externally. The following strings in
976
- the command will be replaced with values which locate the cell being
977
- edited:
978
-
979
- * ``{top}``
980
- * ``{left}``
981
- * ``{bottom}``
982
- * ``{right}``
983
- * ``{width}``
984
- * ``{height}``
985
-
986
- This is useful if you run euporie inside a tmux session, and wish to launch
987
- your editor in a pop-up pane. This can be achieved by setting this parameter
988
- to something like the following:
989
-
990
- .. code-block::
991
-
992
- "tmux display-popup -x {left} -y {bottom} -w {width} -h {height} -B -E micro"
993
-
994
- """,
995
- )