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.
- {webbee-0.3.27 → webbee-0.3.28}/CHANGELOG.md +7 -0
- {webbee-0.3.27 → webbee-0.3.28}/PKG-INFO +1 -1
- {webbee-0.3.27 → webbee-0.3.28}/pyproject.toml +1 -1
- webbee-0.3.28/src/webbee/__init__.py +1 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/home_view.py +51 -20
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_home_view.py +13 -0
- webbee-0.3.27/src/webbee/__init__.py +0 -1
- {webbee-0.3.27 → webbee-0.3.28}/.github/workflows/publish.yml +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/.gitignore +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/LICENSE +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/README.md +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/docs/superpowers/plans/2026-07-21-interactive-home-dashboard.md +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/install.sh +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/account.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/active_sessions.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/banner_art.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/boot.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/checkpoints.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/cli.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/clipboard.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/coding_context.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/commands.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/config.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/consent.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/details.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/events.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/frames.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/home.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/http.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/instance_lock.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/chunker.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/embed.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/graph.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/indexer.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/models.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/query.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/service.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/store.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/intel/watch.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/mode_store.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/newtab_mode.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/output_pane.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/queue_panel.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/reflow.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/remote.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/render.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/repl.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/repo.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/selection.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/session.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/sessions.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/sizing.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/slots.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/steer.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/stream.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tabs.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/thread.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/todo_panel.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tokens.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tools.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/tui.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/update.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/urlopen.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/wallet.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/src/webbee/worktrees.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/__init__.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/conftest.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_account.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_active_sessions.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_checkpoints.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_cli.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_clipboard.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_commands.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_config.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_details.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_events.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_freeze_fix.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_home.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_instance_lock.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_chunker.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_embed.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_graph.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_indexer.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_query.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_service.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_store.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_vectors.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_intel_watch.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_marathon.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_mode_store.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_newtab_mode.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_packaging.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_reflow.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_render.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_repl.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_repo.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_session.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_sessions.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_sizing.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_slots.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_steer.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_stream.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_tabs.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_thread.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_todo_panel.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_tokens.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_tools.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_tui.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_tui_hardening.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_update.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_urlopen.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_version.py +0 -0
- {webbee-0.3.27 → webbee-0.3.28}/tests/test_wallet.py +0 -0
- {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.
|
|
@@ -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
|
|
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
|
-
#
|
|
302
|
-
#
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
#
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
#
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
{webbee-0.3.27 → webbee-0.3.28}/docs/superpowers/plans/2026-07-21-interactive-home-dashboard.md
RENAMED
|
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
|
|
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
|