feat: allow side elements to opt out of automatic expansion - #3054
minwookshin wants to merge 4 commits into
Conversation
Co-Authored-By: Codex <noreply@openai.com>
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Only clear an empty side-element view when it still displays a side element tracked by the message hook. Preserve unrelated ElementSidebar state, including its title and key, when inline or page elements arrive or tracked content is replaced. Check display as well as ID so a reused ID cannot claim an inline view. Cover title-only and populated API sidebars, replacements with new or reused IDs, and cleanup of explicitly opened opt-out elements. Six preservation regressions fail before this change; all 44 frontend tests pass after it. Co-Authored-By: Codex <noreply@openai.com>
|
@codex review |
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Preserve API replacements while cleaning up message-owned panels after remounts and explicit side-element opens. Co-Authored-By: Codex <noreply@openai.com>
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Preserved autoExpand on custom-element updates and added a navigation regression check. Focused backend/frontend tests pass locally; the pre-commit hook could not run here because uv is unavailable. |
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/tests/test_element.py">
<violation number="1" location="backend/tests/test_element.py:119">
P2: The test is named rest_update but never calls the REST endpoint `update_thread_element`; it calls the private helper `_sanitize_custom_element` and `element.update()` directly. The actual user flow (PUT /project/element) additionally routes the payload through `ElementRequest` Pydantic validation and requires `type == "custom"`, so a regression that drops `autoExpand` during request deserialization or in the endpoint wiring would go uncaught by this test. Test the endpoint (e.g., via httpx/AsyncClient with mocked `WebsocketSession.get_by_id` and `init_ws_context`), or rename the test to reflect that it only covers the `_sanitize_custom_element` mapping.</violation>
</file>
<file name="frontend/tests/sideElementsNavigation.spec.tsx">
<violation number="1" location="frontend/tests/sideElementsNavigation.spec.tsx:148">
P2: The new assertion at line 148 is 102 characters, over Prettier's default 80-column width; `prettier --check` fails on this file (verified), and CI enforces `pnpm format-check`. Replacing it with `toHaveBeenCalledWith` also fixes the diagnostics: the current `.some(...).toBe(true)` reports only `expected true to be false` on failure, while `toHaveBeenCalledWith({ id: 'other' })` prints the actual recorded calls.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| assert payload.get("autoExpand", True) is auto_expand | ||
| assert "autoExpand" not in element.to_dict() | ||
|
|
||
| async def test_custom_element_rest_update_preserves_collapsed_hint( |
There was a problem hiding this comment.
P2: The test is named rest_update but never calls the REST endpoint update_thread_element; it calls the private helper _sanitize_custom_element and element.update() directly. The actual user flow (PUT /project/element) additionally routes the payload through ElementRequest Pydantic validation and requires type == "custom", so a regression that drops autoExpand during request deserialization or in the endpoint wiring would go uncaught by this test. Test the endpoint (e.g., via httpx/AsyncClient with mocked WebsocketSession.get_by_id and init_ws_context), or rename the test to reflect that it only covers the _sanitize_custom_element mapping.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/tests/test_element.py, line 119:
<comment>The test is named rest_update but never calls the REST endpoint `update_thread_element`; it calls the private helper `_sanitize_custom_element` and `element.update()` directly. The actual user flow (PUT /project/element) additionally routes the payload through `ElementRequest` Pydantic validation and requires `type == "custom"`, so a regression that drops `autoExpand` during request deserialization or in the endpoint wiring would go uncaught by this test. Test the endpoint (e.g., via httpx/AsyncClient with mocked `WebsocketSession.get_by_id` and `init_ws_context`), or rename the test to reflect that it only covers the `_sanitize_custom_element` mapping.</comment>
<file context>
@@ -116,6 +116,31 @@ async def test_side_element_auto_expand_is_a_live_presentation_hint(
assert payload.get("autoExpand", True) is auto_expand
assert "autoExpand" not in element.to_dict()
+ async def test_custom_element_rest_update_preserves_collapsed_hint(
+ self, mock_chainlit_context
+ ):
</file context>
|
|
||
| act(() => result.current.navigate('/thread/other')); | ||
|
|
||
| expect(vi.mocked(AutoResumeThread).mock.calls.some(([props]) => props.id === 'other')).toBe(true); |
There was a problem hiding this comment.
P2: The new assertion at line 148 is 102 characters, over Prettier's default 80-column width; prettier --check fails on this file (verified), and CI enforces pnpm format-check. Replacing it with toHaveBeenCalledWith also fixes the diagnostics: the current .some(...).toBe(true) reports only expected true to be false on failure, while toHaveBeenCalledWith({ id: 'other' }) prints the actual recorded calls.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/tests/sideElementsNavigation.spec.tsx, line 148:
<comment>The new assertion at line 148 is 102 characters, over Prettier's default 80-column width; `prettier --check` fails on this file (verified), and CI enforces `pnpm format-check`. Replacing it with `toHaveBeenCalledWith` also fixes the diagnostics: the current `.some(...).toBe(true)` reports only `expected true to be false` on failure, while `toHaveBeenCalledWith({ id: 'other' })` prints the actual recorded calls.</comment>
<file context>
@@ -138,7 +136,15 @@ describe('side elements across thread navigation', () => {
+
+ act(() => result.current.navigate('/thread/other'));
+
+ expect(vi.mocked(AutoResumeThread).mock.calls.some(([props]) => props.id === 'other')).toBe(true);
+ });
});
</file context>
Addresses #2932.
A new or updated side element currently reopens a panel the user has closed. Add a keyword-only
auto_expand=Falseoption to elements, includingCustomElement, and carry it as the optionalautoExpandlive event hint.The frontend keeps a closed panel closed for opted-out arrivals, updates an already open panel, and still opens it when the user clicks the message reference. Existing callers keep automatic expansion by default. When no side elements remain, cleanup only closes a view still displaying a tracked side element; sidebars opened through
ElementSidebar.set_titleorset_elementsretain their contents, title and key across unrelated inline/page updates.Validation: All 938 backend tests on Python 3.13 and all 44 frontend tests pass. The seven new sidebar cases include six preservation regressions that fail before the cleanup fix, including a replacement reusing a tracked ID, and one explicit opt-out element cleanup case. Six API compatibility cases also pass on Python 3.10, covering Text, File and CustomElement with both values of auto_expand. The repository pre-commit checks, including full backend mypy, source/test lint and frontend/react-client typechecks, pass. Repository-wide ESLint/format checks and the embedded copilot build also pass. The production frontend Vite build passes with
NODE_OPTIONS=--max-old-space-size=4096; the default 2GB Node heap was insufficient on the validation machine. Existing sourcemap and chunk-size warnings remain. Chrome verification against the real backend covers default opening, quiet Text and CustomElement arrivals, and explicit opening of both kinds. Documentation includes the API and persistence boundary.The hint is intentionally not persisted; restored historical threads keep their current display behavior. Apply the option to each new or updated element that should remain quiet.
Summary by cubic
Fixes the side panel reopening on every side-element update (issue #2932). Elements can now opt out with
auto_expand=Falseand remain reachable only through their message reference.auto_expandapplies toElementandCustomElementand defaults toTrue.Written for commit 65a3849. Summary will update on new commits.