Improve stream sidebar experience and avatar affordance #284

Merged
talksik merged 5 commits from experience-nits into main 2026-06-13 18:39:29 +00:00
talksik commented 2026-06-13 18:26:40 +00:00 (Migrated from github.com)

Closes #283
Closes #282

Summary by CodeRabbit

  • Improvements

    • Sidebar: narrower layout, shows message counts, header no longer displays a stream name, and text items truncate sooner
    • Stream view now initializes to player mode by default with unchanged toggle between player and list
  • Style

    • Refined profile avatar sizing and fallback text sizing on Settings
    • Adjusted camera icon dimensions for clearer visual hierarchy
    • Ensured scroll area renders full-width content
Closes #283 Closes #282 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Sidebar: narrower layout, shows message counts, header no longer displays a stream name, and text items truncate sooner * Stream view now initializes to player mode by default with unchanged toggle between player and list * **Style** * Refined profile avatar sizing and fallback text sizing on Settings * Adjusted camera icon dimensions for clearer visual hierarchy * Ensured scroll area renders full-width content <!-- end of auto-generated comment: release notes by coderabbit.ai -->
coderabbitai[bot] commented 2026-06-13 18:26:53 +00:00 (Migrated from github.com)

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b6125fe9-d4e6-43b8-99f0-fa4a3b077ef4

📥 Commits

Reviewing files that changed from the base of the PR and between 3175b04d3c and ba0fbcb4ba.

📒 Files selected for processing (1)
  • js/desktop/src/features/particles/stream-view.tsx
💤 Files with no reviewable changes (1)
  • js/desktop/src/features/particles/stream-view.tsx

📝 Walkthrough

Walkthrough

Removes streamName prop from StreamListSidebar and its callsites; tightens text clamp; defaults useStreamViewMode to 'player'; enforces ScrollArea child full width; and adjusts HumanAvatar sizing and camera overlay icon.

Changes

Stream Sidebar and Layout Refinements

Layer / File(s) Summary
Stream sidebar component refactoring
js/desktop/src/features/particles/stream-list-sidebar.tsx
StreamListSidebarProps removes streamName; component props destructuring drops streamName; header no longer displays streamName and shows "{items.length} messages"; text particle clamp reduced from line-clamp-3 to line-clamp-2.
Stream view integration
js/desktop/src/features/particles/stream-view.tsx
useStreamViewMode is now called without arguments; StreamView no longer passes streamName={streamParticle.properties.name} to StreamListSidebar.
View mode initial state
js/desktop/src/hooks/use-stream-view-mode.ts
useStreamViewMode becomes parameterless and initializes mode to 'player', removing prior stream-id-based decide logic.
ScrollArea child width enforcement
js/desktop/src/components/ui/scroll-area.tsx
ScrollArea root className adds [&>div]:!w-full to force child div full width.
Avatar styling enhancements
js/desktop/src/features/settings-page.tsx
HumanAvatar className changed to size-16 and fallbackClassName updated with text-xl; camera overlay icon size reduced to size-2.5.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

I hop along the sidebar's edge,
I clip the lines and trim the ledge.
Avatars snug, the camera small,
View starts as player — ready for all. 🐇

🚥 Pre-merge checks | 3 | 2

Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The hook simplification in useStreamViewMode appears to go beyond proportions/affordance requirements and may represent scope creep. Clarify whether removing stream-id-driven mode re-evaluation logic from useStreamViewMode was intentional or represents unintended scope expansion beyond the stated UI improvements.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed checks (3 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title accurately summarizes the main changes: improving the stream sidebar experience through UI adjustments and improving avatar affordance via styling updates.
Linked Issues check Passed Changes address both linked issues: #283 sidebar proportions improved via scroll-area styling and streamName removal for narrower sidebar; #282 avatar affordance enhanced through HumanAvatar styling adjustments.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch experience-nits

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/flowy-live/llink/pull/284?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> No actionable comments were generated in the recent review. 🎉 <details> <summary>ℹ️ Recent review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro Plus **Run ID**: `b6125fe9-d4e6-43b8-99f0-fa4a3b077ef4` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 3175b04d3c666d7bc1715b349fa141d55fa462f2 and ba0fbcb4bab9103444fe2414ff84d1bce2d516c0. </details> <details> <summary>📒 Files selected for processing (1)</summary> * `js/desktop/src/features/particles/stream-view.tsx` </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * js/desktop/src/features/particles/stream-view.tsx </details> </details> --- <!-- walkthrough_start --> <details> <summary>📝 Walkthrough</summary> ## Walkthrough Removes `streamName` prop from StreamListSidebar and its callsites; tightens text clamp; defaults useStreamViewMode to 'player'; enforces ScrollArea child full width; and adjusts HumanAvatar sizing and camera overlay icon. ## Changes **Stream Sidebar and Layout Refinements** |Layer / File(s)|Summary| |---|---| |**Stream sidebar component refactoring** <br> `js/desktop/src/features/particles/stream-list-sidebar.tsx`|`StreamListSidebarProps` removes `streamName`; component props destructuring drops `streamName`; header no longer displays streamName and shows "{items.length} messages"; text particle clamp reduced from `line-clamp-3` to `line-clamp-2`.| |**Stream view integration** <br> `js/desktop/src/features/particles/stream-view.tsx`|`useStreamViewMode` is now called without arguments; StreamView no longer passes `streamName={streamParticle.properties.name}` to StreamListSidebar.| |**View mode initial state** <br> `js/desktop/src/hooks/use-stream-view-mode.ts`|`useStreamViewMode` becomes parameterless and initializes `mode` to `'player'`, removing prior stream-id-based decide logic.| |**ScrollArea child width enforcement** <br> `js/desktop/src/components/ui/scroll-area.tsx`|`ScrollArea` root `className` adds `[&>div]:!w-full` to force child div full width.| |**Avatar styling enhancements** <br> `js/desktop/src/features/settings-page.tsx`|`HumanAvatar` `className` changed to `size-16` and `fallbackClassName` updated with `text-xl`; camera overlay icon size reduced to `size-2.5`.| ## Estimated code review effort 🎯 3 (Moderate) | ⏱️ ~20 minutes ## Possibly related PRs - [flowy-live/llink#279](https://github.com/flowy-live/llink/pull/279): Related refactors to StreamListSidebar and view-mode wiring. ## Poem > I hop along the sidebar's edge, > I clip the lines and trim the ledge. > Avatars snug, the camera small, > View starts as player — ready for all. 🐇 </details> <!-- walkthrough_end --> <!-- pre_merge_checks_walkthrough_start --> <details> <summary>🚥 Pre-merge checks | ✅ 3 | ❌ 2</summary> ### ❌ Failed checks (2 warnings) | Check name | Status | Explanation | Resolution | | :------------------------: | :--------- | :----------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Out of Scope Changes check | ⚠️ Warning | The hook simplification in useStreamViewMode appears to go beyond proportions/affordance requirements and may represent scope creep. | Clarify whether removing stream-id-driven mode re-evaluation logic from useStreamViewMode was intentional or represents unintended scope expansion beyond the stated UI improvements. | | Docstring Coverage | ⚠️ Warning | Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. | <details> <summary>✅ Passed checks (3 passed)</summary> | Check name | Status | Explanation | | :-----------------: | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. | | Title check | ✅ Passed | The title accurately summarizes the main changes: improving the stream sidebar experience through UI adjustments and improving avatar affordance via styling updates. | | Linked Issues check | ✅ Passed | Changes address both linked issues: `#283` sidebar proportions improved via scroll-area styling and streamName removal for narrower sidebar; `#282` avatar affordance enhanced through HumanAvatar styling adjustments. | </details> <sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub> </details> <!-- pre_merge_checks_walkthrough_end --> <!-- finishing_touch_checkbox_start --> <details> <summary>✨ Finishing Touches</summary> <details> <summary>📝 Generate docstrings</summary> - [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create stacked PR - [ ] <!-- {"checkboxId": "3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit on current branch </details> <details> <summary>🧪 Generate unit tests (beta)</summary> - [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests - [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Commit unit tests in branch `experience-nits` </details> </details> <!-- finishing_touch_checkbox_end --> <!-- tips_start --> --- <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end --> <!-- internal state start --> <!-- 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 --> <!-- internal state end -->
coderabbitai[bot] (Migrated from github.com) reviewed 2026-06-13 18:29:32 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@js/desktop/src/components/ui/scroll-area.tsx`:
- Line 19: The Tailwind important modifier is placed before the utility in the
className string inside the ScrollArea component; change the arbitrary variant
"[&>div]:!w-full" to place the ! after the utility like "[&>div]:w-full!" so it
conforms to Tailwind v4 syntax; update the className assignment used in the
component (the string containing "focus-visible:ring-ring/50 size-full
rounded-[inherit] transition-[color,box-shadow] outline-none
focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full") to use the
corrected "[&>div]:w-full!" token.

In `@js/desktop/src/hooks/use-stream-view-mode.ts`:
- Line 21: The initial state for mode was changed to a constant 'player',
causing inconsistent behavior; restore the lazy initializer so the state is
computed via decideMode(streamParticle, userId) on first mount (i.e., change the
useState in use-stream-view-mode.ts back to useState<StreamViewMode>(() =>
decideMode(streamParticle, userId))). Ensure this makes the initial mount match
the navigation recomputation that already calls decideMode; alternatively, if
starting always in 'player' was intentional, document that rationale and remove
the navigation re-computation and the now-unused decideMode function to keep
behavior consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 05fb4bae-9ed4-4693-a929-ca4778cf53bf

📥 Commits

Reviewing files that changed from the base of the PR and between 9249cb4107 and 3381ff0b02.

📒 Files selected for processing (5)
  • js/desktop/src/components/ui/scroll-area.tsx
  • js/desktop/src/features/particles/stream-list-sidebar.tsx
  • js/desktop/src/features/particles/stream-view.tsx
  • js/desktop/src/features/settings-page.tsx
  • js/desktop/src/hooks/use-stream-view-mode.ts
💤 Files with no reviewable changes (1)
  • js/desktop/src/features/particles/stream-view.tsx
**Actionable comments posted: 2** <details> <summary>🤖 Prompt for all review comments with AI agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Inline comments: In `@js/desktop/src/components/ui/scroll-area.tsx`: - Line 19: The Tailwind important modifier is placed before the utility in the className string inside the ScrollArea component; change the arbitrary variant "[&>div]:!w-full" to place the ! after the utility like "[&>div]:w-full!" so it conforms to Tailwind v4 syntax; update the className assignment used in the component (the string containing "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full") to use the corrected "[&>div]:w-full!" token. In `@js/desktop/src/hooks/use-stream-view-mode.ts`: - Line 21: The initial state for mode was changed to a constant 'player', causing inconsistent behavior; restore the lazy initializer so the state is computed via decideMode(streamParticle, userId) on first mount (i.e., change the useState in use-stream-view-mode.ts back to useState<StreamViewMode>(() => decideMode(streamParticle, userId))). Ensure this makes the initial mount match the navigation recomputation that already calls decideMode; alternatively, if starting always in 'player' was intentional, document that rationale and remove the navigation re-computation and the now-unused decideMode function to keep behavior consistent. ``` </details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro Plus **Run ID**: `05fb4bae-9ed4-4693-a929-ca4778cf53bf` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 9249cb4107fc1f5bcc4516740f1e793c0f4da778 and 3381ff0b025daf6df02ab10680e869b1ba4313c4. </details> <details> <summary>📒 Files selected for processing (5)</summary> * `js/desktop/src/components/ui/scroll-area.tsx` * `js/desktop/src/features/particles/stream-list-sidebar.tsx` * `js/desktop/src/features/particles/stream-view.tsx` * `js/desktop/src/features/settings-page.tsx` * `js/desktop/src/hooks/use-stream-view-mode.ts` </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * js/desktop/src/features/particles/stream-view.tsx </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@@ -18,3 +18,3 @@
data-slot="scroll-area-viewport"
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full"
>
coderabbitai[bot] (Migrated from github.com) commented 2026-06-13 18:29:32 +00:00

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Tailwind CSS v4 important modifier syntax arbitrary variants

💡 Result:

In Tailwind CSS v4, the syntax and handling for important modifiers and arbitrary variants have been updated to align with standard CSS conventions and a CSS-first configuration approach. Important Modifier Syntax In Tailwind v4, the important modifier (!) is placed at the end of the utility class, mirroring native CSS where!important follows the declaration [1][2][3]. - Example: Instead of!text-blue-500 (v3 style), use text-blue-500! [4][2][3]. - This applies to utilities in HTML as well as those used within @apply directives in CSS [5][3]. - Global important: To apply!important to all utility classes globally, add the important keyword to the Tailwind import statement in your CSS: @import "tailwindcss" important; [6][7]. You can also wrap all utilities in a custom selector by specifying it, for example: @import "tailwindcss" important(#app); [6]. Arbitrary Variants Arbitrary variants allow for on-the-fly selector modification directly in your HTML using square bracket notation, similar to arbitrary values [8][9]. - Syntax: They are written as [selector], where the ampersand (&) represents the current element [10]. - Example: [

...
] applies styles to child list items [10]. - If a selector contains spaces, use an underscore (e.g., [&_p]:text-blue-500) [8]. - For frequently used variants, Tailwind v4 introduces the @custom-variant directive in CSS, which allows you to define reusable variant selectors without requiring JavaScript plugins [11][12]. Top Results: [4][6][2][8][3]

Citations:


Fix Tailwind v4 important modifier placement in scroll-area.tsx.

In Tailwind v4, ! must come after the utility (even inside arbitrary variants), so [&>div]:w-full! instead of [&>div]:!w-full.

🔧 Proposed fix
-        className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full"
+        className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:w-full!"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/desktop/src/components/ui/scroll-area.tsx` at line 19, The Tailwind
important modifier is placed before the utility in the className string inside
the ScrollArea component; change the arbitrary variant "[&>div]:!w-full" to
place the ! after the utility like "[&>div]:w-full!" so it conforms to Tailwind
v4 syntax; update the className assignment used in the component (the string
containing "focus-visible:ring-ring/50 size-full rounded-[inherit]
transition-[color,box-shadow] outline-none focus-visible:ring-[3px]
focus-visible:outline-1 [&>div]:!w-full") to use the corrected "[&>div]:w-full!"
token.
_⚠️ Potential issue_ | _🔴 Critical_ <details> <summary>🧩 Analysis chain</summary> 🌐 Web query: `Tailwind CSS v4 important modifier syntax arbitrary variants` 💡 Result: In Tailwind CSS v4, the syntax and handling for important modifiers and arbitrary variants have been updated to align with standard CSS conventions and a CSS-first configuration approach. Important Modifier Syntax In Tailwind v4, the important modifier (!) is placed at the end of the utility class, mirroring native CSS where!important follows the declaration [1][2][3]. - Example: Instead of!text-blue-500 (v3 style), use text-blue-500! [4][2][3]. - This applies to utilities in HTML as well as those used within `@apply` directives in CSS [5][3]. - Global important: To apply!important to all utility classes globally, add the important keyword to the Tailwind import statement in your CSS: `@import` "tailwindcss" important; [6][7]. You can also wrap all utilities in a custom selector by specifying it, for example: `@import` "tailwindcss" important(`#app`); [6]. Arbitrary Variants Arbitrary variants allow for on-the-fly selector modification directly in your HTML using square bracket notation, similar to arbitrary values [8][9]. - Syntax: They are written as [selector], where the ampersand (&) represents the current element [10]. - Example: [<div class="[&>li]:py-2">...</div>] applies styles to child list items [10]. - If a selector contains spaces, use an underscore (e.g., [&_p]:text-blue-500) [8]. - For frequently used variants, Tailwind v4 introduces the `@custom-variant` directive in CSS, which allows you to define reusable variant selectors without requiring JavaScript plugins [11][12]. Top Results: [4][6][2][8][3] Citations: - 1: https://stackoverflow.com/questions/75664539/tailwind-css-class-precedence-is-not-respected - 2: https://playbooks.com/skills/fusengine/agents/tailwindcss-v4 - 3: https://tailwindcss.com/docs/upgrade-guide - 4: https://tailwindlabs-tailwindcss.mintlify.app/advanced/important-modifier - 5: https://github.com/tailwindlabs/tailwindcss/pull/14434 - 6: https://github.com/tailwindlabs/tailwindcss/pull/14448 - 7: https://github.com/tailwindlabs/tailwindcss/discussions/17994 - 8: https://tailwindcss.com/docs/hover-focus-and-other-states - 9: https://tailwindcss.com/docs/adding-custom-styles - 10: https://llmbestpractices.com/frontend/tailwind-arbitrary-values - 11: https://tailwindcss.com/docs/functions-and-directives - 12: https://deepwiki.com/tlq5l/tailwindcss-v4-skill/2.4-the-@variant-and-@custom-variant-directives --- </details> **Fix Tailwind v4 important modifier placement in `scroll-area.tsx`.** In Tailwind v4, `!` must come **after** the utility (even inside arbitrary variants), so `[&>div]:w-full!` instead of `[&>div]:!w-full`. <details> <summary>🔧 Proposed fix</summary> ```diff - className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full" + className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:w-full!" ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@js/desktop/src/components/ui/scroll-area.tsx` at line 19, The Tailwind important modifier is placed before the utility in the className string inside the ScrollArea component; change the arbitrary variant "[&>div]:!w-full" to place the ! after the utility like "[&>div]:w-full!" so it conforms to Tailwind v4 syntax; update the className assignment used in the component (the string containing "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full") to use the corrected "[&>div]:w-full!" token. ``` </details> <!-- fingerprinting:phantom:poseidon:puma --> <!-- cr-comment:v1:4dccc68c6d825f79f4034232 --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2026-06-13 18:29:32 +00:00

⚠️ Potential issue | 🟠 Major | Quick win

Inconsistent initial mode behavior degrades UX for caught-up users.

Removing the lazy initializer that computed the initial mode via decideMode(streamParticle, userId) creates a behavioral inconsistency:

  • First mount: Mode is always 'player', regardless of whether the user is caught up with the stream.
  • Navigation (lines 25-27): Mode is correctly computed via decideMode, so subsequent streams show the appropriate view.

This means users who are caught up with a stream will initially see the player view when they should see the list view, but navigating to another stream and back will show the correct mode. The inconsistency is confusing and degrades the user experience.

Recommended fix: Restore the lazy initializer to ensure consistent behavior on both initial mount and navigation.

🔄 Proposed fix to restore consistent initial mode
-  const [mode, setMode] = useState<StreamViewMode>('player');
+  const [mode, setMode] = useState<StreamViewMode>(() =>
+    decideMode(streamParticle, userId)
+  );

If this change was intentional (e.g., to always start in player mode for layout reasons), please document the rationale and consider removing the navigation re-computation logic (lines 23-28) and the now-mostly-unused decideMode function for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  const [mode, setMode] = useState<StreamViewMode>(() =>
    decideMode(streamParticle, userId)
  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/desktop/src/hooks/use-stream-view-mode.ts` at line 21, The initial state
for mode was changed to a constant 'player', causing inconsistent behavior;
restore the lazy initializer so the state is computed via
decideMode(streamParticle, userId) on first mount (i.e., change the useState in
use-stream-view-mode.ts back to useState<StreamViewMode>(() =>
decideMode(streamParticle, userId))). Ensure this makes the initial mount match
the navigation recomputation that already calls decideMode; alternatively, if
starting always in 'player' was intentional, document that rationale and remove
the navigation re-computation and the now-unused decideMode function to keep
behavior consistent.
_⚠️ Potential issue_ | _🟠 Major_ | _⚡ Quick win_ **Inconsistent initial mode behavior degrades UX for caught-up users.** Removing the lazy initializer that computed the initial mode via `decideMode(streamParticle, userId)` creates a behavioral inconsistency: - **First mount**: Mode is always `'player'`, regardless of whether the user is caught up with the stream. - **Navigation** (lines 25-27): Mode is correctly computed via `decideMode`, so subsequent streams show the appropriate view. This means users who are caught up with a stream will initially see the player view when they should see the list view, but navigating to another stream and back will show the correct mode. The inconsistency is confusing and degrades the user experience. **Recommended fix**: Restore the lazy initializer to ensure consistent behavior on both initial mount and navigation. <details> <summary>🔄 Proposed fix to restore consistent initial mode</summary> ```diff - const [mode, setMode] = useState<StreamViewMode>('player'); + const [mode, setMode] = useState<StreamViewMode>(() => + decideMode(streamParticle, userId) + ); ``` </details> If this change was intentional (e.g., to always start in player mode for layout reasons), please document the rationale and consider removing the navigation re-computation logic (lines 23-28) and the now-mostly-unused `decideMode` function for consistency. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion const [mode, setMode] = useState<StreamViewMode>(() => decideMode(streamParticle, userId) ); ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@js/desktop/src/hooks/use-stream-view-mode.ts` at line 21, The initial state for mode was changed to a constant 'player', causing inconsistent behavior; restore the lazy initializer so the state is computed via decideMode(streamParticle, userId) on first mount (i.e., change the useState in use-stream-view-mode.ts back to useState<StreamViewMode>(() => decideMode(streamParticle, userId))). Ensure this makes the initial mount match the navigation recomputation that already calls decideMode; alternatively, if starting always in 'player' was intentional, document that rationale and remove the navigation re-computation and the now-unused decideMode function to keep behavior consistent. ``` </details> <!-- fingerprinting:phantom:poseidon:puma --> <!-- cr-comment:v1:9d61156f6b3488286272070e --> <!-- This is an auto-generated comment by CodeRabbit -->
Sign in to join this conversation.