diff --git a/CHANGELOG.md b/CHANGELOG.md index a6141f8c..2908a634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ ### Fixed +- **The consolidated Kanban board now fills the panel's vertical space.** In consolidated view (a single board, not split into per-profile lanes), the columns were previously clamped to a short fixed height, leaving a large empty gap below the board. The consolidated board now grows to the full panel height and scrolls as one tall surface; the per-profile lane view keeps its existing height clamp and contained scrolling. CSS-only. Thanks @jpalazz2. (#6308) + - **Long-running installs use less memory: the default resident-session cache cap is lowered from 300 to 100.** The in-memory `SESSIONS` LRU (compact session objects) is a dominant contributor to WebUI RSS climbing over hours of continuous use; the shipped no-override default is now tuned for the common single-user install. Larger deployments can still raise it via `webui.sessions_cache_max` in `config.yaml` (preferred) or the `HERMES_WEBUI_SESSIONS_MAX` env fallback. Eviction safety is unchanged — only clean, persisted, non-active sessions are ever dropped, and an evicted session lazily reloads from its JSON sidecar on next access (no data loss). Thanks @rodboev. (#6362, #6351) - **Completed image tool-results are compacted from persisted history, cutting long-session memory and save/load cost.** After a turn finishes, base64 image parts in persisted *tool* results are replaced with a small `[screenshot]` text placeholder, so JSON sidecar saves/loads and session API responses no longer scale with image bytes (the active model still gets the full image while the tool call is running). The transform preserves every non-image part in place (text, documents, provider-specific parts) without flattening or reordering, only touches `role: tool` messages (user attachments are untouched), runs on all live and settled persistence writebacks, and is idempotent. Thanks @sjungwon03. (#6315, #1217) diff --git a/static/panels.js b/static/panels.js index e4e30752..6bda3158 100644 --- a/static/panels.js +++ b/static/panels.js @@ -2526,6 +2526,7 @@ function _kanbanRenderBoard(){ board.innerHTML = _kanbanEmptyBoardHtml(); return; } + board.classList.toggle('kanban-board-consolidated', !_kanbanLanesByProfile); const columns = _kanbanVisibleTasks(); const total = columns.reduce((n, col) => n + (col.tasks || []).length, 0); if ($('kanbanSummary')) $('kanbanSummary').textContent = String(t('kanban_visible_tasks')).replace('{0}', total); diff --git a/static/style.css b/static/style.css index 9814b6da..be923c43 100644 --- a/static/style.css +++ b/static/style.css @@ -6696,10 +6696,12 @@ main.main.showing-insights > #mainInsights{display:flex;overflow-y:auto;} .kanban-list-title{font-size:13px;font-weight:600;line-height:1.35;} .kanban-board-wrap{flex:1;min-height:0;overflow:auto;padding:16px;background:var(--bg);} .kanban-board{display:flex;gap:12px;min-height:100%;overflow-x:auto;padding-bottom:8px;} +.kanban-board.kanban-board-consolidated{height:100%;} .kanban-column{display:flex;flex-direction:column;min-width:260px;max-width:320px;flex:1;background:var(--panel);border:1px solid var(--border);border-radius:10px;min-height:240px;} .kanban-column-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--border);font-size:13px;font-weight:600;color:var(--text);} .kanban-count{font-size:11px;color:var(--muted);background:var(--input-bg);border:1px solid var(--border);border-radius:999px;padding:1px 7px;} .kanban-column-body{display:flex;flex-direction:column;gap:8px;padding:10px;min-height:0;max-height:min(68vh,720px);overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;} +.kanban-board-consolidated .kanban-column-body{height:100%;max-height:unset;overscroll-behavior:auto;} .kanban-card{border:1px solid var(--border);border-radius:9px;background:var(--bg);padding:10px;cursor:pointer;box-shadow:var(--shadow-sm);} .kanban-card:hover,.kanban-card.selected{border-color:var(--accent);} .kanban-card-title{font-size:13px;font-weight:650;color:var(--text);line-height:1.35;margin-bottom:6px;} diff --git a/tests/test_kanban_board_ui.py b/tests/test_kanban_board_ui.py index 112df424..9779222b 100644 --- a/tests/test_kanban_board_ui.py +++ b/tests/test_kanban_board_ui.py @@ -25,3 +25,30 @@ def test_kanban_columns_render_scrollable_card_lists(): assert "overflow-y:auto" in rule assert "overscroll-behavior:contain" in rule assert "scrollbar-gutter:stable" in rule + + +def test_kanban_consolidated_board_fills_viewport_height(): + """The consolidated (single-board) view must stretch to fill the viewport + instead of leaving empty space below the columns on desktop. + + Both the board element and its column bodies drop their capped heights and + take height:100% so the columns extend to the bottom of the board wrap. + """ + board_rule = _css_rule(".kanban-board.kanban-board-consolidated") + assert "height:100%" in board_rule + + body_rule = _css_rule(".kanban-board-consolidated .kanban-column-body") + assert "height:100%" in body_rule + assert "max-height:unset" in body_rule + # The consolidated view owns its own full-height scroll container, so it opts + # out of the scroll-chaining lock the capped lane view uses. + assert "overscroll-behavior:auto" in body_rule + + +def test_kanban_consolidated_class_toggled_by_lane_mode(): + """The consolidated-height CSS only takes effect when the board element + carries the `kanban-board-consolidated` class, which the renderer adds only + in the single-board (non-lanes) view. Pin the wiring so a render refactor + can't silently strip the class and reintroduce the empty-space regression. + """ + assert "classList.toggle('kanban-board-consolidated'" in PANELS_JS