kiwi-code 0.0.34__tar.gz → 0.0.35__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.
Files changed (53) hide show
  1. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/PKG-INFO +1 -1
  2. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/pyproject.toml +1 -1
  3. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/main.py +40 -33
  4. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/attach_content.py +1 -1
  5. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/command_result.py +1 -1
  6. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/file_browser.py +23 -1
  7. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/help.py +2 -2
  8. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/id_picker.py +11 -11
  9. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/login.py +1 -1
  10. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/runtime_cleanup.py +31 -7
  11. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/runtime_logs.py +28 -0
  12. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/slash_picker.py +2 -1
  13. kiwi_code-0.0.35/tests/test_tui_palette.py +68 -0
  14. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/uv.lock +1 -1
  15. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/.github/workflows/publish.yml +0 -0
  16. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/.github/workflows/test.yml +0 -0
  17. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/.gitignore +0 -0
  18. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/.python-version +0 -0
  19. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/CLAUDE.md +0 -0
  20. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/Makefile +0 -0
  21. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/README.md +0 -0
  22. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/__init__.py +0 -0
  23. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/auth.py +0 -0
  24. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/cli.py +0 -0
  25. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/client.py +0 -0
  26. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/commands.py +0 -0
  27. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/logger.py +0 -0
  28. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/models.py +0 -0
  29. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/runtime_manager.py +0 -0
  30. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_cli/server.py +0 -0
  31. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_runtime/__init__.py +0 -0
  32. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_runtime/__main__.py +0 -0
  33. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_runtime/main.py +0 -0
  34. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_runtime/snake_game/.gitignore +0 -0
  35. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_runtime/snake_game/requirements.txt +0 -0
  36. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/__init__.py +0 -0
  37. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/inline_file_picker.py +0 -0
  38. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/random_words.py +0 -0
  39. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/runtime_agent.py +0 -0
  40. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/__init__.py +0 -0
  41. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/screens/dashboard.py +0 -0
  42. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/slash_commands.py +0 -0
  43. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/status_words.py +0 -0
  44. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/src/kiwi_tui/widgets.py +0 -0
  45. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/test_hello.py +0 -0
  46. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/__init__.py +0 -0
  47. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/conftest.py +0 -0
  48. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/test_cli_help.py +0 -0
  49. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/test_imports.py +0 -0
  50. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/test_reexec_kiwi.py +0 -0
  51. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/test_runtime_log_trimming.py +0 -0
  52. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/test_tokens.py +0 -0
  53. {kiwi_code-0.0.34 → kiwi_code-0.0.35}/tests/test_tui_headless.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kiwi-code
3
- Version: 0.0.34
3
+ Version: 0.0.35
4
4
  Summary: A textual-based terminal user interface application
5
5
  Project-URL: Homepage, https://meetkiwi.ai
6
6
  Project-URL: Repository, https://github.com/jetoslabs/kiwi-code
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kiwi-code"
3
- version = "0.0.34"
3
+ version = "0.0.35"
4
4
  description = "A textual-based terminal user interface application"
5
5
  readme = {file = "README.md", content-type = "text/markdown"}
6
6
  requires-python = ">=3.11,<4.0"
@@ -235,8 +235,8 @@ class AutobotsTUI(App):
235
235
  # Brand color for the kiwi cyan, kept readable on both dark and
236
236
  # light themes. Exposed to CSS as `$brand-cyan` via
237
237
  # `get_css_variables` below.
238
- BRAND_CYAN_DARK = "#5fffff" # bright cyan — pops on dark backgrounds
239
- BRAND_CYAN_LIGHT = "#006d8a" # deeper teal readable on light backgrounds
238
+ BRAND_CYAN_DARK = "#63d8dc" # softened cyan — same feel, less eye strain on dark backgrounds
239
+ BRAND_CYAN_LIGHT = "#0b7084" # slightly softened teal for light backgrounds
240
240
 
241
241
  def _is_dark_theme(self) -> bool:
242
242
  """Best-effort detection of whether the active theme is dark."""
@@ -249,65 +249,72 @@ class AutobotsTUI(App):
249
249
  return bool(getattr(self, "dark", True))
250
250
 
251
251
  def get_css_variables(self) -> dict[str, str]:
252
- """Inject theme-aware brand color and override palette to match kiwi-code UX.
253
-
254
- UX requirement: use a minimal palette (white + kiwi cyan) for UI text and
255
- accents across screens.
256
- """
252
+ """Inject theme-aware brand color and a softer neutral palette."""
257
253
  variables = super().get_css_variables()
258
- cyan = self.BRAND_CYAN_DARK if self._is_dark_theme() else self.BRAND_CYAN_LIGHT
254
+ dark = self._is_dark_theme()
255
+ cyan = self.BRAND_CYAN_DARK if dark else self.BRAND_CYAN_LIGHT
259
256
  variables["brand-cyan"] = cyan
260
257
 
261
- # Two-color palette: foreground text + cyan accents. Keep readability in light theme.
262
- foreground = "#ffffff" if self._is_dark_theme() else "#000000"
258
+ if dark:
259
+ # Match the reference terminal theme more closely:
260
+ # - global background around #1E1E1E
261
+ # - user prompts on a lighter gray chip around #3A3A3A
262
+ bg = "#1E1E1E"
263
+ surface = "#252526"
264
+ raised = "#2D2D2D"
265
+ panel = "#333333"
266
+ foreground = "#F3F3F3"
267
+ user_msg_bg = "#3A3A3A"
268
+ assistant_stream_bg = "#252526"
269
+ else:
270
+ bg = "#f6f7f8"
271
+ surface = "#ffffff"
272
+ raised = "#eef1f3"
273
+ panel = "#dbe7ea"
274
+ foreground = "#101213"
275
+ user_msg_bg = "#f1f3f4"
276
+ assistant_stream_bg = "#f4f6f7"
277
+
278
+ variables["background"] = bg
263
279
  variables["foreground"] = foreground
264
280
  variables["text"] = foreground
265
281
 
266
- # Map most semantic colors to the brand cyan (removes scattered colors).
282
+ # Keep the kiwi cyan for accents while softening the surrounding surfaces.
267
283
  for k in ("primary", "secondary", "accent", "error", "warning", "success"):
268
284
  variables[k] = cyan
269
285
 
270
- # Also align text-* semantic variants used by Markdown, notifications, etc.
271
286
  for k in ("text-warning", "text-error", "text-success"):
272
287
  variables[k] = cyan
273
288
 
274
- # Surfaces and borders: keep background dark, use cyan borders.
275
- bg = variables.get("background", "#000000")
276
- variables["surface"] = bg
277
- variables["boost"] = bg
289
+ variables["surface"] = surface
290
+ variables["boost"] = raised
278
291
  variables["primary-background"] = bg
279
- variables["panel"] = cyan
280
- variables["border"] = cyan
281
- variables["border-blurred"] = cyan
292
+ variables["panel"] = panel
293
+ variables["border"] = panel
294
+ variables["block-hover-background"] = surface
295
+ variables["border-blurred"] = raised
282
296
 
297
+ variables["user-msg-bg"] = user_msg_bg
298
+ variables["assistant-stream-bg"] = assistant_stream_bg
283
299
 
284
- # Chat UX: full-width highlight color for user-message rows.
285
- # Keep it subtle and theme-aware.
286
- variables["user-msg-bg"] = "#333333" if self._is_dark_theme() else "#e8e8e8"
287
- # Chat UX: subtle highlight for the *active streaming* assistant message.
288
- variables["assistant-stream-bg"] = "#222222" if self._is_dark_theme() else "#f2f2f2"
289
- # Scrollbars: cyan thumb, dark track.
290
300
  variables["scrollbar"] = cyan
291
301
  variables["scrollbar-hover"] = cyan
292
302
  variables["scrollbar-active"] = cyan
293
- variables["scrollbar-background"] = bg
294
- variables["scrollbar-background-hover"] = bg
295
- variables["scrollbar-background-active"] = bg
303
+ variables["scrollbar-background"] = surface
304
+ variables["scrollbar-background-hover"] = surface
305
+ variables["scrollbar-background-active"] = raised
296
306
  variables["scrollbar-corner-color"] = bg
297
307
 
298
- # Footer palette variables (Textual 8 uses these).
299
- variables["footer-background"] = bg
308
+ variables["footer-background"] = surface
300
309
  variables["footer-foreground"] = foreground
301
- variables["footer-description-background"] = bg
310
+ variables["footer-description-background"] = surface
302
311
  variables["footer-description-foreground"] = foreground
303
312
 
304
- # Cursor / selection highlight: cyan text on background (no extra colors).
305
313
  variables["block-cursor-foreground"] = cyan
306
314
  variables["block-cursor-background"] = bg
307
315
  variables["block-cursor-blurred-foreground"] = cyan
308
316
  variables["block-cursor-blurred-background"] = bg
309
317
  return variables
310
-
311
318
  def watch_theme(self, theme: str) -> None:
312
319
  """Re-resolve palette variables whenever the user switches theme."""
313
320
  try:
@@ -27,7 +27,7 @@ class AttachContentScreen(ModalScreen[dict]):
27
27
  width: 60;
28
28
  height: auto;
29
29
  max-height: 20;
30
- background: $surface;
30
+ background: $primary-background;
31
31
  border: solid $accent;
32
32
  padding: 1 2;
33
33
  }
@@ -30,7 +30,7 @@ class CommandResultScreen(ModalScreen[None]):
30
30
  #cmdres-container {
31
31
  width: 90;
32
32
  height: 80%;
33
- background: $surface;
33
+ background: $primary-background;
34
34
  border: solid $accent;
35
35
  padding: 1 2;
36
36
  }
@@ -29,7 +29,7 @@ class FileBrowserScreen(ModalScreen[list[str]]):
29
29
  #file-browser-container {
30
30
  width: 96%;
31
31
  height: 95%;
32
- background: $surface;
32
+ background: $primary-background;
33
33
  border: solid $accent;
34
34
  padding: 0 1;
35
35
  }
@@ -75,10 +75,32 @@ class FileBrowserScreen(ModalScreen[list[str]]):
75
75
  #dir-tree {
76
76
  height: 1fr;
77
77
  width: 100%;
78
+ background: $primary-background;
78
79
  border: solid $panel;
79
80
  scrollbar-size: 1 1;
81
+ #dir-tree:focus {
82
+ background-tint: 0%;
83
+ }
84
+
85
+ #dir-tree > .tree--highlight-line {
86
+ background: $surface;
87
+ }
88
+
89
+ #dir-tree > .tree--cursor {
90
+ background: $surface;
91
+ color: $foreground;
92
+ text-style: none;
80
93
  }
81
94
 
95
+ #dir-tree:focus > .tree--cursor {
96
+ background: $boost;
97
+ color: $foreground;
98
+ text-style: bold;
99
+ }
100
+
101
+ }
102
+
103
+
82
104
  #selected-files {
83
105
  height: auto;
84
106
  max-height: 3;
@@ -29,7 +29,7 @@ class HelpScreen(ModalScreen[None]):
29
29
  #help-container {
30
30
  width: 96;
31
31
  height: 80%;
32
- background: $surface;
32
+ background: $primary-background;
33
33
  border: solid $accent;
34
34
  /* Reduce top padding so the title/hint sit closer to the border. */
35
35
  padding: 0 3 1 3;
@@ -58,7 +58,7 @@ class HelpScreen(ModalScreen[None]):
58
58
  height: 1fr;
59
59
  width: 100%;
60
60
  border: solid $panel;
61
- background: $surface;
61
+ background: $primary-background;
62
62
  scrollbar-size: 1 1;
63
63
  /* Keep the list away from the container border for consistent margins. */
64
64
  margin: 0 1;
@@ -101,7 +101,7 @@ class IdPickerScreen(ModalScreen[str]):
101
101
  #idpicker-container {
102
102
  width: 90;
103
103
  height: 90%;
104
- background: $surface;
104
+ background: $primary-background;
105
105
  border: solid $accent;
106
106
  padding: 0 1;
107
107
  }
@@ -111,7 +111,7 @@ class IdPickerScreen(ModalScreen[str]):
111
111
  text-align: center;
112
112
  text-style: bold;
113
113
  color: $primary;
114
- background: $surface;
114
+ background: $primary-background;
115
115
  height: 1;
116
116
  margin-bottom: 0;
117
117
  }
@@ -119,17 +119,17 @@ class IdPickerScreen(ModalScreen[str]):
119
119
  #idpicker-table {
120
120
  height: 1fr;
121
121
  width: 100%;
122
- background: $surface;
122
+ background: $primary-background;
123
123
  /* Slightly dim the row text so it's easier on the eyes. */
124
124
  color: $foreground 80%;
125
125
  border: none;
126
126
  overflow-x: auto;
127
127
  overflow-y: auto;
128
128
  scrollbar-gutter: auto;
129
- scrollbar-background: $surface;
130
- scrollbar-background-hover: $surface;
131
- scrollbar-background-active: $surface;
132
- scrollbar-corner-color: $surface;
129
+ scrollbar-background: $primary-background;
130
+ scrollbar-background-hover: $primary-background;
131
+ scrollbar-background-active: $primary-background;
132
+ scrollbar-corner-color: $primary-background;
133
133
  scrollbar-color: $panel;
134
134
  scrollbar-color-hover: $primary;
135
135
  scrollbar-color-active: $primary;
@@ -163,10 +163,10 @@ class IdPickerScreen(ModalScreen[str]):
163
163
  overflow-x: auto;
164
164
  overflow-y: auto;
165
165
  scrollbar-gutter: auto;
166
- scrollbar-background: $surface;
167
- scrollbar-background-hover: $surface;
168
- scrollbar-background-active: $surface;
169
- scrollbar-corner-color: $surface;
166
+ scrollbar-background: $primary-background;
167
+ scrollbar-background-hover: $primary-background;
168
+ scrollbar-background-active: $primary-background;
169
+ scrollbar-corner-color: $primary-background;
170
170
  scrollbar-color: $panel;
171
171
  scrollbar-color-hover: $primary;
172
172
  scrollbar-color-active: $primary;
@@ -22,7 +22,7 @@ class LoginScreen(Screen):
22
22
 
23
23
  CSS = """
24
24
  LoginScreen {
25
- background: $surface;
25
+ background: $background;
26
26
  layout: vertical;
27
27
  }
28
28
 
@@ -19,6 +19,7 @@ import json
19
19
  from typing import Any
20
20
 
21
21
  from loguru import logger
22
+ from rich.text import Text
22
23
  from textual import events
23
24
  from textual.binding import Binding
24
25
  from textual.screen import ModalScreen
@@ -121,13 +122,29 @@ class RuntimeCleanupScreen(ModalScreen[list[int]]):
121
122
 
122
123
  #runtime-cleanup-help {
123
124
  padding: 1 2;
124
- color: $text;
125
- opacity: 0.9;
125
+ background: $background;
126
+ color: $brand-cyan;
127
+ text-style: bold;
128
+ opacity: 1;
126
129
  }
127
130
 
128
131
  #runtime-list {
129
132
  height: 1fr;
130
133
  width: 100%;
134
+ background: $background;
135
+ color: $foreground;
136
+ border: none;
137
+ }
138
+
139
+ #runtime-list > .option-list--option {
140
+ background: $background;
141
+ color: $foreground;
142
+ }
143
+
144
+ #runtime-list > .option-list--option-highlighted {
145
+ background: $panel;
146
+ color: $brand-cyan;
147
+ text-style: bold;
131
148
  }
132
149
  """
133
150
 
@@ -139,8 +156,8 @@ class RuntimeCleanupScreen(ModalScreen[list[int]]):
139
156
  def compose(self):
140
157
  yield Header(icon="❊")
141
158
  yield Static(
142
- "Select runtimes to kill before exiting.\n"
143
- "Space = toggle kill, Enter = confirm & exit, Esc = keep all.",
159
+ "Interactive runtime cleanup\n"
160
+ "Use ↑/↓ to move, Space to toggle kill, Enter to confirm & exit, Esc to keep all.",
144
161
  id="runtime-cleanup-help",
145
162
  )
146
163
  yield RuntimeCleanupList(id="runtime-list")
@@ -164,12 +181,19 @@ class RuntimeCleanupScreen(ModalScreen[list[int]]):
164
181
  def _option_id_for_row(self, r: RuntimeRow) -> str:
165
182
  return f"{r.kind}:{r.runtime_id}:{r.pid}"
166
183
 
167
- def _format_row(self, r: RuntimeRow) -> str:
184
+ def _format_row(self, r: RuntimeRow) -> Text:
168
185
  kill = "YES" if r.kill else "NO"
169
186
  # Note: kinds come from runtime_agent.list_known_runtimes(): "by-run" or "pending".
170
187
  name = r.name or ("(pending)" if r.kind == "pending" else "(unknown)")
171
- # Avoid Rich markup confusion ("[...]") by not using square brackets.
172
- return f"Kill={kill} {name} | {r.runtime_id} | pid {r.pid} | {r.kind}"
188
+ row = Text()
189
+ try:
190
+ cyan = self.app.get_css_variables().get("brand-cyan", "#63d8dc")
191
+ except Exception:
192
+ cyan = "#63d8dc"
193
+ row.append(f"Kill={kill}", style=f"bold {cyan}")
194
+ row.append(f" {name}", style="bold")
195
+ row.append(f" | {r.runtime_id} | pid {r.pid} | {r.kind}")
196
+ return row
173
197
 
174
198
  def _selected_id(self) -> str | None:
175
199
  lst = self.query_one("#runtime-list", RuntimeCleanupList)
@@ -63,14 +63,34 @@ class RuntimeLogsScreen(Screen):
63
63
  background: $background;
64
64
  }
65
65
 
66
+ RichLog {
67
+ background: $background;
68
+ color: $foreground;
69
+ background-tint: 0%;
70
+ }
71
+
72
+ RichLog:focus {
73
+ background: $background;
74
+ color: $foreground;
75
+ background-tint: 0%;
76
+ }
77
+
66
78
  #runtime-log {
67
79
  height: 1fr;
68
80
  width: 100%;
81
+ background: $background;
82
+ color: $foreground;
83
+ background-tint: 0%;
69
84
  scrollbar-size: 1 1;
85
+ scrollbar-background: $background;
86
+ scrollbar-background-hover: $background;
87
+ scrollbar-background-active: $background;
88
+ scrollbar-corner-color: $background;
70
89
  }
71
90
 
72
91
  #runtime-log-missing {
73
92
  padding: 2 4;
93
+ background: $background;
74
94
  color: $error;
75
95
  }
76
96
  """
@@ -109,6 +129,14 @@ class RuntimeLogsScreen(Screen):
109
129
  except Exception:
110
130
  return
111
131
 
132
+ try:
133
+ bg = self.app.get_css_variables().get("background", "#1E1E1E")
134
+ fg = self.app.get_css_variables().get("foreground", "#F3F3F3")
135
+ log.styles.background = bg
136
+ log.styles.color = fg
137
+ except Exception:
138
+ pass
139
+
112
140
  for line in _tail_lines(self._log_path, max_lines=2000):
113
141
  log.write(Text.from_ansi(line))
114
142
 
@@ -35,7 +35,7 @@ class SlashPickerScreen(ModalScreen[str]):
35
35
  #slash-container {
36
36
  width: 70;
37
37
  height: 80%;
38
- background: $surface;
38
+ background: $primary-background;
39
39
  border: solid $accent;
40
40
  padding: 1 2;
41
41
  }
@@ -53,6 +53,7 @@ class SlashPickerScreen(ModalScreen[str]):
53
53
  height: 1fr;
54
54
  width: 100%;
55
55
  border: solid $panel;
56
+ background: $primary-background;
56
57
  }
57
58
  """
58
59
 
@@ -0,0 +1,68 @@
1
+ from textual.css.stylesheet import Stylesheet
2
+ from pathlib import Path
3
+
4
+ from kiwi_tui.main import AutobotsTUI
5
+ from kiwi_tui.screens.attach_content import AttachContentScreen
6
+ from kiwi_tui.screens.command_result import CommandResultScreen
7
+ from kiwi_tui.screens.file_browser import FileBrowserScreen
8
+ from kiwi_tui.screens.help import HelpScreen
9
+ from kiwi_tui.screens.id_picker import IdPickerScreen
10
+ from kiwi_tui.screens.slash_picker import SlashPickerScreen
11
+
12
+
13
+ def test_tui_dark_palette_vars(isolated_home: Path, monkeypatch) -> None:
14
+ app = AutobotsTUI()
15
+ monkeypatch.setattr(app, "_is_dark_theme", lambda: True)
16
+
17
+ variables = app.get_css_variables()
18
+
19
+ assert variables["background"] == "#1E1E1E"
20
+ assert variables["surface"] == "#252526"
21
+ assert variables["boost"] == "#2D2D2D"
22
+ assert variables["panel"] == "#333333"
23
+ assert variables["foreground"] == "#F3F3F3"
24
+ assert variables["user-msg-bg"] == "#3A3A3A"
25
+ assert variables["assistant-stream-bg"] == "#252526"
26
+
27
+
28
+ def test_tui_light_palette_vars(isolated_home: Path, monkeypatch) -> None:
29
+ app = AutobotsTUI()
30
+ monkeypatch.setattr(app, "_is_dark_theme", lambda: False)
31
+
32
+ variables = app.get_css_variables()
33
+
34
+ assert variables["background"] == "#f6f7f8"
35
+ assert variables["surface"] == "#ffffff"
36
+ assert variables["boost"] == "#eef1f3"
37
+ assert variables["panel"] == "#dbe7ea"
38
+ assert variables["foreground"] == "#101213"
39
+ assert variables["user-msg-bg"] == "#f1f3f4"
40
+ assert variables["assistant-stream-bg"] == "#f4f6f7"
41
+
42
+
43
+ def test_slash_related_modals_use_primary_background() -> None:
44
+ css_blocks = {
45
+ "slash_picker": SlashPickerScreen.CSS,
46
+ "help": HelpScreen.CSS,
47
+ "id_picker": IdPickerScreen.CSS,
48
+ "command_result": CommandResultScreen.CSS,
49
+ "attach_content": AttachContentScreen.CSS,
50
+ "file_browser": FileBrowserScreen.CSS,
51
+ }
52
+
53
+ for name, css in css_blocks.items():
54
+ assert "$primary-background" in css, f"{name} should use the shared modal background"
55
+
56
+
57
+ def test_file_browser_css_parses(isolated_home: Path) -> None:
58
+ app = AutobotsTUI()
59
+ sheet = Stylesheet(variables=app.get_css_variables())
60
+ sheet.add_source(FileBrowserScreen.CSS, read_from=("file_browser.py", "FileBrowserScreen.CSS"))
61
+ sheet.parse()
62
+
63
+
64
+ def test_file_browser_tree_hover_styling_is_softened() -> None:
65
+ css = FileBrowserScreen.CSS
66
+ assert "#dir-tree > .tree--highlight-line" in css
67
+ assert "#dir-tree > .tree--cursor" in css
68
+ assert "background-tint: 0%" in css
@@ -397,7 +397,7 @@ wheels = [
397
397
 
398
398
  [[package]]
399
399
  name = "kiwi-code"
400
- version = "0.0.34"
400
+ version = "0.0.35"
401
401
  source = { editable = "." }
402
402
  dependencies = [
403
403
  { name = "autobots-client" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes