webbee 0.3.27__tar.gz → 0.3.28__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 (118) hide show
  1. {webbee-0.3.27 → webbee-0.3.28}/CHANGELOG.md +7 -0
  2. {webbee-0.3.27 → webbee-0.3.28}/PKG-INFO +1 -1
  3. {webbee-0.3.27 → webbee-0.3.28}/pyproject.toml +1 -1
  4. webbee-0.3.28/src/webbee/__init__.py +1 -0
  5. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/home_view.py +51 -20
  6. {webbee-0.3.27 → webbee-0.3.28}/tests/test_home_view.py +13 -0
  7. webbee-0.3.27/src/webbee/__init__.py +0 -1
  8. {webbee-0.3.27 → webbee-0.3.28}/.github/workflows/publish.yml +0 -0
  9. {webbee-0.3.27 → webbee-0.3.28}/.gitignore +0 -0
  10. {webbee-0.3.27 → webbee-0.3.28}/LICENSE +0 -0
  11. {webbee-0.3.27 → webbee-0.3.28}/README.md +0 -0
  12. {webbee-0.3.27 → webbee-0.3.28}/docs/superpowers/plans/2026-07-21-interactive-home-dashboard.md +0 -0
  13. {webbee-0.3.27 → webbee-0.3.28}/install.sh +0 -0
  14. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/account.py +0 -0
  15. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/active_sessions.py +0 -0
  16. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/banner_art.py +0 -0
  17. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/boot.py +0 -0
  18. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/checkpoints.py +0 -0
  19. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/cli.py +0 -0
  20. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/clipboard.py +0 -0
  21. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/coding_context.py +0 -0
  22. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/commands.py +0 -0
  23. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/config.py +0 -0
  24. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/consent.py +0 -0
  25. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/details.py +0 -0
  26. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/events.py +0 -0
  27. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/frames.py +0 -0
  28. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/home.py +0 -0
  29. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/http.py +0 -0
  30. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/instance_lock.py +0 -0
  31. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/__init__.py +0 -0
  32. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/chunker.py +0 -0
  33. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/embed.py +0 -0
  34. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/graph.py +0 -0
  35. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/indexer.py +0 -0
  36. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/models.py +0 -0
  37. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/query.py +0 -0
  38. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/service.py +0 -0
  39. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/store.py +0 -0
  40. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/vectors.py +0 -0
  41. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/watch.py +0 -0
  42. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/mode_store.py +0 -0
  43. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/newtab_mode.py +0 -0
  44. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/output_pane.py +0 -0
  45. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/queue_panel.py +0 -0
  46. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/reflow.py +0 -0
  47. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/remote.py +0 -0
  48. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/render.py +0 -0
  49. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/repl.py +0 -0
  50. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/repo.py +0 -0
  51. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/selection.py +0 -0
  52. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/session.py +0 -0
  53. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/sessions.py +0 -0
  54. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/sizing.py +0 -0
  55. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/slots.py +0 -0
  56. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/steer.py +0 -0
  57. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/stream.py +0 -0
  58. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tabs.py +0 -0
  59. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/thread.py +0 -0
  60. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/todo_panel.py +0 -0
  61. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tokens.py +0 -0
  62. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tools.py +0 -0
  63. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tui.py +0 -0
  64. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/update.py +0 -0
  65. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/urlopen.py +0 -0
  66. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/wallet.py +0 -0
  67. {webbee-0.3.27 → webbee-0.3.28}/src/webbee/worktrees.py +0 -0
  68. {webbee-0.3.27 → webbee-0.3.28}/tests/__init__.py +0 -0
  69. {webbee-0.3.27 → webbee-0.3.28}/tests/conftest.py +0 -0
  70. {webbee-0.3.27 → webbee-0.3.28}/tests/test_account.py +0 -0
  71. {webbee-0.3.27 → webbee-0.3.28}/tests/test_active_sessions.py +0 -0
  72. {webbee-0.3.27 → webbee-0.3.28}/tests/test_checkpoints.py +0 -0
  73. {webbee-0.3.27 → webbee-0.3.28}/tests/test_cli.py +0 -0
  74. {webbee-0.3.27 → webbee-0.3.28}/tests/test_clipboard.py +0 -0
  75. {webbee-0.3.27 → webbee-0.3.28}/tests/test_commands.py +0 -0
  76. {webbee-0.3.27 → webbee-0.3.28}/tests/test_config.py +0 -0
  77. {webbee-0.3.27 → webbee-0.3.28}/tests/test_cpc_contract_stable.py +0 -0
  78. {webbee-0.3.27 → webbee-0.3.28}/tests/test_details.py +0 -0
  79. {webbee-0.3.27 → webbee-0.3.28}/tests/test_events.py +0 -0
  80. {webbee-0.3.27 → webbee-0.3.28}/tests/test_freeze_fix.py +0 -0
  81. {webbee-0.3.27 → webbee-0.3.28}/tests/test_home.py +0 -0
  82. {webbee-0.3.27 → webbee-0.3.28}/tests/test_instance_lock.py +0 -0
  83. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_chunker.py +0 -0
  84. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_embed.py +0 -0
  85. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_graph.py +0 -0
  86. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_indexer.py +0 -0
  87. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_no_numpy.py +0 -0
  88. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_query.py +0 -0
  89. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_service.py +0 -0
  90. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_store.py +0 -0
  91. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_vectors.py +0 -0
  92. {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_watch.py +0 -0
  93. {webbee-0.3.27 → webbee-0.3.28}/tests/test_marathon.py +0 -0
  94. {webbee-0.3.27 → webbee-0.3.28}/tests/test_mode_store.py +0 -0
  95. {webbee-0.3.27 → webbee-0.3.28}/tests/test_newtab_mode.py +0 -0
  96. {webbee-0.3.27 → webbee-0.3.28}/tests/test_packaging.py +0 -0
  97. {webbee-0.3.27 → webbee-0.3.28}/tests/test_reflow.py +0 -0
  98. {webbee-0.3.27 → webbee-0.3.28}/tests/test_render.py +0 -0
  99. {webbee-0.3.27 → webbee-0.3.28}/tests/test_repl.py +0 -0
  100. {webbee-0.3.27 → webbee-0.3.28}/tests/test_repo.py +0 -0
  101. {webbee-0.3.27 → webbee-0.3.28}/tests/test_session.py +0 -0
  102. {webbee-0.3.27 → webbee-0.3.28}/tests/test_sessions.py +0 -0
  103. {webbee-0.3.27 → webbee-0.3.28}/tests/test_sizing.py +0 -0
  104. {webbee-0.3.27 → webbee-0.3.28}/tests/test_slots.py +0 -0
  105. {webbee-0.3.27 → webbee-0.3.28}/tests/test_steer.py +0 -0
  106. {webbee-0.3.27 → webbee-0.3.28}/tests/test_stream.py +0 -0
  107. {webbee-0.3.27 → webbee-0.3.28}/tests/test_tabs.py +0 -0
  108. {webbee-0.3.27 → webbee-0.3.28}/tests/test_thread.py +0 -0
  109. {webbee-0.3.27 → webbee-0.3.28}/tests/test_todo_panel.py +0 -0
  110. {webbee-0.3.27 → webbee-0.3.28}/tests/test_tokens.py +0 -0
  111. {webbee-0.3.27 → webbee-0.3.28}/tests/test_tools.py +0 -0
  112. {webbee-0.3.27 → webbee-0.3.28}/tests/test_tui.py +0 -0
  113. {webbee-0.3.27 → webbee-0.3.28}/tests/test_tui_hardening.py +0 -0
  114. {webbee-0.3.27 → webbee-0.3.28}/tests/test_update.py +0 -0
  115. {webbee-0.3.27 → webbee-0.3.28}/tests/test_urlopen.py +0 -0
  116. {webbee-0.3.27 → webbee-0.3.28}/tests/test_version.py +0 -0
  117. {webbee-0.3.27 → webbee-0.3.28}/tests/test_wallet.py +0 -0
  118. {webbee-0.3.27 → webbee-0.3.28}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.28
4
+
5
+ The Home screen scrolls now. When Home has more than fills the window — a tall
6
+ stack of tabs, or command output — scroll it with the mouse wheel or
7
+ PageUp/PageDown, like any other view. Output from a command run on Home (for
8
+ example `/help`) shows inline at the bottom and the view jumps to it.
9
+
3
10
  ## 0.3.27
4
11
 
5
12
  Home dashboard polish.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webbee
3
- Version: 0.3.27
3
+ Version: 0.3.28
4
4
  Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
5
5
  Project-URL: Homepage, https://imperal.io
6
6
  Project-URL: Documentation, https://docs.imperal.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.3.27"
3
+ version = "0.3.28"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1 @@
1
+ __version__ = "0.3.28"
@@ -293,20 +293,24 @@ class HomeView:
293
293
 
294
294
  def __init__(self, *, slots, actions: "HomeActions",
295
295
  data: "HomeData | None" = None, width: int = 100, out_pane=None):
296
- from prompt_toolkit.layout.containers import HSplit, Window
296
+ from prompt_toolkit.layout.containers import Window
297
297
  from prompt_toolkit.layout.controls import FormattedTextControl
298
298
  self.slots = slots
299
299
  self.actions = actions
300
300
  self.data = data if data is not None else HomeData()
301
- # An OutputPane below the dashboard is Home's command/output region:
302
- # `_say` and slash-command output (e.g. /help, gated-action notes)
303
- # print into its captured console so a command run while Home is
304
- # active still shows an honest answer, and the dock's OutputPane spy +
305
- # ticker (dump/reflow/scroll/flash/edge_tick/forward_mouse) keep
306
- # working. The dock builds it via `tui.OutputPane` and passes it
307
- # (spy-visible, created first = Home); a bare fallback covers direct
308
- # unit use. `self.console` IS that pane's console it drives both the
309
- # command output AND the dashboard's column-width math.
301
+ # Home is ONE ordinary scrollable Window (NOT dont_extend_height, NOT
302
+ # an HSplit): when its content is taller than the viewport, prompt_
303
+ # toolkit's own Window mouse handler scrolls it on the wheel (content
304
+ # fragment handlers return NotImplemented for SCROLL, so it falls
305
+ # through to Window._mouse_handler), and pageup/pagedown route here via
306
+ # `scroll()`. `_say`/slash-command output is rendered INLINE at the
307
+ # bottom of the SAME scrollable content (see `_fragments`), so it
308
+ # scrolls with everything and never gets squeezed off a short terminal.
309
+ # `out_pane` (built via `tui.OutputPane` by the dock, created first =
310
+ # Home for the spy) stays purely as the CAPTURE backend: `_say` prints
311
+ # into its console and `dump()` reads it back; its own window is NOT in
312
+ # the layout. `self.console` IS that pane's console — it drives both the
313
+ # captured command output AND the dashboard's column-width math.
310
314
  if out_pane is None:
311
315
  from webbee.output_pane import OutputPane
312
316
  out_pane = OutputPane(width=width)
@@ -316,37 +320,53 @@ class HomeView:
316
320
  self._hover_id: "str | None" = None
317
321
  self._model: "HomeModel | None" = None
318
322
  self.control = FormattedTextControl(self._fragments, focusable=True, show_cursor=False)
319
- self._dash = Window(content=self.control, wrap_lines=False,
320
- always_hide_cursor=True, dont_extend_height=True)
321
- self.window = HSplit([self._dash, out_pane.window])
323
+ self.window = Window(content=self.control, wrap_lines=False, always_hide_cursor=True)
322
324
 
323
325
  # ---- OutputPane-compatible surface (ticker/layout duck-type) ----------
324
- # All delegate to the composed output pane so the dock's ticker/layout
325
- # drive Home's command-output region exactly as they do a session pane.
326
326
  def reflow(self, new_width: int) -> None:
327
+ # Keep the capture console's width in sync (drives the dashboard's
328
+ # column math) and repaint; the Window re-lays-out at the new size.
327
329
  self._out.reflow(new_width)
330
+ self._invalidate()
328
331
 
329
332
  def edge_tick(self) -> None:
330
- self._out.edge_tick()
333
+ return None
331
334
 
332
335
  def flash(self) -> str:
333
336
  return self._out.flash()
334
337
 
335
338
  def scroll(self, delta: int) -> None:
336
- self._out.scroll(delta)
339
+ # pageup/pagedown (tui routes here). Move the Window's own vertical
340
+ # scroll, clamped to the scrollable range; render_info is present once
341
+ # the window has been drawn at least one frame.
342
+ win = self.window
343
+ info = getattr(win, "render_info", None)
344
+ if info is None:
345
+ return
346
+ max_scroll = max(0, info.content_height - info.window_height)
347
+ win.vertical_scroll = min(max_scroll, max(0, win.vertical_scroll + delta))
348
+ self._invalidate()
337
349
 
338
350
  @property
339
351
  def _view_h(self) -> int:
340
- return self._out._view_h
352
+ info = getattr(self.window, "render_info", None)
353
+ return info.window_height if info is not None else 20
341
354
 
342
355
  def forward_mouse(self, ev, clamp: str = "bottom") -> bool:
343
- return self._out.forward_mouse(ev, clamp)
356
+ return False
344
357
 
345
358
  def dump(self) -> str:
346
359
  return self._out.dump()
347
360
 
348
361
  def notify(self) -> None:
349
- self._out.notify()
362
+ # New captured output landed (a `_say` note / command reply). Reveal
363
+ # it: jump the scroll to the bottom (PT clamps on the next render) so
364
+ # the answer is visible, then repaint.
365
+ try:
366
+ self.window.vertical_scroll = 10 ** 7
367
+ except Exception:
368
+ pass
369
+ self._invalidate()
350
370
 
351
371
  def _invalidate(self) -> None:
352
372
  try:
@@ -579,6 +599,17 @@ class HomeView:
579
599
  lines += [[("", "")]] + security_lines()
580
600
  lines += footer_lines()
581
601
 
602
+ # `_say` / slash-command output, captured in the backend pane, rendered
603
+ # INLINE at the very bottom of the same scrollable content (ANSI →
604
+ # fragments, styling preserved). `notify()` jumps the scroll here so a
605
+ # command's answer is visible; it scrolls with the rest of Home.
606
+ dump = self._out.dump()
607
+ if dump.strip():
608
+ from prompt_toolkit.formatted_text import ANSI, to_formatted_text
609
+ lines.append([("", "")])
610
+ lines.append([("class:home.header", " Output ")])
611
+ lines.append(list(to_formatted_text(ANSI(dump.rstrip("\n")))))
612
+
582
613
  out = []
583
614
  for ln in lines:
584
615
  out.extend(ln)
@@ -302,3 +302,16 @@ def test_home_renders_webbee_code_logo_and_title():
302
302
  text = "".join(f[1] for f in hv._fragments())
303
303
  assert "_____" in text # a slice of the WEBBEE_CODE ascii banner
304
304
  assert "◆ Home" in text
305
+
306
+
307
+ def test_captured_output_renders_inline_and_notify_reveals_it():
308
+ # Home is one scrollable window; _say/command output is captured in the
309
+ # backend pane and rendered INLINE at the bottom, and notify() jumps the
310
+ # scroll to reveal it.
311
+ hv, _ = _view()
312
+ hv.console.print("hello-inline-output")
313
+ text = "".join(f[1] for f in hv._fragments())
314
+ assert "hello-inline-output" in text
315
+ assert "Output" in text
316
+ hv.notify()
317
+ assert hv.window.vertical_scroll >= 10 ** 6
@@ -1 +0,0 @@
1
- __version__ = "0.3.27"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes