euporie 2.8.5__py3-none-any.whl → 2.8.7__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 (74) hide show
  1. euporie/console/app.py +2 -0
  2. euporie/console/tabs/console.py +27 -17
  3. euporie/core/__init__.py +2 -2
  4. euporie/core/__main__.py +2 -2
  5. euporie/core/_settings.py +7 -2
  6. euporie/core/app/_commands.py +20 -12
  7. euporie/core/app/_settings.py +34 -4
  8. euporie/core/app/app.py +31 -18
  9. euporie/core/bars/command.py +53 -27
  10. euporie/core/bars/search.py +43 -2
  11. euporie/core/border.py +7 -2
  12. euporie/core/comm/base.py +2 -2
  13. euporie/core/comm/ipywidgets.py +3 -3
  14. euporie/core/commands.py +44 -24
  15. euporie/core/completion.py +14 -6
  16. euporie/core/convert/datum.py +7 -7
  17. euporie/core/data_structures.py +20 -1
  18. euporie/core/filters.py +40 -9
  19. euporie/core/format.py +2 -3
  20. euporie/core/ft/html.py +47 -40
  21. euporie/core/graphics.py +199 -31
  22. euporie/core/history.py +15 -5
  23. euporie/core/inspection.py +16 -9
  24. euporie/core/kernel/__init__.py +53 -1
  25. euporie/core/kernel/base.py +571 -0
  26. euporie/core/kernel/{client.py → jupyter.py} +173 -430
  27. euporie/core/kernel/{manager.py → jupyter_manager.py} +4 -3
  28. euporie/core/kernel/local.py +694 -0
  29. euporie/core/key_binding/bindings/basic.py +6 -3
  30. euporie/core/keys.py +26 -25
  31. euporie/core/layout/cache.py +31 -7
  32. euporie/core/layout/containers.py +88 -13
  33. euporie/core/layout/scroll.py +69 -170
  34. euporie/core/log.py +2 -5
  35. euporie/core/path.py +61 -13
  36. euporie/core/style.py +2 -1
  37. euporie/core/suggest.py +155 -74
  38. euporie/core/tabs/__init__.py +12 -4
  39. euporie/core/tabs/_commands.py +76 -0
  40. euporie/core/tabs/_settings.py +16 -0
  41. euporie/core/tabs/base.py +89 -9
  42. euporie/core/tabs/kernel.py +83 -38
  43. euporie/core/tabs/notebook.py +28 -76
  44. euporie/core/utils.py +2 -19
  45. euporie/core/validation.py +8 -8
  46. euporie/core/widgets/_settings.py +19 -2
  47. euporie/core/widgets/cell.py +32 -32
  48. euporie/core/widgets/cell_outputs.py +10 -1
  49. euporie/core/widgets/dialog.py +60 -76
  50. euporie/core/widgets/display.py +2 -2
  51. euporie/core/widgets/forms.py +71 -59
  52. euporie/core/widgets/inputs.py +7 -4
  53. euporie/core/widgets/layout.py +281 -93
  54. euporie/core/widgets/menu.py +56 -16
  55. euporie/core/widgets/palette.py +3 -1
  56. euporie/core/widgets/tree.py +86 -76
  57. euporie/notebook/app.py +35 -16
  58. euporie/notebook/tabs/display.py +2 -2
  59. euporie/notebook/tabs/edit.py +11 -46
  60. euporie/notebook/tabs/json.py +8 -4
  61. euporie/notebook/tabs/notebook.py +26 -8
  62. euporie/preview/tabs/notebook.py +17 -13
  63. euporie/web/__init__.py +1 -0
  64. euporie/web/tabs/__init__.py +14 -0
  65. euporie/web/tabs/web.py +30 -5
  66. euporie/web/widgets/__init__.py +1 -0
  67. euporie/web/widgets/webview.py +5 -4
  68. {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/METADATA +4 -2
  69. {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/RECORD +74 -68
  70. {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/entry_points.txt +1 -1
  71. {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/licenses/LICENSE +1 -1
  72. {euporie-2.8.5.data → euporie-2.8.7.data}/data/share/applications/euporie-console.desktop +0 -0
  73. {euporie-2.8.5.data → euporie-2.8.7.data}/data/share/applications/euporie-notebook.desktop +0 -0
  74. {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/WHEEL +0 -0
@@ -1,18 +1,19 @@
1
- """Contains containers which display children at full height vertially stacked."""
1
+ """Contains containers which display children at full height vertically stacked."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import logging
6
7
  from typing import TYPE_CHECKING, cast
7
8
 
8
9
  from prompt_toolkit.application.current import get_app
10
+ from prompt_toolkit.cache import FastDictCache
9
11
  from prompt_toolkit.filters import is_searching
10
12
  from prompt_toolkit.layout.containers import (
11
13
  Container,
12
14
  ScrollOffsets,
13
15
  Window,
14
16
  WindowRenderInfo,
15
- to_container,
16
17
  )
17
18
  from prompt_toolkit.layout.controls import UIContent
18
19
  from prompt_toolkit.layout.dimension import Dimension, to_dimension
@@ -20,16 +21,12 @@ from prompt_toolkit.mouse_events import MouseEvent, MouseEventType, MouseModifie
20
21
 
21
22
  from euporie.core.layout.cache import CachedContainer
22
23
  from euporie.core.layout.screen import BoundedWritePosition
23
- from euporie.core.utils import run_in_thread_with_context
24
24
 
25
25
  if TYPE_CHECKING:
26
26
  from collections.abc import Sequence
27
27
  from typing import Callable, Literal
28
28
 
29
- from prompt_toolkit.key_binding.key_bindings import (
30
- KeyBindingsBase,
31
- NotImplementedOrNone,
32
- )
29
+ from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
33
30
  from prompt_toolkit.layout.containers import AnyContainer
34
31
  from prompt_toolkit.layout.dimension import AnyDimension
35
32
  from prompt_toolkit.layout.mouse_handlers import MouseHandlers
@@ -67,8 +64,11 @@ class ScrollingContainer(Container):
67
64
  self.children_func = _children_func
68
65
  self._child_cache: dict[int, CachedContainer] = {}
69
66
  self._children: list[CachedContainer] = []
67
+ self._known_sizes_cache: FastDictCache[tuple[int], list[int]] = FastDictCache(
68
+ self._known_sizes
69
+ )
70
70
  self.refresh_children = True
71
- self.pre_rendered = 0.0
71
+ self.pre_rendered: float | None = None
72
72
 
73
73
  self._selected_slice = slice(
74
74
  0, 1
@@ -79,6 +79,7 @@ class ScrollingContainer(Container):
79
79
  self.index_positions: dict[int, int | None] = {}
80
80
 
81
81
  self.last_write_position: WritePosition = BoundedWritePosition(0, 0, 0, 0)
82
+ self.last_total_height = 0
82
83
 
83
84
  self.width = to_dimension(width).preferred
84
85
  self.height = to_dimension(height).preferred
@@ -92,23 +93,35 @@ class ScrollingContainer(Container):
92
93
 
93
94
  def pre_render_children(self, width: int, height: int) -> None:
94
95
  """Render all unrendered children in a background thread."""
96
+ children = self.all_children()
97
+ if not children:
98
+ return
99
+ self.pre_rendered = 0.0
100
+ incr = 1 / len(children)
101
+ app = get_app()
95
102
 
96
- def _render_in_thread() -> None:
97
- """Render children in thread."""
98
- children = self.all_children()
99
- n_children = len(children)
100
- app = get_app()
101
- for i, child in enumerate(children):
102
- if not app._is_running:
103
- return
104
- if isinstance(child, CachedContainer):
105
- child.render(width, height)
106
- self.pre_rendered = i / n_children
107
- app.invalidate()
103
+ def _cb(task: asyncio.Task) -> None:
104
+ """Task callback to update pre-rendering percentage."""
105
+ assert isinstance(self.pre_rendered, float)
106
+ self.pre_rendered += incr
107
+ app.invalidate()
108
+
109
+ tasks = set()
110
+ for child in children:
111
+ if isinstance(child, CachedContainer):
112
+ task = app.create_background_task(
113
+ asyncio.to_thread(child.render, width, height)
114
+ )
115
+ task.add_done_callback(_cb)
116
+ tasks.add(task)
117
+
118
+ async def _finish() -> None:
119
+ await asyncio.gather(*tasks)
108
120
  self.pre_rendered = 1.0
109
121
  app.invalidate()
122
+ # app.exit()
110
123
 
111
- run_in_thread_with_context(_render_in_thread)
124
+ app.create_background_task(_finish())
112
125
 
113
126
  def reset(self) -> None:
114
127
  """Reset the state of this container and all the children."""
@@ -237,7 +250,6 @@ class ScrollingContainer(Container):
237
250
  :py:const:`None`
238
251
 
239
252
  """
240
- # self.refresh_children = True
241
253
  if n > 0:
242
254
  if (
243
255
  min(self.visible_indices) == 0
@@ -353,6 +365,10 @@ class ScrollingContainer(Container):
353
365
  their style down to the windows that they contain.
354
366
  z_index: Used for propagating z_index from parent to child.
355
367
  """
368
+ # Record where the container was last drawn so we can determine if cell outputs
369
+ # are partially obscured
370
+ self.last_write_position = write_position
371
+
356
372
  ypos = write_position.ypos
357
373
  xpos = write_position.xpos
358
374
 
@@ -365,9 +381,6 @@ class ScrollingContainer(Container):
365
381
  # Record children which are currently visible
366
382
  visible_indices = set()
367
383
 
368
- # Ensure we have the right children
369
- all_children = self.all_children()
370
-
371
384
  # Force the selected children to refresh
372
385
  selected_indices = self.selected_indices
373
386
  self._selected_children: list[CachedContainer] = []
@@ -409,12 +422,9 @@ class ScrollingContainer(Container):
409
422
  self.scroll_to_cursor = False
410
423
 
411
424
  # Adjust scrolling offset
412
- if self.scrolling:
413
- heights = [
414
- # Ensure unrendered cells have at least some height
415
- self.get_child(index).height or 1
416
- for index in range(len(self._children))
417
- ]
425
+ heights = self.known_sizes
426
+ total_height = sum(heights)
427
+ if self.scrolling or total_height != self.last_total_height:
418
428
  heights_above = sum(heights[: self._selected_slice.start])
419
429
  new_child_position = self.selected_child_position + self.scrolling
420
430
  # Do not allow scrolling if there is no overflow
@@ -436,6 +446,8 @@ class ScrollingContainer(Container):
436
446
  self.scrolling = min(0, self.scrolling - underscroll)
437
447
  self.selected_child_position += self.scrolling
438
448
 
449
+ self.last_total_height = total_height
450
+
439
451
  # Blit first selected child and those below it that are on screen
440
452
  line = self.selected_child_position
441
453
  for i in range(self._selected_slice.start, len(self._children)):
@@ -545,23 +557,10 @@ class ScrollingContainer(Container):
545
557
 
546
558
  _walk(self)
547
559
 
548
- # Record where the contain was last drawn so we can determine if cell outputs
549
- # are partially obscured
550
- self.last_write_position = write_position
551
-
552
- # Calculate scrollbar info
553
- sizes = self.known_sizes
554
- avg_size = sum(sizes.values()) / len(sizes) if sizes else 0
555
- n_children = len(all_children)
556
- for i in range(n_children):
557
- if i not in sizes:
558
- sizes[i] = int(avg_size)
559
- content_height = max(sum(sizes.values()), 1)
560
-
561
560
  # Mock up a WindowRenderInfo so we can draw a scrollbar margin
562
561
  self.render_info = WindowRenderInfo(
563
562
  window=cast("Window", self),
564
- ui_content=UIContent(line_count=content_height),
563
+ ui_content=UIContent(line_count=max(sum(heights), 1)),
565
564
  horizontal_scroll=0,
566
565
  vertical_scroll=self.vertical_scroll,
567
566
  window_width=available_width,
@@ -577,14 +576,36 @@ class ScrollingContainer(Container):
577
576
  self.scrolling = 0
578
577
 
579
578
  # Trigger pre-rendering of children
580
- if not self.pre_rendered:
579
+ if self.pre_rendered is None:
581
580
  self.pre_render_children(available_width, available_height)
582
581
 
582
+ @property
583
+ def known_sizes(self) -> list[int]:
584
+ """Map of child indices to height values."""
585
+ return self._known_sizes_cache[get_app().render_counter,]
586
+
587
+ def _known_sizes(self, render_counter: int) -> list[int]:
588
+ """Calculate sizes of children once per render cycle."""
589
+ sizes = {}
590
+ missing = set()
591
+ available_width = self.last_write_position.width
592
+ available_height = self.last_write_position.height
593
+ for i, child in enumerate(self._children):
594
+ if isinstance(child, CachedContainer) and child.height:
595
+ sizes[i] = child.preferred_height(
596
+ available_width, available_height
597
+ ).preferred
598
+ else:
599
+ missing.add(i)
600
+ avg = int(sum(sizes.values()) / (len(sizes) or 1))
601
+ sizes.update(dict.fromkeys(missing, avg))
602
+ return [v for k, v in sorted(sizes.items())]
603
+
583
604
  @property
584
605
  def vertical_scroll(self) -> int:
585
606
  """The best guess at the absolute vertical scroll position."""
586
607
  return (
587
- sum(list(self.known_sizes.values())[: self._selected_slice.start])
608
+ sum(self.known_sizes[: self._selected_slice.start])
588
609
  - self.selected_child_position
589
610
  )
590
611
 
@@ -592,7 +613,7 @@ class ScrollingContainer(Container):
592
613
  def vertical_scroll(self, value: int) -> None:
593
614
  """Set the absolute vertical scroll position."""
594
615
  self.selected_child_position = (
595
- sum(list(self.known_sizes.values())[: self._selected_slice.start]) - value
616
+ sum(self.known_sizes[: self._selected_slice.start]) - value
596
617
  )
597
618
 
598
619
  def all_children(self) -> Sequence[Container]:
@@ -712,29 +733,6 @@ class ScrollingContainer(Container):
712
733
  else:
713
734
  self.selected_child_position = new_top
714
735
 
715
- heights = [
716
- self.get_child(index).height or 1 for index in range(len(self._children))
717
- ]
718
- # Do not allow bottom child to scroll above screen bottom
719
- self.selected_child_position += max(
720
- 0,
721
- self.last_write_position.height
722
- - (self.selected_child_position + sum(heights[index:])),
723
- )
724
- # Do not allow top child to scroll below screen top
725
- self.selected_child_position -= max(
726
- 0, self.selected_child_position - sum(heights[:index])
727
- )
728
-
729
- @property
730
- def known_sizes(self) -> dict[int, int]:
731
- """A dictionary mapping child indices to height values."""
732
- sizes = {}
733
- for i, child in enumerate(self._children):
734
- if isinstance(child, CachedContainer) and child.height:
735
- sizes[i] = child.height
736
- return sizes
737
-
738
736
  def _scroll_up(self) -> None:
739
737
  """Scroll up one line: for compatibility with :py:class:`Window`."""
740
738
  self.scroll(1)
@@ -742,102 +740,3 @@ class ScrollingContainer(Container):
742
740
  def _scroll_down(self) -> None:
743
741
  """Scroll down one line: for compatibility with :py:class:`Window`."""
744
742
  self.scroll(-1)
745
-
746
-
747
- class PrintingContainer(Container):
748
- """A container which displays all it's children in a vertical list."""
749
-
750
- def __init__(
751
- self,
752
- children: Callable | Sequence[AnyContainer],
753
- width: AnyDimension = None,
754
- key_bindings: KeyBindingsBase | None = None,
755
- ) -> None:
756
- """Initiate the container."""
757
- self.width = width
758
- self.rendered = False
759
- self._children = children
760
- self.key_bindings = key_bindings
761
-
762
- def get_key_bindings(self) -> KeyBindingsBase | None:
763
- """Return the container's key bindings."""
764
- return self.key_bindings
765
-
766
- @property
767
- def children(self) -> Sequence[AnyContainer]:
768
- """Return the container's children."""
769
- children = self._children() if callable(self._children) else self._children
770
- return children or [Window()]
771
-
772
- def get_children(self) -> list[Container]:
773
- """Return a list of all child containers."""
774
- return [to_container(child) for child in self.children]
775
-
776
- def write_to_screen(
777
- self,
778
- screen: PtkScreen,
779
- mouse_handlers: MouseHandlers,
780
- write_position: WritePosition,
781
- parent_style: str,
782
- erase_bg: bool,
783
- z_index: int | None,
784
- ) -> None:
785
- """Render the container to a `Screen` instance.
786
-
787
- All children are rendered vertically in sequence.
788
-
789
- Args:
790
- screen: The :class:`~prompt_toolkit.layout.screen.Screen` class to which
791
- the output has to be written.
792
- mouse_handlers: :class:`prompt_toolkit.layout.mouse_handlers.MouseHandlers`.
793
- write_position: A :class:`prompt_toolkit.layout.screen.WritePosition` object
794
- defining where this container should be drawn.
795
- erase_bg: If true, the background will be erased prior to drawing.
796
- parent_style: Style string to pass to the :class:`.Window` object. This will
797
- be applied to all content of the windows. :class:`.VSplit` and
798
- :class:`prompt_toolkit.layout.containers.HSplit` can use it to pass
799
- their style down to the windows that they contain.
800
- z_index: Used for propagating z_index from parent to child.
801
-
802
- """
803
- xpos = write_position.xpos
804
- ypos = write_position.ypos
805
-
806
- children = self.get_children()
807
- for child in children:
808
- height = child.preferred_height(write_position.width, 999999).preferred
809
- child.write_to_screen(
810
- screen,
811
- mouse_handlers,
812
- BoundedWritePosition(xpos, ypos, write_position.width, height),
813
- parent_style,
814
- erase_bg,
815
- z_index,
816
- )
817
- ypos += height
818
-
819
- def preferred_height(self, width: int, max_available_height: int) -> Dimension:
820
- """Return the preferred height, equal to the sum of the child heights."""
821
- return Dimension(
822
- min=1,
823
- preferred=sum(
824
- [
825
- c.preferred_height(width, max_available_height).preferred
826
- for c in self.get_children()
827
- ]
828
- ),
829
- )
830
-
831
- def preferred_width(self, max_available_width: int) -> Dimension:
832
- """Calculate and returns the desired width for this container."""
833
- if self.width is not None:
834
- dim = to_dimension(self.width).preferred
835
- return Dimension(max=dim, preferred=dim)
836
- else:
837
- return Dimension(max_available_width)
838
-
839
- def reset(self) -> None:
840
- """Reet the state of this container and all the children.
841
-
842
- Does nothing as this container is used for dumping output.
843
- """
euporie/core/log.py CHANGED
@@ -340,7 +340,7 @@ class StdoutFormatter(FtFormatter):
340
340
 
341
341
 
342
342
  class stdout_to_log:
343
- """A decorator which captures standard output and logs it."""
343
+ """A context manager which captures standard output and logs it."""
344
344
 
345
345
  def __init__(
346
346
  self, log: logging.Logger, output: str = "Literal['stdout','stderr']"
@@ -506,10 +506,7 @@ def setup_logs(config: Config | None = None) -> None:
506
506
 
507
507
  # Update log_config based on additional config dict provided
508
508
  if config.log_config:
509
- import json
510
-
511
- extra_config = json.loads(config.log_config)
512
- dict_merge(log_config, extra_config)
509
+ dict_merge(log_config, config.log_config)
513
510
 
514
511
  # Configure the logger
515
512
  # Pytype used TypedDicts to validate the dictionary structure, but I cannot get
euporie/core/path.py CHANGED
@@ -9,36 +9,72 @@ from typing import TYPE_CHECKING
9
9
  import upath
10
10
  from aiohttp.client_reqrep import ClientResponse
11
11
  from fsspec.implementations.http import HTTPFileSystem as FsHTTPFileSystem
12
+ from fsspec.implementations.http import get_client
12
13
  from fsspec.registry import register_implementation as fs_register_implementation
13
14
  from upath import UPath
14
15
 
15
16
  if TYPE_CHECKING:
17
+ import asyncio
16
18
  from collections.abc import Mapping
17
19
  from os import PathLike
18
- from typing import Any
20
+ from typing import Any, Callable
19
21
 
20
-
21
- log = logging.getLogger(__name__)
22
-
23
-
24
- # Monkey-patch `aiohttp` to not raise exceptions on non-200 responses
22
+ import aiohttp
25
23
 
26
24
 
27
- def _raise_for_status(self: ClientResponse) -> None:
28
- """Monkey-patch :py:class:`aiohttp.ClientResponse` not to raise for any status."""
29
-
25
+ log = logging.getLogger(__name__)
30
26
 
31
- setattr(ClientResponse, "raise_for_status", _raise_for_status) # noqa B010
32
27
 
28
+ class NoRaiseClientResponse(ClientResponse):
29
+ """An ``aiohttp`` client response which does not raise on >=400 status responses."""
33
30
 
34
- # Define and register non-raising HTTP filesystem implementation for fsspec
31
+ @property
32
+ def ok(self) -> bool:
33
+ """Returns ``True`` if ``status`` is probably renderable."""
34
+ return self.status not in {405}
35
35
 
36
36
 
37
37
  class HTTPFileSystem(FsHTTPFileSystem):
38
- """A :py:class:`HTTPFileSystem` which does not raise exceptions on 404 errors."""
38
+ """A HTTP filesystem implementation which does not raise on errors."""
39
+
40
+ def __init__(
41
+ self,
42
+ simple_links: bool = True,
43
+ block_size: int | None = None,
44
+ same_scheme: bool = True,
45
+ size_policy: None = None,
46
+ cache_type: str = "bytes",
47
+ cache_options: dict[str, Any] | None = None,
48
+ asynchronous: bool = False,
49
+ loop: asyncio.AbstractEventLoop | None = None,
50
+ client_kwargs: dict[str, Any] | None = None,
51
+ get_client: Callable[..., aiohttp.ClientSession] = get_client,
52
+ encoded: bool = False,
53
+ **storage_options: Any,
54
+ ) -> None:
55
+ """Defaults to using :py:mod:`NoRaiseClientResponse` for responses."""
56
+ client_kwargs = {
57
+ "response_class": NoRaiseClientResponse,
58
+ **(client_kwargs or {}),
59
+ }
60
+ super().__init__(
61
+ simple_links=simple_links,
62
+ block_size=block_size,
63
+ same_scheme=same_scheme,
64
+ size_policy=size_policy,
65
+ cache_type=cache_type,
66
+ cache_options=cache_options,
67
+ asynchronous=asynchronous,
68
+ loop=loop,
69
+ client_kwargs=client_kwargs,
70
+ get_client=get_client,
71
+ encoded=encoded,
72
+ **storage_options,
73
+ )
39
74
 
40
75
  def _raise_not_found_for_status(self, response: ClientResponse, url: str) -> None:
41
76
  """Do not raise an exception for 404 errors."""
77
+ response.raise_for_status()
42
78
 
43
79
 
44
80
  fs_register_implementation("http", HTTPFileSystem, clobber=True)
@@ -63,8 +99,15 @@ class UntitledPath(upath.core.UPath):
63
99
  return False
64
100
 
65
101
 
66
- def parse_path(path: str | PathLike, resolve: bool = True) -> Path:
102
+ def parse_path(path: str | PathLike, resolve: bool | None = None) -> Path:
67
103
  """Parse and resolve a path."""
104
+ if resolve is None:
105
+ from upath.implementations.http import HTTPPath
106
+
107
+ if isinstance(path, HTTPPath):
108
+ resolve = True
109
+ else:
110
+ resolve = False
68
111
  if not isinstance(path, Path):
69
112
  path = UPath(path)
70
113
  try:
@@ -76,4 +119,9 @@ def parse_path(path: str | PathLike, resolve: bool = True) -> Path:
76
119
  path = path.resolve()
77
120
  except (AttributeError, NotImplementedError, Exception):
78
121
  log.info("Path %s not resolvable", path)
122
+ else:
123
+ try:
124
+ path = path.absolute()
125
+ except NotImplementedError:
126
+ pass
79
127
  return path
euporie/core/style.py CHANGED
@@ -141,6 +141,7 @@ IPYWIDGET_STYLE = [
141
141
  # ("ipywidget danger border right selection", "fg:ansibrightred"),
142
142
  # ("ipywidget danger border bottom selection", "fg:ansibrightred"),
143
143
  ("ipywidget text text-area", "fg:black bg:white"),
144
+ ("ipywidget text text-area disabled", "fg:#888888"),
144
145
  ("ipywidget text text-area focused", "fg:black bg:white"),
145
146
  ("ipywidget text placeholder", "fg:#AAAAAA bg:white"),
146
147
  ("ipywidget text border right", "fg:#E9E7E3"),
@@ -436,7 +437,7 @@ def build_style(
436
437
  # Logo
437
438
  "logo": "fg:#dd0000",
438
439
  # Pattern
439
- "pattern": f"fg:{cp.bg.more(0.075)}",
440
+ "pattern": f"fg:{cp.bg.more(0.05)}",
440
441
  # Chrome
441
442
  "chrome": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
442
443
  "tab-padding": f"fg:{cp.bg.more(0.2)} bg:{cp.bg.base}",