Skip to content

Feat/5914 | Add single click to expand/collapse memo content#5924

Open
KakshiDEV56 wants to merge 1 commit into
usememos:mainfrom
KakshiDEV56:feature/single-click-expand
Open

Feat/5914 | Add single click to expand/collapse memo content#5924
KakshiDEV56 wants to merge 1 commit into
usememos:mainfrom
KakshiDEV56:feature/single-click-expand

Conversation

@KakshiDEV56
Copy link
Copy Markdown

@KakshiDEV56 KakshiDEV56 commented May 1, 2026

Changes Made

  • Added enable_single_click_memo_expand setting to protobuf definitions
  • Added toggle switch in Settings → System page
  • Refactored useCompactMode to expose toggle function
  • Added click handler with guards for interactive elements
  • Added English localization text

Testing

  • Setting toggles on/off correctly
  • Setting persists after page refresh
  • Single-click expands/collapses when enabled
  • Links, buttons, images unaffected
  • Double-click edit still works
  • Text selection not interrupted

Settings Page

Memos-Settings

Demo

Memos.webm

Fixes #5914

- Add enable_single_click_memo_expand setting to proto
- Add toggle in Settings → System page
- Add click handler with guards for interactive elements
- Fixes usememos#5914
@KakshiDEV56 KakshiDEV56 requested a review from a team as a code owner May 1, 2026 20:12
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

Adds support for single-click memo expand/collapse via a new enable_single_click_memo_expand instance setting. Changes include protocol buffer definitions, backend conversion logic, frontend UI controls, context propagation, and single-click toggle implementation with interactive element exclusions.

Changes

Cohort / File(s) Summary
Protocol Definitions
proto/api/v1/instance_service.proto, proto/store/instance_setting.proto
Added enable_single_click_memo_expand boolean field (field 5) to memo-related instance setting messages in both API and store layers.
Backend Service
server/router/api/v1/instance_service.go
Updated conversion functions convertInstanceMemoRelatedSettingFromStore and convertInstanceMemoRelatedSettingToStore to map the new EnableSingleClickMemoExpand field between store and API models.
Component Types & Hooks
web/src/components/MemoContent/types.ts, web/src/components/MemoContent/hooks.ts
Added MemoContentCompactMode interface to component props; enhanced useCompactMode hook with optional resetKey parameter and memoized return value for stable object references.
Memo View Components
web/src/components/MemoView/MemoView.tsx, web/src/components/MemoView/MemoViewContext.tsx, web/src/components/MemoView/components/MemoBody.tsx, web/src/components/MemoPreview/MemoPreview.tsx
Added compactMode property to context; implemented single-click toggle handler excluding interactive elements (links, buttons, inputs, images); propagated compactMode through context and component chain.
Memo Content Component
web/src/components/MemoContent/index.tsx
Updated to use externally provided compactMode prop from context or fall back to internally computed mode based on compact prop.
Settings & Localization
web/src/components/Settings/MemoRelatedSettings.tsx, web/src/locales/en.json
Added toggle control for single-click memo expansion in settings UI and English localization entry for the new setting.
Generated Types
web/src/types/proto/api/v1/instance_service_pb.ts
Updated protobuf-generated TypeScript descriptor payload and InstanceSetting_MemoRelatedSetting type to include enableSingleClickMemoExpand field.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Title check ✅ Passed The title clearly and specifically describes the main feature: adding single-click expand/collapse memo content functionality.
Linked Issues check ✅ Passed The PR fully implements the requested feature from issue #5914: a configurable single-click expand/collapse option for memos.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the single-click memo expand/collapse feature requested in issue #5914.
Description check ✅ Passed The pull request description comprehensively outlines the changes made, including protobuf additions, UI toggles, hook refactoring, click handlers, and localization updates, directly aligned with the changeset.

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


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
web/src/components/MemoView/MemoViewContext.tsx (1)

8-8: 💤 Low value

Use the @/ alias for this cross-directory import.

Every other non-same-directory import in this file uses the @/ alias (e.g., @/types/proto/..., @/utils/media-item), but the new import uses a relative path that crosses into a sibling component directory.

✏️ Proposed fix
-import type { MemoContentCompactMode } from "../MemoContent/types";
+import type { MemoContentCompactMode } from "@/components/MemoContent/types";

As per coding guidelines: "Use @/ alias for absolute imports in frontend TypeScript/React files."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/MemoView/MemoViewContext.tsx` at line 8, The import of
MemoContentCompactMode uses a relative cross-directory path; replace it with the
project alias style (use the "@/..." alias) so it matches other
non-same-directory imports—update the import statement that brings in
MemoContentCompactMode (from ../MemoContent/types) to use the aliased path
(e.g., "@/components/MemoContent/types" or the appropriate "@/..." path in your
codebase) so all external imports in MemoViewContext.tsx follow the "@/..."
convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/MemoView/MemoView.tsx`:
- Around line 48-65: The single-click handler handleArticleClick currently uses
event.detail to try to ignore double-clicks but fires on the first click; change
it to schedule compactMode.toggle() via a short setTimeout (e.g., 200–300ms) and
store that timer in a ref (timerRef) so it can be cancelled; add a window
'dblclick' listener (registered/cleaned up in a useEffect) that clears timerRef
and prevents the pending toggle when a double-click occurs, and also clear the
timer on unmount and when handleArticleClick early-returns; keep references to
memoRelatedSetting.enableSingleClickMemoExpand and compactMode (and call
compactMode.toggle()) in the callback dependencies.

---

Nitpick comments:
In `@web/src/components/MemoView/MemoViewContext.tsx`:
- Line 8: The import of MemoContentCompactMode uses a relative cross-directory
path; replace it with the project alias style (use the "@/..." alias) so it
matches other non-same-directory imports—update the import statement that brings
in MemoContentCompactMode (from ../MemoContent/types) to use the aliased path
(e.g., "@/components/MemoContent/types" or the appropriate "@/..." path in your
codebase) so all external imports in MemoViewContext.tsx follow the "@/..."
convention.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 20812005-7196-4b51-bcc2-d8613cc37e2d

📥 Commits

Reviewing files that changed from the base of the PR and between 8daef1d and 8eafc14.

⛔ Files ignored due to path filters (3)
  • proto/gen/api/v1/instance_service.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • proto/gen/openapi.yaml is excluded by !**/gen/**
  • proto/gen/store/instance_setting.pb.go is excluded by !**/*.pb.go, !**/gen/**
📒 Files selected for processing (13)
  • proto/api/v1/instance_service.proto
  • proto/store/instance_setting.proto
  • server/router/api/v1/instance_service.go
  • web/src/components/MemoContent/hooks.ts
  • web/src/components/MemoContent/index.tsx
  • web/src/components/MemoContent/types.ts
  • web/src/components/MemoPreview/MemoPreview.tsx
  • web/src/components/MemoView/MemoView.tsx
  • web/src/components/MemoView/MemoViewContext.tsx
  • web/src/components/MemoView/components/MemoBody.tsx
  • web/src/components/Settings/MemoRelatedSettings.tsx
  • web/src/locales/en.json
  • web/src/types/proto/api/v1/instance_service_pb.ts

Comment on lines +48 to +65
const handleArticleClick = useCallback(
(event: React.MouseEvent<HTMLElement>) => {
if (!memoRelatedSetting.enableSingleClickMemoExpand || !compactMode.mode || event.defaultPrevented || event.detail > 1) {
return;
}

const target = event.target as HTMLElement;
if (target.closest("a, button, input, textarea, select, [role='button'], [data-ignore-memo-expand]") || target.tagName === "IMG") {
return;
}
if (window.getSelection()?.toString()) {
return;
}

compactMode.toggle();
},
[compactMode, memoRelatedSetting.enableSingleClickMemoExpand],
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l web/src/components/MemoView/MemoView.tsx

Repository: usememos/memos

Length of output: 103


🏁 Script executed:

cat -n web/src/components/MemoView/MemoView.tsx | head -200

Repository: usememos/memos

Length of output: 7270


🏁 Script executed:

rg -n "dblclick|double" web/src/components/MemoView/MemoView.tsx

Repository: usememos/memos

Length of output: 40


🏁 Script executed:

rg -n "openEditor|dblclick" web/src/components/MemoView/

Repository: usememos/memos

Length of output: 1433


🏁 Script executed:

rg -n "onDoubleClick|dblclick" web/src/components/MemoContent/

Repository: usememos/memos

Length of output: 358


🏁 Script executed:

rg -n "openEditor" web/src/components/MemoView/components/

Repository: usememos/memos

Length of output: 723


🏁 Script executed:

cat -n web/src/components/MemoView/hooks/useMemoHandlers.ts

Repository: usememos/memos

Length of output: 1651


🏁 Script executed:

cat -n web/src/components/MemoView/components/MemoBody.tsx | head -50

Repository: usememos/memos

Length of output: 2659


🏁 Script executed:

cat -n web/src/components/MemoContent/index.tsx | head -100

Repository: usememos/memos

Length of output: 5397


🏁 Script executed:

cat -n web/src/components/MemoContent/index.tsx | sed -n '40,90p'

Repository: usememos/memos

Length of output: 2390


🏁 Script executed:

# Verify browser event sequence for double-click: does first click fire before dblclick?
python3 <<'EOF'
print("Browser Double-Click Event Sequence (W3C Standard):")
print("=" * 50)
print("1. mousedown (first press)")
print("2. mouseup (first release)")  
print("3. click (detail=1) <- This fires before dblclick")
print("4. mousedown (second press)")
print("5. mouseup (second release)")
print("6. click (detail=2)")
print("7. dblclick")
print()
print("The key insight:")
print("- First 'click' event fires with detail=1 BEFORE 'dblclick' event")
print("- event.detail > 1 only catches the SECOND click (after dblclick)")
print("- So first click's toggle() executes before dblclick's preventDefault()/openEditor()")
EOF

Repository: usememos/memos

Length of output: 588


🏁 Script executed:

# Check if there's any stopPropagation or other event handling that might prevent bubbling
rg -n "stopPropagation|preventDefault|bubbl" web/src/components/MemoView/components/MemoBody.tsx

Repository: usememos/memos

Length of output: 40


🏁 Script executed:

# Verify the actual element structure and event attachment points
rg -A5 "onClick=\{handleArticleClick\}" web/src/components/MemoView/MemoView.tsx

Repository: usememos/memos

Length of output: 241


🏁 Script executed:

rg -n "stopPropagation" web/src/components/MemoView/components/MemoBody.tsx web/src/components/MemoContent/index.tsx

Repository: usememos/memos

Length of output: 40


Use a timeout to delay expand/collapse, canceling if a double-click occurs within the window.

Line 50's event.detail > 1 check suppresses only the second click event. On a real double-click sequence, the first click fires with detail === 1 before the dblclick event, so compactMode.toggle() executes during the first click. When the double-click edit handler fires moments later, the card has already expanded/collapsed and reflowed, creating unwanted visual motion before entering edit mode.

Replace the detail check with a debounce/timeout pattern that can be canceled by detecting a dblclick event within the window, ensuring single-click and double-click behaviors don't interfere.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/MemoView/MemoView.tsx` around lines 48 - 65, The
single-click handler handleArticleClick currently uses event.detail to try to
ignore double-clicks but fires on the first click; change it to schedule
compactMode.toggle() via a short setTimeout (e.g., 200–300ms) and store that
timer in a ref (timerRef) so it can be cancelled; add a window 'dblclick'
listener (registered/cleaned up in a useEffect) that clears timerRef and
prevents the pending toggle when a double-click occurs, and also clear the timer
on unmount and when handleArticleClick early-returns; keep references to
memoRelatedSetting.enableSingleClickMemoExpand and compactMode (and call
compactMode.toggle()) in the callback dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Single Click Expand

1 participant