webbee 0.3.26__tar.gz → 0.3.27__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.26 → webbee-0.3.27}/CHANGELOG.md +10 -0
- {webbee-0.3.26 → webbee-0.3.27}/PKG-INFO +1 -1
- {webbee-0.3.26 → webbee-0.3.27}/pyproject.toml +1 -1
- webbee-0.3.27/src/webbee/__init__.py +1 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/home_view.py +23 -2
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/repl.py +16 -8
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_home.py +4 -4
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_home_view.py +23 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_repl.py +27 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_tui.py +1 -1
- webbee-0.3.26/src/webbee/__init__.py +0 -1
- {webbee-0.3.26 → webbee-0.3.27}/.github/workflows/publish.yml +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/.gitignore +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/LICENSE +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/README.md +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/docs/superpowers/plans/2026-07-21-interactive-home-dashboard.md +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/install.sh +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/account.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/active_sessions.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/banner_art.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/boot.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/checkpoints.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/cli.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/clipboard.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/coding_context.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/commands.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/config.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/consent.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/details.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/events.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/frames.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/home.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/http.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/instance_lock.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/chunker.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/embed.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/graph.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/indexer.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/models.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/query.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/service.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/store.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/intel/watch.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/mode_store.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/newtab_mode.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/output_pane.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/queue_panel.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/reflow.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/remote.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/render.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/repo.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/selection.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/session.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/sessions.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/sizing.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/slots.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/steer.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/stream.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/tabs.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/thread.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/todo_panel.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/tokens.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/tools.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/tui.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/update.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/urlopen.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/wallet.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/src/webbee/worktrees.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/__init__.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/conftest.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_account.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_active_sessions.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_checkpoints.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_cli.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_clipboard.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_commands.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_config.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_details.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_events.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_freeze_fix.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_instance_lock.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_chunker.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_embed.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_graph.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_indexer.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_query.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_service.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_store.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_vectors.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_intel_watch.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_marathon.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_mode_store.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_newtab_mode.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_packaging.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_reflow.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_render.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_repo.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_session.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_sessions.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_sizing.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_slots.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_steer.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_stream.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_tabs.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_thread.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_todo_panel.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_tokens.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_tools.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_tui_hardening.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_update.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_urlopen.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_version.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_wallet.py +0 -0
- {webbee-0.3.26 → webbee-0.3.27}/tests/test_worktrees.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.27
|
|
4
|
+
|
|
5
|
+
Home dashboard polish.
|
|
6
|
+
|
|
7
|
+
- The Webbee Code logo is back at the top of Home.
|
|
8
|
+
- Home now shows when your plan renews (or expires).
|
|
9
|
+
- "Top up credits" opens the right billing page.
|
|
10
|
+
- The new-tab mode you pick on Home is now honored no matter how you open
|
|
11
|
+
the tab — typing, the "+" button, and Ctrl+T all agree.
|
|
12
|
+
|
|
3
13
|
## 0.3.26
|
|
4
14
|
|
|
5
15
|
The Home tab is now an interactive dashboard — a little website inside your
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.27"
|
|
@@ -445,10 +445,17 @@ class HomeView:
|
|
|
445
445
|
nick = getattr(a, "nickname", "") or ""
|
|
446
446
|
L.append([("class:home.value", f" @{nick}" if nick else " (no nickname)")])
|
|
447
447
|
plan = getattr(a, "plan", "") or ""
|
|
448
|
+
status = getattr(a, "plan_status", "") or ""
|
|
448
449
|
if plan:
|
|
449
|
-
status = getattr(a, "plan_status", "") or ""
|
|
450
450
|
tag = f" ({status})" if status and status != "active" else ""
|
|
451
451
|
L.append([("class:home.dim", f" {plan} plan{tag}")])
|
|
452
|
+
renews = getattr(a, "plan_renews", "") or ""
|
|
453
|
+
if renews:
|
|
454
|
+
# `plan_renews` is the formatted subscription expires_at: an
|
|
455
|
+
# auto-renewing (active/trialing) plan RENEWS then, a
|
|
456
|
+
# cancelled/lapsing one EXPIRES then.
|
|
457
|
+
verb = "renews" if status in ("active", "trialing", "") else "expires"
|
|
458
|
+
L.append([("class:home.dim", f" {verb} {renews}")])
|
|
452
459
|
email = getattr(a, "email", "") or ""
|
|
453
460
|
if email:
|
|
454
461
|
L.append([("class:home.dim", f" {_mask_email(email)}")])
|
|
@@ -544,7 +551,21 @@ class HomeView:
|
|
|
544
551
|
"· Ctrl+T new tab · Alt+N switch")])
|
|
545
552
|
return L
|
|
546
553
|
|
|
547
|
-
|
|
554
|
+
# Header: the Webbee Code ASCII logo (bee-yellow), then a centered
|
|
555
|
+
# "◆ Home" — restored (it was on the pre-dashboard Home and Valentin
|
|
556
|
+
# wants it kept). The whole art block is centered by ONE offset (max
|
|
557
|
+
# line width) so its shape is preserved; degrades to left-aligned when
|
|
558
|
+
# the terminal is narrower than the art.
|
|
559
|
+
from webbee.banner_art import WEBBEE_CODE
|
|
560
|
+
art = WEBBEE_CODE.rstrip("\n").split("\n")
|
|
561
|
+
art_w = max((len(ln) for ln in art), default=0)
|
|
562
|
+
art_pad = max(0, (width - art_w) // 2)
|
|
563
|
+
lines = [[("", "")]]
|
|
564
|
+
for ln in art:
|
|
565
|
+
lines.append([("", " " * art_pad), ("class:home.header", ln)] if ln else [("", "")])
|
|
566
|
+
title_pad = max(0, (width - len("◆ Home")) // 2)
|
|
567
|
+
lines.append([("", " " * title_pad), ("class:home.header", "◆ Home")])
|
|
568
|
+
lines.append([("", "")])
|
|
548
569
|
if two_column(width):
|
|
549
570
|
colw = max(20, (width // 2) - 3)
|
|
550
571
|
lines += _side_by_side(you_lines(), wallet_lines(), colw)
|
|
@@ -465,7 +465,7 @@ async def _isolate_workspace(workspace: str, resources: WorkspaceResources, *,
|
|
|
465
465
|
async def _make_session_slot(cfg, token_provider, workspace, mode, *, resources: WorkspaceResources,
|
|
466
466
|
shared_client, agent_factory, intel_factory, shadow_factory,
|
|
467
467
|
first: bool, account=None, _with_replayed: bool = False,
|
|
468
|
-
slot_id: "str | None" = None):
|
|
468
|
+
slot_id: "str | None" = None, mode_pinned: bool = False):
|
|
469
469
|
"""Builds ONE dock tab's atomic {agent, sink, pane} triple (map §6 —
|
|
470
470
|
created together, a sink must never point at another slot's pane/
|
|
471
471
|
console). `shared_client` isn't consumed here yet — reserved for Task 3's
|
|
@@ -533,7 +533,12 @@ async def _make_session_slot(cfg, token_provider, workspace, mode, *, resources:
|
|
|
533
533
|
return compute_repo_key(find_repo_root(workspace))
|
|
534
534
|
repo_key_task = asyncio.ensure_future(asyncio.to_thread(_repo_key))
|
|
535
535
|
|
|
536
|
-
|
|
536
|
+
# `mode_pinned` = the caller's mode is an EXPLICIT user choice (Home's
|
|
537
|
+
# "new-tab mode" setting, threaded through _open_new_tab/_home_input) and
|
|
538
|
+
# MUST win over this repo's remembered mode — otherwise a new tab opened
|
|
539
|
+
# from Home silently ignores the mode you just picked. Only the boot
|
|
540
|
+
# first-tab (mode_pinned=False) resumes the per-repo remembered mode.
|
|
541
|
+
effective_mode = mode if mode_pinned else (await asyncio.to_thread(load_mode, workspace) or mode)
|
|
537
542
|
width, _height = get_size(None) # pre-app: same fallback tui.run_session's own sizing uses
|
|
538
543
|
pane = tui.OutputPane(width=width)
|
|
539
544
|
sink = RichSink(console=pane.console, on_output=pane.notify)
|
|
@@ -585,9 +590,11 @@ async def _home_input(text: str, *, slots: SlotManager, cfg, token_provider, mod
|
|
|
585
590
|
"""Home's Enter path (Task 6, wired into `tui.run_session` as
|
|
586
591
|
`home_input=`): typing a task on Home opens a session tab in one motion
|
|
587
592
|
— the SAME `_make_session_slot`/switch path the `/new` command uses
|
|
588
|
-
(always `first=False
|
|
589
|
-
|
|
590
|
-
|
|
593
|
+
(always `first=False`; the `mode` passed in is Home's explicit "new-tab
|
|
594
|
+
mode" setting and is PINNED — `mode_pinned=True` — so it wins over this
|
|
595
|
+
repo's remembered mode, exactly like the Ctrl+T / + / `/new` path, and a
|
|
596
|
+
new tab honors the mode you picked on Home instead of silently resuming a
|
|
597
|
+
stale per-repo default) — then runs the typed text as that NEW
|
|
591
598
|
slot's own first turn, explicitly against `new_slot` (FIX1: `run_turn` --
|
|
592
599
|
repl's `_run_turn` -- is slot-explicit now, never resolves
|
|
593
600
|
`slots.active()` internally). FIX3: the turn is started through
|
|
@@ -616,7 +623,8 @@ async def _home_input(text: str, *, slots: SlotManager, cfg, token_provider, mod
|
|
|
616
623
|
new_slot = await _make_session_slot(
|
|
617
624
|
cfg, token_provider, ws, mode, resources=resources,
|
|
618
625
|
shared_client=shared_client, agent_factory=agent_factory,
|
|
619
|
-
intel_factory=intel_factory, shadow_factory=shadow_factory, first=False
|
|
626
|
+
intel_factory=intel_factory, shadow_factory=shadow_factory, first=False,
|
|
627
|
+
mode_pinned=True)
|
|
620
628
|
idx = slots.add(new_slot)
|
|
621
629
|
ui_hooks.get("switch", slots.switch)(idx)
|
|
622
630
|
if spawn_poller is not None:
|
|
@@ -1194,7 +1202,7 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
1194
1202
|
cfg, token_provider, ws, state.get("new_tab_mode") or mode, resources=resources,
|
|
1195
1203
|
shared_client=shared_client, agent_factory=agent_factory,
|
|
1196
1204
|
intel_factory=intel_factory, shadow_factory=shadow_factory,
|
|
1197
|
-
first=False)
|
|
1205
|
+
first=False, mode_pinned=True)
|
|
1198
1206
|
idx = slots.add(new_slot)
|
|
1199
1207
|
ui_hooks.get("switch", slots.switch)(idx)
|
|
1200
1208
|
_spawn_slot_poller(new_slot)
|
|
@@ -1256,7 +1264,7 @@ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None,
|
|
|
1256
1264
|
ui_hooks.get("switch", slots.switch)(idx)
|
|
1257
1265
|
|
|
1258
1266
|
def _home_top_up() -> None:
|
|
1259
|
-
url = urlopen.open_url(f"{cfg.panel_url}/billing")
|
|
1267
|
+
url = urlopen.open_url(f"{cfg.panel_url}/ext/billing")
|
|
1260
1268
|
home_view.data.notice = f"top up at {url}"
|
|
1261
1269
|
home_view.notify()
|
|
1262
1270
|
|
|
@@ -216,7 +216,7 @@ def test_home_input_creates_slot_switches_and_runs_first_turn():
|
|
|
216
216
|
created = {}
|
|
217
217
|
|
|
218
218
|
async def fake_make_session_slot(cfg, tp, ws, mode, *, resources, shared_client,
|
|
219
|
-
agent_factory, intel_factory, shadow_factory, first):
|
|
219
|
+
agent_factory, intel_factory, shadow_factory, first, mode_pinned=False):
|
|
220
220
|
created["ws"] = ws
|
|
221
221
|
created["first"] = first
|
|
222
222
|
slot = _session_slot(workspace=ws, label="new")
|
|
@@ -268,7 +268,7 @@ def test_home_input_new_slot_sink_records_echo(monkeypatch):
|
|
|
268
268
|
slots.add(_home_slot(workspace="/cwd"))
|
|
269
269
|
|
|
270
270
|
async def fake_make_session_slot(cfg, tp, ws, mode, *, resources, shared_client,
|
|
271
|
-
agent_factory, intel_factory, shadow_factory, first):
|
|
271
|
+
agent_factory, intel_factory, shadow_factory, first, mode_pinned=False):
|
|
272
272
|
s = _session_slot(workspace=ws)
|
|
273
273
|
s.sink = _EchoSink()
|
|
274
274
|
return s
|
|
@@ -303,7 +303,7 @@ def test_home_input_dock_path_echoes_exactly_once():
|
|
|
303
303
|
slots.add(_home_slot(workspace="/cwd"))
|
|
304
304
|
|
|
305
305
|
async def fake_make_session_slot(cfg, tp, ws, mode, *, resources, shared_client,
|
|
306
|
-
agent_factory, intel_factory, shadow_factory, first):
|
|
306
|
+
agent_factory, intel_factory, shadow_factory, first, mode_pinned=False):
|
|
307
307
|
s = _session_slot(workspace=ws)
|
|
308
308
|
s.sink = _EchoSink()
|
|
309
309
|
return s
|
|
@@ -381,7 +381,7 @@ def test_home_input_uses_new_tab_mode_when_provided():
|
|
|
381
381
|
seen = {}
|
|
382
382
|
|
|
383
383
|
async def fake_make_session_slot(cfg, tp, ws, mode, *, resources, shared_client,
|
|
384
|
-
agent_factory, intel_factory, shadow_factory, first):
|
|
384
|
+
agent_factory, intel_factory, shadow_factory, first, mode_pinned=False):
|
|
385
385
|
seen["mode"] = mode
|
|
386
386
|
s = _session_slot(workspace=ws)
|
|
387
387
|
class _E:
|
|
@@ -279,3 +279,26 @@ def test_outputpane_compat_surface_delegates_to_output_pane():
|
|
|
279
279
|
# _say prints into HomeView.console (the output pane) and is dump()-visible
|
|
280
280
|
hv.console.print("hello-from-say")
|
|
281
281
|
assert "hello-from-say" in hv.dump()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def test_you_tile_shows_plan_renews_when_active():
|
|
285
|
+
data = HomeData(account=Account(signed_in=True, nickname="v", plan="pro",
|
|
286
|
+
plan_status="active", plan_renews="Aug 3"))
|
|
287
|
+
hv, _ = _view(data=data)
|
|
288
|
+
text = "".join(f[1] for f in hv._fragments())
|
|
289
|
+
assert "renews Aug 3" in text
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def test_you_tile_shows_expires_when_not_active():
|
|
293
|
+
data = HomeData(account=Account(signed_in=True, nickname="v", plan="pro",
|
|
294
|
+
plan_status="canceled", plan_renews="Aug 3"))
|
|
295
|
+
hv, _ = _view(data=data)
|
|
296
|
+
text = "".join(f[1] for f in hv._fragments())
|
|
297
|
+
assert "expires Aug 3" in text
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def test_home_renders_webbee_code_logo_and_title():
|
|
301
|
+
hv, _ = _view()
|
|
302
|
+
text = "".join(f[1] for f in hv._fragments())
|
|
303
|
+
assert "_____" in text # a slice of the WEBBEE_CODE ascii banner
|
|
304
|
+
assert "◆ Home" in text
|
|
@@ -1881,6 +1881,33 @@ def test_make_session_slot_uses_remembered_mode_over_process_baseline(monkeypatc
|
|
|
1881
1881
|
assert captured["mode"] == "plan" # the agent itself is built with it too
|
|
1882
1882
|
|
|
1883
1883
|
|
|
1884
|
+
def test_make_session_slot_mode_pinned_beats_remembered_mode(monkeypatch):
|
|
1885
|
+
# W5 fix: a new tab opened from Home carries the user's EXPLICIT "new-tab
|
|
1886
|
+
# mode" (mode_pinned=True) which must win over this repo's remembered
|
|
1887
|
+
# mode. Before this, load_mode silently overrode the picked mode —
|
|
1888
|
+
# inconsistently by target repo — so Ctrl+T opened with the wrong mode
|
|
1889
|
+
# while typing on Home happened to keep it. This is the shared chokepoint
|
|
1890
|
+
# both _open_new_tab (Ctrl+T / + / /new) and _home_input (typing) use.
|
|
1891
|
+
import webbee.mode_store as MS
|
|
1892
|
+
monkeypatch.setattr(MS, "load_mode", lambda ws: "plan")
|
|
1893
|
+
captured = {}
|
|
1894
|
+
|
|
1895
|
+
def agent_factory(c, tp, ws, m):
|
|
1896
|
+
captured["mode"] = m
|
|
1897
|
+
return FakeAgent()
|
|
1898
|
+
|
|
1899
|
+
async def _drive():
|
|
1900
|
+
return await _make_session_slot(
|
|
1901
|
+
_mk_cfg(), _noop_token_provider, os.getcwd(), "autopilot",
|
|
1902
|
+
resources=WorkspaceResources(), shared_client=None,
|
|
1903
|
+
agent_factory=agent_factory, intel_factory=lambda cfg, ws: _NoopIntel(),
|
|
1904
|
+
shadow_factory=lambda cfg, ws: None, first=False, mode_pinned=True)
|
|
1905
|
+
|
|
1906
|
+
slot = asyncio.run(_drive())
|
|
1907
|
+
assert slot.mode == "autopilot" # pinned explicit choice beats remembered "plan"
|
|
1908
|
+
assert captured["mode"] == "autopilot"
|
|
1909
|
+
|
|
1910
|
+
|
|
1884
1911
|
def test_make_session_slot_falls_back_to_baseline_when_nothing_remembered(monkeypatch):
|
|
1885
1912
|
import webbee.mode_store as MS
|
|
1886
1913
|
monkeypatch.setattr(MS, "load_mode", lambda ws: None)
|
|
@@ -3979,7 +3979,7 @@ def test_home_input_first_turn_task_is_visible_and_esc_cancels_it():
|
|
|
3979
3979
|
user_echo=lambda t: None)
|
|
3980
3980
|
|
|
3981
3981
|
async def fake_make_session_slot(cfg, tp, ws, mode, *, resources, shared_client,
|
|
3982
|
-
agent_factory, intel_factory, shadow_factory, first):
|
|
3982
|
+
agent_factory, intel_factory, shadow_factory, first, mode_pinned=False):
|
|
3983
3983
|
return SessionSlot(kind="session", workspace=ws, label="new",
|
|
3984
3984
|
pane=tui.OutputPane(width=80), sink=new_sink, agent=None)
|
|
3985
3985
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.26"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{webbee-0.3.26 → webbee-0.3.27}/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
|