lazyopencode 0.1.0__py3-none-any.whl → 0.2.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.
@@ -0,0 +1,130 @@
1
+ """Level selector bar for copy operations."""
2
+
3
+ from textual.app import ComposeResult
4
+ from textual.binding import Binding
5
+ from textual.message import Message
6
+ from textual.widget import Widget
7
+ from textual.widgets import Static
8
+
9
+ from lazyopencode.models.customization import ConfigLevel
10
+
11
+
12
+ class LevelSelector(Widget):
13
+ """Bottom bar for selecting target configuration level."""
14
+
15
+ BINDINGS = [
16
+ Binding("1", "select_global", "Global", show=False),
17
+ Binding("2", "select_project", "Project", show=False),
18
+ Binding("escape", "cancel", "Cancel", show=False),
19
+ ]
20
+
21
+ DEFAULT_CSS = """
22
+ LevelSelector {
23
+ dock: bottom;
24
+ height: 3;
25
+ border: solid $accent;
26
+ padding: 0 1;
27
+ margin-bottom: 1;
28
+ display: none;
29
+ background: $surface;
30
+ }
31
+
32
+ LevelSelector.visible {
33
+ display: block;
34
+ }
35
+
36
+ LevelSelector:focus {
37
+ border: double $accent;
38
+ }
39
+
40
+ LevelSelector #prompt {
41
+ width: 100%;
42
+ text-align: center;
43
+ }
44
+
45
+ LevelSelector .key {
46
+ color: $accent;
47
+ text-style: bold;
48
+ }
49
+ """
50
+
51
+ can_focus = True
52
+
53
+ class LevelSelected(Message):
54
+ """Emitted when a level is selected."""
55
+
56
+ def __init__(self, level: ConfigLevel) -> None:
57
+ self.level = level
58
+ super().__init__()
59
+
60
+ class SelectionCancelled(Message):
61
+ """Emitted when selection is cancelled."""
62
+
63
+ pass
64
+
65
+ def __init__(
66
+ self,
67
+ name: str | None = None,
68
+ id: str | None = None,
69
+ classes: str | None = None,
70
+ ) -> None:
71
+ """Initialize LevelSelector."""
72
+ super().__init__(name=name, id=id, classes=classes)
73
+ self._available_levels: list[ConfigLevel] = []
74
+ self._customization_name: str = ""
75
+
76
+ def compose(self) -> ComposeResult:
77
+ """Compose the level selector bar."""
78
+ yield Static("", id="prompt")
79
+
80
+ def show(
81
+ self,
82
+ available_levels: list[ConfigLevel],
83
+ customization_name: str = "",
84
+ ) -> None:
85
+ """Show the level selector and focus it."""
86
+ self._available_levels = available_levels
87
+ self._customization_name = customization_name
88
+ self._update_prompt()
89
+ self.add_class("visible")
90
+ self.focus()
91
+
92
+ def hide(self) -> None:
93
+ """Hide the level selector."""
94
+ self.remove_class("visible")
95
+
96
+ def _update_prompt(self) -> None:
97
+ """Update the prompt text based on available levels."""
98
+ name_part = f'"{self._customization_name}" ' if self._customization_name else ""
99
+
100
+ options = []
101
+ if ConfigLevel.GLOBAL in self._available_levels:
102
+ options.append("\\[1] Global")
103
+ if ConfigLevel.PROJECT in self._available_levels:
104
+ options.append("\\[2] Project")
105
+
106
+ options_text = " ".join(options)
107
+ prompt_widget = self.query_one("#prompt", Static)
108
+ prompt_widget.update(f"Copy {name_part}to: {options_text} \\[Esc] Cancel")
109
+
110
+ def action_select_global(self) -> None:
111
+ """Select global level."""
112
+ if ConfigLevel.GLOBAL in self._available_levels:
113
+ self.hide()
114
+ self.post_message(self.LevelSelected(ConfigLevel.GLOBAL))
115
+
116
+ def action_select_project(self) -> None:
117
+ """Select project level."""
118
+ if ConfigLevel.PROJECT in self._available_levels:
119
+ self.hide()
120
+ self.post_message(self.LevelSelected(ConfigLevel.PROJECT))
121
+
122
+ def action_cancel(self) -> None:
123
+ """Cancel selection."""
124
+ self.hide()
125
+ self.post_message(self.SelectionCancelled())
126
+
127
+ @property
128
+ def is_visible(self) -> bool:
129
+ """Check if the level selector is visible."""
130
+ return self.has_class("visible")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lazyopencode
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A lazygit-style TUI for visualizing OpenCode customizations
5
5
  Project-URL: Homepage, https://github.com/nikiforovall/lazyopencode
6
6
  Project-URL: Repository, https://github.com/nikiforovall/lazyopencode
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Topic :: Software Development :: User Interfaces
20
20
  Requires-Python: >=3.11
21
21
  Requires-Dist: pathspec>=0.12.0
22
+ Requires-Dist: pyperclip>=1.9.0
22
23
  Requires-Dist: pyyaml>=6.0
23
24
  Requires-Dist: rich>=13.0.0
24
25
  Requires-Dist: textual>=0.89.0
@@ -34,7 +35,7 @@ Description-Content-Type: text/markdown
34
35
 
35
36
  A keyboard-driven TUI for managing OpenCode customizations.
36
37
 
37
- <!-- ![LazyOpenCode Screenshot](docs/screenshot.png) -->
38
+ ![LazyOpenCode Screenshot](artifacts/demo.png)
38
39
 
39
40
  ## Features
40
41
 
@@ -58,34 +59,36 @@ pip install lazyopencode
58
59
 
59
60
  ## Keyboard Shortcuts
60
61
 
61
- | Key | Action |
62
- | --------- | -------------- |
63
- | `q` | Quit |
64
- | `1` | Commands panel |
65
- | `2` | Agents panel |
66
- | `3` | Skills panel |
67
- | `4` | Rules panel |
68
- | `5` | MCPs panel |
69
- | `6` | Plugins panel |
70
- | `j` / `↓` | Move down |
71
- | `k` / `↑` | Move up |
72
- | `Tab` | Next panel |
73
- | `e` | Edit selected |
74
- | `ctrl`+`u` | User Config |
75
- | `?` | Help |
62
+ | Key | Action |
63
+ | ---------- | ---------------- |
64
+ | `j` / `↓` | Move down |
65
+ | `k` / `↑` | Move up |
66
+ | `Tab` | Next panel |
67
+ | `[` / `]` | Prev/Next view |
68
+ | `1`-`7` | Jump to panel |
69
+ | `a` | All filter |
70
+ | `g` | Global filter |
71
+ | `p` | Project filter |
72
+ | `/` | Search |
73
+ | `e` | Edit selected |
74
+ | `r` | Refresh |
75
+ | `ctrl+u` | User Config |
76
+ | `?` | Help |
77
+ | `q` | Quit |
76
78
 
77
79
  ## Configuration Paths
78
80
 
79
81
  LazyOpenCode discovers customizations from:
80
82
 
81
- | Type | Global | Project |
82
- | -------- | ------------------------------ | -------------------- |
83
- | Commands | `~/.config/opencode/command/` | `.opencode/command/` |
84
- | Agents | `~/.config/opencode/agent/` | `.opencode/agent/` |
85
- | Skills | `~/.config/opencode/skill/` | `.opencode/skill/` |
86
- | Rules | `~/.config/opencode/AGENTS.md` | `AGENTS.md` |
87
- | MCPs | `opencode.json` | `opencode.json` |
88
- | Plugins | `~/.config/opencode/plugin/` | `.opencode/plugin/` |
83
+ | Type | Global | Project |
84
+ | -------- | ---------------------------------- | -------------------- |
85
+ | Commands | `~/.config/opencode/command/` | `.opencode/command/` |
86
+ | Agents | `~/.config/opencode/agent/` | `.opencode/agent/` |
87
+ | Skills | `~/.config/opencode/skill/` | `.opencode/skill/` |
88
+ | Rules | `~/.config/opencode/AGENTS.md` | `AGENTS.md` |
89
+ | MCPs | `~/.config/opencode/opencode.json` | `opencode.json` |
90
+ | Tools | `~/.config/opencode/tool/` | `.opencode/tool/` |
91
+ | Plugins | `~/.config/opencode/plugin/` | `.opencode/plugin/` |
89
92
 
90
93
  ## Inspired By
91
94
 
@@ -1,17 +1,25 @@
1
- lazyopencode/__init__.py,sha256=p0PzeS7KA58kGpxWMzOVMHIPAXDXOPg-bMiF9Jxtp2c,1303
1
+ lazyopencode/__init__.py,sha256=roprUA5NQ0OsqJrG-XYGgCJYST5_M3oQNKSVrpDZM1E,1558
2
2
  lazyopencode/__main__.py,sha256=8sGwTbHrRZyACIYWqpMRP76sq7fTBeJSj-VOAd6hUKE,120
3
- lazyopencode/_version.py,sha256=5jwwVncvCiTnhOedfkzzxmxsggwmTBORdFL_4wq0ZeY,704
4
- lazyopencode/app.py,sha256=JiEn-Dv8OvIH8EoGsO1WX_qk2jPyBgt81aNx60IZzfo,11330
5
- lazyopencode/bindings.py,sha256=N6e-U23aFuMDlOjqtsGvoLl4_qGq-loafgKZ2US7A7M,1214
3
+ lazyopencode/_version.py,sha256=Dg8AmJomLVpjKL6prJylOONZAPRtB86LOce7dorQS_A,704
4
+ lazyopencode/app.py,sha256=PmVBhvRbggITuC2KNKP3DiOcaYiThQXmD3Dlc8UYODg,15886
5
+ lazyopencode/bindings.py,sha256=QOPYO3BnxuriNd4lqHaCl1v_-CjogcL_o9Ibpa1eqDs,1342
6
6
  lazyopencode/themes.py,sha256=9KsyWlk9XeAupAdUTj785riWuDlQMnERjD8DUzogCPc,713
7
7
  lazyopencode/mixins/filtering.py,sha256=m7G5NN-dvphPCnnNc8GyJY3p6ijmFxnr4GrzukYMoII,988
8
- lazyopencode/mixins/help.py,sha256=sZdoa0kmdfGE-81hNQAQKPY-ycH2f-2widAiWvH5iiM,1980
9
- lazyopencode/mixins/navigation.py,sha256=ES8Oc1bf3A7MmtyLVkynSlQWSrXsiI7cDTyfLRYSGYE,6850
8
+ lazyopencode/mixins/help.py,sha256=4OnRoUlaVPM_81IDyG04YgJquZwv_wGAkmom-5ebyUk,2059
9
+ lazyopencode/mixins/navigation.py,sha256=RHA5J9Wn9nYjDmRglMagvn94OFTti8q3yuYI6bNAgcM,6600
10
10
  lazyopencode/models/__init__.py,sha256=X2Wlkc6IS1nQF0JNTvqZNNLh6YI1SH-D5YP4xpWL-rc,298
11
- lazyopencode/models/customization.py,sha256=Tom492ns5ZqGFGURMKNRugqwt43GZrGBuHfa5BUx_sw,2910
11
+ lazyopencode/models/customization.py,sha256=INO9NkEu5vBKYZ8vxdWB5QuQ6QqKVSzuT4jNjwmkx8g,4089
12
12
  lazyopencode/services/__init__.py,sha256=aCVZJecnMeupVU-T6EhR34dFu295XwJP9dqZ8MKWOJE,146
13
- lazyopencode/services/discovery.py,sha256=5helVx3gye6axuVClKpRIGMMLOjTA9Je7oSkQDx0aOk,12608
13
+ lazyopencode/services/discovery.py,sha256=w7qv_Sk83zpmEZJnP1HCM8JxaJDHqyQE59zQP3prrJc,13510
14
14
  lazyopencode/services/gitignore_filter.py,sha256=9QFKrUsSm0kSSYXzJ3_Zx440Pk1T6CSo6eaoPP5Pthk,3155
15
+ lazyopencode/services/writer.py,sha256=eRDCUTzmoQ92cD5gB4nGEWE9l03SqRY53UFKm4dyGFc,4443
16
+ lazyopencode/services/claude_code/__init__.py,sha256=lMcoJR-x2eHR6ddvL3YbYT4xonGjKKFUdHwjBwEcaNg,310
17
+ lazyopencode/services/claude_code/discovery.py,sha256=gRzX_HUWPI5tdkhV4JmIeh539QkhpEeSXM3a4fI2rT0,5780
18
+ lazyopencode/services/claude_code/plugin_loader.py,sha256=KriqpCeYVR_8NExdgAKtMASz2r2krYNiKNxEYwtKhnE,5572
19
+ lazyopencode/services/claude_code/parsers/__init__.py,sha256=L0SleU_DoacF-rN-crQZ6Rkf5BWvnEhpSGhVHg504ic,326
20
+ lazyopencode/services/claude_code/parsers/agent.py,sha256=YeqmBinlqViAWb2FGxnsAnfQOFoTGXLVJ_pUWvnvqBQ,1754
21
+ lazyopencode/services/claude_code/parsers/command.py,sha256=E8H0X0lMaEzT2PIpNQKhtydf3chw7NKzNh-F9B121Cw,2429
22
+ lazyopencode/services/claude_code/parsers/skill.py,sha256=nD7JxkJW_hHrvA0Nfb5t7OKx4p1TBIdlfn90-KfGVak,3805
15
23
  lazyopencode/services/parsers/__init__.py,sha256=zav6eIpqF5TGFp2jlGSOFZtCicHyltA--cVMycox9M0,4344
16
24
  lazyopencode/services/parsers/agent.py,sha256=zaw8y3RZsPFOu8DsoM3b2XPGzp0BnQJcbx86mEIYUxM,2885
17
25
  lazyopencode/services/parsers/command.py,sha256=-Mg3sq_4TLkIvXKzIX4lB7KvU0e4T0TA-5lpwhcUgBE,2977
@@ -22,16 +30,17 @@ lazyopencode/services/parsers/skill.py,sha256=WMKIv1HJSdx0vt0KJ9YtQ0oB1G5GOOC_sm
22
30
  lazyopencode/services/parsers/tool.py,sha256=sL3N63cim68R_wo5s0WONnnnvYxrK-tyiGCIc7guMV0,1800
23
31
  lazyopencode/styles/app.tcss,sha256=fAAWxQ7ePJ7oYfMUoU3ffC3PfJ9hGocHhPF7nNMf-qU,2658
24
32
  lazyopencode/widgets/__init__.py,sha256=syhZGjXKhKjg75iSU1xgRqiRWV-S-Jh8ipd0g1-qkMs,497
25
- lazyopencode/widgets/app_footer.py,sha256=kapIzxk2F5h6NgoDLYtkbFSTU8le5YtvDQcJ_Mribsw,2200
33
+ lazyopencode/widgets/app_footer.py,sha256=blVdZobd4XXJ4qxQDsXyP6tm52gSvloImqFa_x5L8Bo,2217
26
34
  lazyopencode/widgets/combined_panel.py,sha256=vEopdSAmThctwAlawwY88u5Bmz4XNMnDY9u2YnCLco8,12113
27
35
  lazyopencode/widgets/detail_pane.py,sha256=fFTGZhaJXP4PGAUItv4rYMiDkTbdqRXG0NTI6ooS_kc,11296
28
36
  lazyopencode/widgets/filter_input.py,sha256=rJp7Y0NVJSj7c-wIS8ZPXgkxTtYU4Gp-DXJOFYOhV5U,2518
37
+ lazyopencode/widgets/level_selector.py,sha256=rO3Ik87rDLmlsw4eXkUE5ps7rrrv7v_KO1_HwlfMTHA,3733
29
38
  lazyopencode/widgets/status_panel.py,sha256=k4lx4GrHlVUvt6t32_bz2WOWvZdPNawdSMhmXUZYpc8,2127
30
39
  lazyopencode/widgets/type_panel.py,sha256=wg2D5diKvdrwco7pvcUnF9WPOgTveiH1qt0O82gkqa4,19313
31
40
  lazyopencode/widgets/helpers/__init__.py,sha256=-jGrYtTMl9rmX8Q5Vod_oAuYaMl6Yh5JRwv8AFKQcwk,135
32
41
  lazyopencode/widgets/helpers/rendering.py,sha256=lgxzVsnH6fgTZss2x4n8hKYYtDN35YmFMD83impkpH8,576
33
- lazyopencode-0.1.0.dist-info/METADATA,sha256=N4Yk0AS2rFw_B4mmAt4_b1dReGYk2uDuv3evMdPUTEM,3353
34
- lazyopencode-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
35
- lazyopencode-0.1.0.dist-info/entry_points.txt,sha256=PPVT4NhHce2hFuMj-NQTSN9xQIM2jwb0_ojMEcOpPJ0,60
36
- lazyopencode-0.1.0.dist-info/licenses/LICENSE,sha256=KY9Pw3pDaLTe2eiMvUoj09VHhE3i8N1Le0d01JrG_3Q,1069
37
- lazyopencode-0.1.0.dist-info/RECORD,,
42
+ lazyopencode-0.2.0.dist-info/METADATA,sha256=YVZPl7bgvnynIOWAJyctsF-xzXiZfxwzfWrOZ11ja4s,3558
43
+ lazyopencode-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
44
+ lazyopencode-0.2.0.dist-info/entry_points.txt,sha256=PPVT4NhHce2hFuMj-NQTSN9xQIM2jwb0_ojMEcOpPJ0,60
45
+ lazyopencode-0.2.0.dist-info/licenses/LICENSE,sha256=KY9Pw3pDaLTe2eiMvUoj09VHhE3i8N1Le0d01JrG_3Q,1069
46
+ lazyopencode-0.2.0.dist-info/RECORD,,