Added a unified keyboard-hint control and styled key chips for interactive shortcuts across the app.
Style
Replaced scattered inline key/kbd hints with the unified control, standardizing appearance and behavior in overlays, editors, playback, and navigation.
Improved hint wording, titles/ARIA, and click/tap affordances for keyboard shortcuts.
Closes #186
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Added a unified keyboard-hint control and styled key chips for interactive shortcuts across the app.
* **Style**
* Replaced scattered inline key/kbd hints with the unified control, standardizing appearance and behavior in overlays, editors, playback, and navigation.
* Improved hint wording, titles/ARIA, and click/tap affordances for keyboard shortcuts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Adds Kbd primitives and a reusable KeyHint component, then replaces inline hint markup across overlays, compose flows, stream view, and other UI with KeyHint instances that optionally handle clicks and preserve focus.
Changes
KeyHint Foundation and Keyboard Shortcut Consolidation
Layer / File(s)
Summary
KeyHint component foundation js/desktop/src/components/ui/kbd.tsx, js/desktop/src/components/key-hint.tsx
New Kbd and KbdGroup primitives and KeyHint component render key chips, support prefix/separator/children, and render as a non-interactive span or clickable button with focus-preserving onMouseDown handling.
Overlay components replace inline <kbd> close/navigation hints with KeyHint elements wired to onClose or equivalent handlers and updated title/aria text.
Compose-related UIs now use KeyHint for Esc, Enter, Meta+Enter, Meta+M, and other hints, preserving conditional rendering and existing click/keyboard handlers.
Other components migrated from inline <kbd>/button markup to KeyHint, delegating click behavior to existing handlers and adding title/aria attributes where applicable.
Stream view controls and huddle integration js/desktop/src/features/particles/stream-view.tsx, js/desktop/src/hooks/use-stream-navigation-keys.ts
StreamViewControls refactored to use KeyHint for in-stream actions and receives onOpenHuddle and onExit callbacks; useStreamNavigationKeys now accepts onExit and calls it on Escape.
🎯 3 (Moderate) | ⏱️ ~22 minutes
"🐰
Keys once scattered, now they unite,
One component to make hints bright,
Click or type, both work just right,
Keyboard flows polished in sight,
Hop on — shortcuts feel light!"
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name
Status
Explanation
Resolution
Docstring Coverage
⚠️ Warning
Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%.
Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name
Status
Explanation
Description Check
✅ Passed
Check skipped - CodeRabbit’s high-level summary is enabled.
Title check
✅ Passed
The title 'feat: allow clicking keyboard hints throughout' clearly and concisely describes the main change—enabling keyboard hint clicks across the application.
Linked Issues check
✅ Passed
The PR successfully addresses issue #186 by implementing clickable keyboard shortcut hints via the new KeyHint component, allowing mouse-click alternatives throughout the UI.
Out of Scope Changes check
✅ Passed
All changes are directly aligned with the PR objectives. New components (KeyHint, Kbd, KbdGroup) support the feature, and refactored files consistently replace hardcoded keyboard hints with the reusable KeyHint component.
✏️ 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 worktree-harmonic-purring-fox
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 -->
[](https://app.coderabbit.ai/change-stack/flowy-live/llink/pull/266?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**: `e0523d50-f395-4995-bba9-b64c341a2f72`
</details>
<details>
<summary>📥 Commits</summary>
Reviewing files that changed from the base of the PR and between dd850d28d13405a8065caccfef5fb52ed148ad18 and 1cf144bce3c8a2d6bf8f414148c22b9ec2781abf.
</details>
<details>
<summary>📒 Files selected for processing (2)</summary>
* `js/desktop/src/features/particles/stream-view.tsx`
* `js/desktop/src/hooks/use-stream-navigation-keys.ts`
</details>
</details>
---
<!-- walkthrough_start -->
<details>
<summary>📝 Walkthrough</summary>
## Walkthrough
Adds Kbd primitives and a reusable KeyHint component, then replaces inline <kbd> hint markup across overlays, compose flows, stream view, and other UI with KeyHint instances that optionally handle clicks and preserve focus.
## Changes
**KeyHint Foundation and Keyboard Shortcut Consolidation**
| Layer / File(s)|Summary|
|---|---|
|**KeyHint component foundation** <br> `js/desktop/src/components/ui/kbd.tsx`, `js/desktop/src/components/key-hint.tsx`|New `Kbd` and `KbdGroup` primitives and `KeyHint` component render key chips, support `prefix`/`separator`/`children`, and render as a non-interactive `span` or clickable `button` with focus-preserving `onMouseDown` handling.|
|**Overlay keyboard shortcuts** <br> `js/desktop/src/components/confirm-destructive-overlay.tsx`, `js/desktop/src/components/keybindings-overlay.tsx`, `js/desktop/src/features/particles/rename-stream-overlay.tsx`, `js/desktop/src/features/particles/stream-members-overlay.tsx`|Overlay components replace inline `<kbd>` close/navigation hints with `KeyHint` elements wired to `onClose` or equivalent handlers and updated title/aria text.|
|**Compose feature keyboard shortcuts** <br> `js/desktop/src/features/compose/text-editor.tsx`, `js/desktop/src/features/compose/recording-overlay.tsx`, `js/desktop/src/features/compose/compose-overlay.tsx`, `js/desktop/src/features/compose/configure-stream-step.tsx`|Compose-related UIs now use `KeyHint` for Esc, Enter, Meta+Enter, Meta+M, and other hints, preserving conditional rendering and existing click/keyboard handlers.|
|**Additional component keyboard shortcuts** <br> `js/desktop/src/components/video-audio-toggle.tsx`, `js/desktop/src/features/attachments/attachment-lightbox.tsx`, `js/desktop/src/components/keybindings-overlay.tsx`, `js/desktop/src/features/network-root.tsx`|Other components migrated from inline `<kbd>`/button markup to `KeyHint`, delegating click behavior to existing handlers and adding title/aria attributes where applicable.|
|**Stream view controls and huddle integration** <br> `js/desktop/src/features/particles/stream-view.tsx`, `js/desktop/src/hooks/use-stream-navigation-keys.ts`|`StreamViewControls` refactored to use `KeyHint` for in-stream actions and receives `onOpenHuddle` and `onExit` callbacks; `useStreamNavigationKeys` now accepts `onExit` and calls it on Escape.|
🎯 3 (Moderate) | ⏱️ ~22 minutes
> "🐰
> Keys once scattered, now they unite,
> One component to make hints bright,
> Click or type, both work just right,
> Keyboard flows polished in sight,
> Hop on — shortcuts feel light!"
</details>
<!-- walkthrough_end -->
<!-- pre_merge_checks_walkthrough_start -->
<details>
<summary>🚥 Pre-merge checks | ✅ 4 | ❌ 1</summary>
### ❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 40.00% 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 (4 passed)</summary>
| Check name | Status | Explanation |
| :------------------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title 'feat: allow clicking keyboard hints throughout' clearly and concisely describes the main change—enabling keyboard hint clicks across the application. |
| Linked Issues check | ✅ Passed | The PR successfully addresses issue `#186` by implementing clickable keyboard shortcut hints via the new KeyHint component, allowing mouse-click alternatives throughout the UI. |
| Out of Scope Changes check | ✅ Passed | All changes are directly aligned with the PR objectives. New components (KeyHint, Kbd, KbdGroup) support the feature, and refactored files consistently replace hardcoded keyboard hints with the reusable KeyHint component. |
</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 `worktree-harmonic-purring-fox`
</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-11 17:31:13 +00:00
coderabbitai[bot]
(Migrated from github.com)
left a comment
Copy Link
Copy Source
Actionable comments posted: 3
🤖 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/key-hint.tsx`:
- Around line 45-49: The Fragment in KeyHint maps keyList with <Fragment
key={k}> which can collide when labels repeat; change the key to a composite
unique identifier (for example using the index) such as `${k}-${i}` to avoid
React reconciliation issues — locate the map in the KeyHint component
(keyList.map((k, i) => ...)) and replace the Fragment key to use both the label
and index (or just the index if appropriate) so each rendered element has a
stable, unique key.
In `@js/desktop/src/components/ui/kbd.tsx`:
- Around line 16-23: KbdGroup is typed as React.ComponentProps<'div'> but
returns a <kbd>, causing a prop-type mismatch; fix by making the component's
props match the rendered element: either change the render to a <div> in the
KbdGroup function or update the props typing to React.ComponentProps<'kbd'>
(adjust any consumers if needed), ensuring the component signature (KbdGroup)
and spread {...props} align with the element type.
In `@js/desktop/src/features/particles/stream-view.tsx`:
- Around line 610-616: The Escape action currently uses history back
(navigate(-1)) which can leave users off the network root; update the Escape
handlers to navigate to the network root instead: replace navigate(-1) in the
KeyHint onClick (KeyHint in stream-view.tsx) and the Escape case in
useStreamNavigationKeys to call navigate(`/${networkId}`) (use the same
networkId used by handleExitNavigate) so the keybinding, title, and exit
countdown all consistently go to the network root; alternatively, if you prefer
history semantics, change the KeyHint title/text from “Back to network” to
“Back”/“History back” instead.
🪄 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: 390544c3-d236-4d15-982a-436bf62b136b
📥 Commits
Reviewing files that changed from the base of the PR and between 9fd7e611f3 and 023e332e01.
**Actionable comments posted: 3**
<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/key-hint.tsx`:
- Around line 45-49: The Fragment in KeyHint maps keyList with <Fragment
key={k}> which can collide when labels repeat; change the key to a composite
unique identifier (for example using the index) such as `${k}-${i}` to avoid
React reconciliation issues — locate the map in the KeyHint component
(keyList.map((k, i) => ...)) and replace the Fragment key to use both the label
and index (or just the index if appropriate) so each rendered element has a
stable, unique key.
In `@js/desktop/src/components/ui/kbd.tsx`:
- Around line 16-23: KbdGroup is typed as React.ComponentProps<'div'> but
returns a <kbd>, causing a prop-type mismatch; fix by making the component's
props match the rendered element: either change the render to a <div> in the
KbdGroup function or update the props typing to React.ComponentProps<'kbd'>
(adjust any consumers if needed), ensuring the component signature (KbdGroup)
and spread {...props} align with the element type.
In `@js/desktop/src/features/particles/stream-view.tsx`:
- Around line 610-616: The Escape action currently uses history back
(navigate(-1)) which can leave users off the network root; update the Escape
handlers to navigate to the network root instead: replace navigate(-1) in the
KeyHint onClick (KeyHint in stream-view.tsx) and the Escape case in
useStreamNavigationKeys to call navigate(`/${networkId}`) (use the same
networkId used by handleExitNavigate) so the keybinding, title, and exit
countdown all consistently go to the network root; alternatively, if you prefer
history semantics, change the KeyHint title/text from “Back to network” to
“Back”/“History back” instead.
```
</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**: `390544c3-d236-4d15-982a-436bf62b136b`
</details>
<details>
<summary>📥 Commits</summary>
Reviewing files that changed from the base of the PR and between 9fd7e611f31d09957648ccb5117fc12bbdc6c0bf and 023e332e011714c127bba86ea85b1fada81d43fc.
</details>
<details>
<summary>📒 Files selected for processing (14)</summary>
* `js/desktop/src/components/confirm-destructive-overlay.tsx`
* `js/desktop/src/components/key-hint.tsx`
* `js/desktop/src/components/keybindings-overlay.tsx`
* `js/desktop/src/components/ui/kbd.tsx`
* `js/desktop/src/components/video-audio-toggle.tsx`
* `js/desktop/src/features/attachments/attachment-lightbox.tsx`
* `js/desktop/src/features/compose/compose-overlay.tsx`
* `js/desktop/src/features/compose/configure-stream-step.tsx`
* `js/desktop/src/features/compose/recording-overlay.tsx`
* `js/desktop/src/features/compose/text-editor.tsx`
* `js/desktop/src/features/network-root.tsx`
* `js/desktop/src/features/particles/rename-stream-overlay.tsx`
* `js/desktop/src/features/particles/stream-members-overlay.tsx`
* `js/desktop/src/features/particles/stream-view.tsx`
</details>
</details>
<!-- This is an auto-generated comment by CodeRabbit for review status -->
Prevent React key collisions for repeated labels in KeyHint. <Fragment key={k}> uses the label as the React key; if keyList contains duplicate labels (e.g. "Shift" twice), reconciliation can be incorrect. Use a composite key with the index.
‼️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.
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/key-hint.tsx` around lines 45 - 49, The Fragment in
KeyHint maps keyList with <Fragment key={k}> which can collide when labels
repeat; change the key to a composite unique identifier (for example using the
index) such as `${k}-${i}` to avoid React reconciliation issues — locate the map
in the KeyHint component (keyList.map((k, i) => ...)) and replace the Fragment
key to use both the label and index (or just the index if appropriate) so each
rendered element has a stable, unique key.
_⚠️ Potential issue_ | _🟡 Minor_
**Prevent React key collisions for repeated labels in `KeyHint`.**
`<Fragment key={k}>` uses the label as the React key; if `keyList` contains duplicate labels (e.g. `"Shift"` twice), reconciliation can be incorrect. Use a composite key with the index.
<details>
<summary>Proposed fix</summary>
```diff
- {keyList.map((k, i) => (
- <Fragment key={k}>
+ {keyList.map((k, i) => (
+ <Fragment key={`${k}-${i}`}>
{i > 0 && (separator != null ? <> {separator} </> : ' ')}
<Kbd className={chipClass}>{k}</Kbd>
</Fragment>
))}
```
</details>
<!-- 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
{keyList.map((k, i) => (
<Fragment key={`${k}-${i}`}>
{i > 0 && (separator != null ? <> {separator} </> : ' ')}
<Kbd className={chipClass}>{k}</Kbd>
</Fragment>
```
</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/components/key-hint.tsx` around lines 45 - 49, The Fragment in
KeyHint maps keyList with <Fragment key={k}> which can collide when labels
repeat; change the key to a composite unique identifier (for example using the
index) such as `${k}-${i}` to avoid React reconciliation issues — locate the map
in the KeyHint component (keyList.map((k, i) => ...)) and replace the Fragment
key to use both the label and index (or just the index if appropriate) so each
rendered element has a stable, unique key.
```
</details>
<!-- fingerprinting:phantom:poseidon:hawk -->
<!-- cr-comment:v1:f704eb304db8d85483dc9879 -->
<!-- This is an auto-generated comment by CodeRabbit -->
✅ Addressed in commit dd850d2
#!/bin/bash
set -euo pipefail
FILE="js/desktop/src/components/ui/kbd.tsx"echo"== File =="
ls -l "$FILE"echo"== Lines 1-120 =="
nl -ba "$FILE"| sed -n '1,120p'
Repository: flowy-live/llink
Length of output: 235
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find usages of KbdGroup
rg -n --hidden --no-ignore-vcs "\bKbdGroup\b" js/desktop/src ||true
Repository: flowy-live/llink
Length of output: 240
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check if there's another KbdGroup defined elsewhere in repo
rg -n --hidden --no-ignore-vcs "function KbdGroup|const KbdGroup|export .*KbdGroup" js ||true
Repository: flowy-live/llink
Length of output: 240
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="js/desktop/src/components/ui/kbd.tsx"echo"== Lines 1-60 =="
cat -n "$FILE"| sed -n '1,60p'
Repository: flowy-live/llink
Length of output: 1101
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Usage of the component in TSX/TS
rg -n --hidden --no-ignore-vcs "<KbdGroup\\b|KbdGroup\\s*\\(" js/desktop/src ||true
Repository: flowy-live/llink
Length of output: 174
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look for the slot attribute usage to understand expected semantics
rg -n --hidden --no-ignore-vcs "data-slot=['\"]kbd-group['\"]" js/desktop/src ||true
Repository: flowy-live/llink
Length of output: 128
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find all usages of KbdGroup (JS/TSX)
rg -n --hidden --no-ignore-vcs "\b<KbdGroup\b|\bKbdGroup\s*\(" js/desktop/src ||true
Repository: flowy-live/llink
Length of output: 174
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find all occurrences of the data-slot value to infer expected markup
rg -n --hidden --no-ignore-vcs "data-slot=['\"]kbd-group['\"]|kbd-group" js/desktop/src ||true
Repository: flowy-live/llink
Length of output: 128
Fix KbdGroup element/prop-type mismatch
KbdGroup is typed as React.ComponentProps<'div'> but renders a <kbd>, creating an API contract mismatch for consumers. Render a <div> (or change the props typing to 'kbd' if <kbd> is intentional).
‼️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.
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/kbd.tsx` around lines 16 - 23, KbdGroup is typed
as React.ComponentProps<'div'> but returns a <kbd>, causing a prop-type
mismatch; fix by making the component's props match the rendered element: either
change the render to a <div> in the KbdGroup function or update the props typing
to React.ComponentProps<'kbd'> (adjust any consumers if needed), ensuring the
component signature (KbdGroup) and spread {...props} align with the element
type.
_⚠️ Potential issue_ | _🟡 Minor_
<details>
<summary>🧩 Analysis chain</summary>
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
FILE="js/desktop/src/components/ui/kbd.tsx"
echo "== File =="
ls -l "$FILE"
echo "== Lines 1-120 =="
nl -ba "$FILE" | sed -n '1,120p'
```
Repository: flowy-live/llink
Length of output: 235
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
# Find usages of KbdGroup
rg -n --hidden --no-ignore-vcs "\bKbdGroup\b" js/desktop/src || true
```
Repository: flowy-live/llink
Length of output: 240
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
# Check if there's another KbdGroup defined elsewhere in repo
rg -n --hidden --no-ignore-vcs "function KbdGroup|const KbdGroup|export .*KbdGroup" js || true
```
Repository: flowy-live/llink
Length of output: 240
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
FILE="js/desktop/src/components/ui/kbd.tsx"
echo "== Lines 1-60 =="
cat -n "$FILE" | sed -n '1,60p'
```
Repository: flowy-live/llink
Length of output: 1101
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
# Usage of the component in TSX/TS
rg -n --hidden --no-ignore-vcs "<KbdGroup\\b|KbdGroup\\s*\\(" js/desktop/src || true
```
Repository: flowy-live/llink
Length of output: 174
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
# Look for the slot attribute usage to understand expected semantics
rg -n --hidden --no-ignore-vcs "data-slot=['\"]kbd-group['\"]" js/desktop/src || true
```
Repository: flowy-live/llink
Length of output: 128
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
# Find all usages of KbdGroup (JS/TSX)
rg -n --hidden --no-ignore-vcs "\b<KbdGroup\b|\bKbdGroup\s*\(" js/desktop/src || true
```
Repository: flowy-live/llink
Length of output: 174
---
🏁 Script executed:
```shell
#!/bin/bash
set -euo pipefail
# Find all occurrences of the data-slot value to infer expected markup
rg -n --hidden --no-ignore-vcs "data-slot=['\"]kbd-group['\"]|kbd-group" js/desktop/src || true
```
Repository: flowy-live/llink
Length of output: 128
---
</details>
**Fix `KbdGroup` element/prop-type mismatch**
`KbdGroup` is typed as `React.ComponentProps<'div'>` but renders a `<kbd>`, creating an API contract mismatch for consumers. Render a `<div>` (or change the props typing to `'kbd'` if `<kbd>` is intentional).
<details>
<summary>Proposed fix</summary>
```diff
function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
- <kbd
+ <div
data-slot="kbd-group"
className={cn('inline-flex items-center gap-1', className)}
{...props}
- />
+ />
);
}
```
</details>
<!-- 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
function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="kbd-group"
className={cn('inline-flex items-center gap-1', className)}
{...props}
/>
);
}
```
</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/components/ui/kbd.tsx` around lines 16 - 23, KbdGroup is typed
as React.ComponentProps<'div'> but returns a <kbd>, causing a prop-type
mismatch; fix by making the component's props match the rendered element: either
change the render to a <div> in the KbdGroup function or update the props typing
to React.ComponentProps<'kbd'> (adjust any consumers if needed), ensuring the
component signature (KbdGroup) and spread {...props} align with the element
type.
```
</details>
<!-- fingerprinting:phantom:poseidon:hawk -->
<!-- cr-comment:v1:31d6b9ecbf02c9fde611d95b -->
<!-- This is an auto-generated comment by CodeRabbit -->
#!/bin/bash
set -euo pipefail
# Inspect the referenced regions in stream-view.tsxFILE="js/desktop/src/features/particles/stream-view.tsx"echo"=== stream-view.tsx: lines 120-160 ==="
nl -ba "$FILE"| sed -n '120,170p'echo"=== stream-view.tsx: lines 300-360 ==="
nl -ba "$FILE"| sed -n '300,360p'echo"=== stream-view.tsx: lines 590-630 ==="
nl -ba "$FILE"| sed -n '590,630p'# Locate the hooks mentioned in the original commentecho"=== Locate useStreamNavigationKeys / useStreamActionKeys ==="
rg -n --hidden --no-ignore -S "useStreamNavigationKeys|useStreamActionKeys""js/desktop/src/features/particles"||true
rg -n --hidden --no-ignore -S "useStreamNavigationKeys|useStreamActionKeys""js/desktop/src"||true# Search for Escape handler implementations across stream-related hooks/filesecho"=== Search for Escape handlers and navigation behavior ==="
rg -n -S --hidden --no-ignore \
-e "Escape|Esc"\
-e "key ==="\
-e "navigate\(-1\)"\
-e "navigate\(\`/\\$\\{networkId\\}\\`\)"\
js/desktop/src/features/particles js/desktop/src | head -n 200# Specifically search for navigate(-1) usage in stream navigation contextecho"=== Search navigate(-1) in stream-related code ==="
rg -n -S --hidden --no-ignore "navigate\(-1\)" js/desktop/src/features/particles js/desktop/src | head -n 200# Try to find the hook file names matching use-stream-*.ts mentioned by the original scriptecho"=== List files matching use-stream-*.ts ==="
fd -t f "use-stream-*.ts" js/desktop/src ||true
Repository: flowy-live/llink
Length of output: 160
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="js/desktop/src/features/particles/stream-view.tsx"echo"=== stream-view.tsx: inspect around ~line 136 (keybinding description) ==="
sed -n '120,170p'"$FILE"echoecho"=== stream-view.tsx: inspect around ~line 330 (exit countdown) ==="
sed -n '300,360p'"$FILE"echoecho"=== stream-view.tsx: inspect around ~line 610 (KeyHint Esc) ==="
sed -n '590,630p'"$FILE"echoecho"=== Find keybinding description text ==="
rg -n --hidden --no-ignore "Back to network|network root|navigate\\(-1\\)|navigate\\(\\`/\\$\\{networkId\\}\\`\\)|Escape|Esc""$FILE"||trueechoecho"=== Locate useStreamNavigationKeys / useStreamActionKeys definitions/calls ==="
rg -n --hidden --no-ignore -S "useStreamNavigationKeys|useStreamActionKeys" js/desktop/src/features/particles js/desktop/src ||trueechoecho"=== Search for Escape/Esc key handlers + navigate(...) patterns across stream-related code ==="
rg -n --hidden --no-ignore -S \
-e "key === 'Escape'|key === \"Escape\"|key === 'Esc'|key === \"Esc\"|Escape|Esc"\
-e "navigate\\(-1\\)"\
-e "navigate\\(\\`/\\$\\{networkId\\}\\`\\)"\
js/desktop/src/features/particles js/desktop/src | head -n 200echoecho"=== List files matching use-stream-*.ts (if any) ==="
fd -t f "use-stream-*.ts" js/desktop/src ||trueechoecho"=== List any use-stream* files under particles ==="
fd -t f "use-stream*" js/desktop/src/features/particles ||true
Repository: flowy-live/llink
Length of output: 6669
🏁 Script executed:
#!/bin/bash
set -euo pipefail
NAV_KEYS="js/desktop/src/hooks/use-stream-navigation-keys.ts"ACTION_KEYS="js/desktop/src/hooks/use-stream-action-keys.ts"STREAM_VIEW="js/desktop/src/features/particles/stream-view.tsx"echo"=== rg: Escape/Esc in navigation/action hooks ==="
rg -n --hidden --no-ignore -S "Escape|Esc""$NAV_KEYS""$ACTION_KEYS"||trueechoecho"=== rg: navigate(-1) in navigation/action hooks ==="
rg -n --hidden --no-ignore -S "navigate\(-1\)""$NAV_KEYS""$ACTION_KEYS"||trueechoecho"=== rg: navigate(\`/${networkId}\`) in navigation/action hooks ==="# Use single quotes to avoid backtick command substitution
rg -n --hidden --no-ignore -S 'navigate\(`\/\$\{networkId\}\`\)'"$NAV_KEYS""$ACTION_KEYS"||trueechoecho"=== Show navigation hook Escape handler block (lines around match) ==="
rg -n --hidden --no-ignore -S "Escape|Esc""$NAV_KEYS" -C 8||trueechoecho"=== Show action hook Escape handler block (lines around match) ==="
rg -n --hidden --no-ignore -S "Escape|Esc""$ACTION_KEYS" -C 8||trueechoecho"=== Also check stream-view where hooks are used (Esc/keybinding wiring) ==="
rg -n --hidden --no-ignore -S "useStreamNavigationKeys|useStreamActionKeys|handleExitNavigate|exit countdown|Esc|Escape""$STREAM_VIEW" -C 3
Repository: flowy-live/llink
Length of output: 512
🏁 Script executed:
#!/bin/bash
set -euo pipefail
NAV_KEYS="js/desktop/src/hooks/use-stream-navigation-keys.ts"ACTION_KEYS="js/desktop/src/hooks/use-stream-action-keys.ts"STREAM_VIEW="js/desktop/src/features/particles/stream-view.tsx"echo"=== stream-navigation-keys.ts (full file if small, else around Escape case) ==="
wc -l "$NAV_KEYS"
sed -n '1,120p'"$NAV_KEYS"echoecho"=== stream-action-keys.ts (full file if small, else around Escape case) ==="
wc -l "$ACTION_KEYS"
sed -n '1,160p'"$ACTION_KEYS"echoecho"=== stream-view.tsx: where hooks are wired (around lines ~240-320) ==="
sed -n '220,320p'"$STREAM_VIEW"echoecho"=== stream-view.tsx: where exit countdown and handleExitNavigate are defined (around ~320-340) ==="
sed -n '320,360p'"$STREAM_VIEW"echoecho"=== Find navigate(-1) occurrences in stream-view.tsx ==="
rg -n "navigate\\(-1\\)""$STREAM_VIEW"||trueechoecho"=== Find handleExitNavigate / navigate(\\`/\\$\\{networkId\\}\\`) occurrences in stream-view.tsx ==="
rg -n "handleExitNavigate|navigate\\(\\`/\\$\\{networkId\\}\\`\\)""$STREAM_VIEW"||true
Repository: flowy-live/llink
Length of output: 9501
Fix “Esc / Back to network” navigation to go to the network root
KeyHint for Esc calls navigate(-1) (js/desktop/src/features/particles/stream-view.tsx around 610-616), and the useStreamNavigationKeys Escape handler also uses navigate(-1) (js/desktop/src/hooks/use-stream-navigation-keys.ts around case 'Escape').
This conflicts with the keybinding/KeyHint copy (“Back to network”) and the exit countdown behavior, which navigates to navigate(\/${networkId}`)(js/desktop/src/features/particles/stream-view.tsx around thehandleExitNavigate`).
If the stream page wasn’t reached via history from the network root, Esc may take the user to the wrong place.
Change the Escape click/keyboard handler to navigate(\/${networkId}`)` for consistency with the exit flow, or update the UI copy/title to match “history back”.
🤖 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/features/particles/stream-view.tsx` around lines 610 - 616,
The Escape action currently uses history back (navigate(-1)) which can leave
users off the network root; update the Escape handlers to navigate to the
network root instead: replace navigate(-1) in the KeyHint onClick (KeyHint in
stream-view.tsx) and the Escape case in useStreamNavigationKeys to call
navigate(`/${networkId}`) (use the same networkId used by handleExitNavigate) so
the keybinding, title, and exit countdown all consistently go to the network
root; alternatively, if you prefer history semantics, change the KeyHint
title/text from “Back to network” to “Back”/“History back” instead.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #186
Summary by CodeRabbit
New Features
Style
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:
e0523d50-f395-4995-bba9-b64c341a2f72📥 Commits
Reviewing files that changed from the base of the PR and between
dd850d28d1and1cf144bce3.📒 Files selected for processing (2)
js/desktop/src/features/particles/stream-view.tsxjs/desktop/src/hooks/use-stream-navigation-keys.ts📝 Walkthrough
Walkthrough
Adds Kbd primitives and a reusable KeyHint component, then replaces inline hint markup across overlays, compose flows, stream view, and other UI with KeyHint instances that optionally handle clicks and preserve focus.
Changes
KeyHint Foundation and Keyboard Shortcut Consolidation
js/desktop/src/components/ui/kbd.tsx,js/desktop/src/components/key-hint.tsxKbdandKbdGroupprimitives andKeyHintcomponent render key chips, supportprefix/separator/children, and render as a non-interactivespanor clickablebuttonwith focus-preservingonMouseDownhandling.js/desktop/src/components/confirm-destructive-overlay.tsx,js/desktop/src/components/keybindings-overlay.tsx,js/desktop/src/features/particles/rename-stream-overlay.tsx,js/desktop/src/features/particles/stream-members-overlay.tsx<kbd>close/navigation hints withKeyHintelements wired toonCloseor equivalent handlers and updated title/aria text.js/desktop/src/features/compose/text-editor.tsx,js/desktop/src/features/compose/recording-overlay.tsx,js/desktop/src/features/compose/compose-overlay.tsx,js/desktop/src/features/compose/configure-stream-step.tsxKeyHintfor Esc, Enter, Meta+Enter, Meta+M, and other hints, preserving conditional rendering and existing click/keyboard handlers.js/desktop/src/components/video-audio-toggle.tsx,js/desktop/src/features/attachments/attachment-lightbox.tsx,js/desktop/src/components/keybindings-overlay.tsx,js/desktop/src/features/network-root.tsx<kbd>/button markup toKeyHint, delegating click behavior to existing handlers and adding title/aria attributes where applicable.js/desktop/src/features/particles/stream-view.tsx,js/desktop/src/hooks/use-stream-navigation-keys.tsStreamViewControlsrefactored to useKeyHintfor in-stream actions and receivesonOpenHuddleandonExitcallbacks;useStreamNavigationKeysnow acceptsonExitand calls it on Escape.🎯 3 (Moderate) | ⏱️ ~22 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
#186by implementing clickable keyboard shortcut hints via the new KeyHint component, allowing mouse-click alternatives throughout the UI.✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
📝 Generate docstrings
🧪 Generate unit tests (beta)
worktree-harmonic-purring-foxComment
@coderabbitai helpto get the list of available commands and usage tips.Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID:
390544c3-d236-4d15-982a-436bf62b136b📥 Commits
Reviewing files that changed from the base of the PR and between
9fd7e611f3and023e332e01.📒 Files selected for processing (14)
js/desktop/src/components/confirm-destructive-overlay.tsxjs/desktop/src/components/key-hint.tsxjs/desktop/src/components/keybindings-overlay.tsxjs/desktop/src/components/ui/kbd.tsxjs/desktop/src/components/video-audio-toggle.tsxjs/desktop/src/features/attachments/attachment-lightbox.tsxjs/desktop/src/features/compose/compose-overlay.tsxjs/desktop/src/features/compose/configure-stream-step.tsxjs/desktop/src/features/compose/recording-overlay.tsxjs/desktop/src/features/compose/text-editor.tsxjs/desktop/src/features/network-root.tsxjs/desktop/src/features/particles/rename-stream-overlay.tsxjs/desktop/src/features/particles/stream-members-overlay.tsxjs/desktop/src/features/particles/stream-view.tsx@@ -0,0 +46,4 @@<Fragment key={`${k}-${i}`}>{i > 0 && (separator != null ? <> {separator} </> : ' ')}<Kbd className={chipClass}>{k}</Kbd></Fragment>⚠️ Potential issue | 🟡 Minor
Prevent React key collisions for repeated labels in
KeyHint.<Fragment key={k}>uses the label as the React key; ifkeyListcontains duplicate labels (e.g."Shift"twice), reconciliation can be incorrect. Use a composite key with the index.Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents
✅ Addressed in commit
dd850d2@@ -0,0 +20,4 @@className={cn('inline-flex items-center gap-1', className)}{...props}/>);⚠️ Potential issue | 🟡 Minor
🧩 Analysis chain
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 235
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 240
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 240
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 1101
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 174
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 128
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 174
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 128
Fix
KbdGroupelement/prop-type mismatchKbdGroupis typed asReact.ComponentProps<'div'>but renders a<kbd>, creating an API contract mismatch for consumers. Render a<div>(or change the props typing to'kbd'if<kbd>is intentional).Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents
⚠️ Potential issue | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 42
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 160
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 6669
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 512
🏁 Script executed:
Repository: flowy-live/llink
Length of output: 9501
Fix “Esc / Back to network” navigation to go to the network root
KeyHintforEsccallsnavigate(-1)(js/desktop/src/features/particles/stream-view.tsx around 610-616), and theuseStreamNavigationKeysEscape handler also usesnavigate(-1)(js/desktop/src/hooks/use-stream-navigation-keys.ts around case'Escape').navigate(\/${networkId}`)(js/desktop/src/features/particles/stream-view.tsx around thehandleExitNavigate`).Escmay take the user to the wrong place.Change the Escape click/keyboard handler to
navigate(\/${networkId}`)` for consistency with the exit flow, or update the UI copy/title to match “history back”.🤖 Prompt for AI Agents
✅ Addressed in commit
1cf144b