Skip to content

Conversation

@zonetecde
Copy link
Collaborator

@zonetecde zonetecde commented Oct 26, 2025

Summary

Fixes #QF-2769

Ensure copying the Arabic verse from 'Quran in a Year' section add spaces between words.
Added a custom onCopy handler that gathers the selected Arabic words and copies them as a space-separated string.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Test plan

I've added a playwright test in this commit to ensure the verse is copied with spaces.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • My changes generate no new warnings
  • Any dependent changes have been merged and published in downstream modules
  • I have commented on my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Screenshots or videos

Before After
وَقَالَٱلرَّسُولُيَٰرَبِّإِنَّقَوۡمِيٱتَّخَذُواْهَٰذَاٱلۡقُرۡءَانَمَهۡجُورٗا٣٠ وَقَالَ ٱلرَّسُولُ يَـٰرَبِّ إِنَّ قَوْمِى ٱتَّخَذُوا۟ هَـٰذَا ٱلْقُرْءَانَ مَهْجُورًۭا ٣٠

Summary by CodeRabbit

  • New Features
    • Added the ability to copy selected verse text; copying adapts to the active font to produce accurate text output.
  • Enhancements
    • Improved selection support within verse text (DOM indexing exposed) to ensure copied selections preserve original word order and formatting.

@zonetecde zonetecde requested a review from osamasayed October 26, 2025 16:50
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +68 to +72
if (!selectedNodes.length) {
return;
}

event.preventDefault();

Choose a reason for hiding this comment

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

P1 Badge Allow copying translations without Arabic override

The new handleCopy prevents the default copy action whenever any [data-word-index] element is selected and replaces the clipboard contents with Arabic words. Word-by-word translations and transliterations are rendered inside these same containers, so selecting only the translation text now also triggers preventDefault() and copies Arabic instead. This regression makes it impossible to copy translation/transliteration text in views like the word-by-word modal. Consider guarding the copy interception the way onCopyQuranWords does—only override the clipboard when the selection actually includes Arabic word nodes.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 26, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds word indexing to verse-word elements and a clipboard copy handler on the verse container. Each PlainVerseTextWord receives a wordIndex prop exposed as data-word-index; the parent onCopy handler gathers selected word elements by that attribute, sorts by index, extracts font-specific text, and copies the joined text to the clipboard.

Changes

Cohort / File(s) Change Summary
Word index exposure
src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
Adds wordIndex: number to Props; destructures wordIndex and sets data-word-index={wordIndex} on the root word container element.
Copy handler and word indexing
src/components/Verse/PlainVerseText/index.tsx
Adds onCopy handler wired to the verse container that: queries selected word nodes via data-word-index, sorts by index, maps each to a font-specific text field using getWordTextFieldNameByFont, joins and copies text using clipboardCopy. Adds imports (useCallback, clipboardCopy, getWordTextFieldNameByFont) and passes wordIndex to each PlainVerseTextWord. Also adjusts word-text extraction to use the font-specific field with safe fallbacks and trimming.

Sequence Diagram

sequenceDiagram
    participant User
    participant VerseContainer as Verse Container (DOM)
    participant OnCopy as onCopy Handler
    participant Clipboard as Clipboard API

    User->>VerseContainer: Select text (one or more words) + Copy
    VerseContainer->>OnCopy: onCopy event
    activate OnCopy
    OnCopy->>VerseContainer: querySelectorAll('[data-word-index]') within selection
    VerseContainer-->>OnCopy: list of word elements with indices
    OnCopy->>OnCopy: sort elements by data-word-index
    OnCopy->>OnCopy: map each element -> word[textFieldName] (font-specific) or fallback
    OnCopy->>Clipboard: clipboardCopy(joinedWords)
    Clipboard-->>User: clipboard contains selected text
    deactivate OnCopy
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to check:
    • Correct setting and retrieval of data-word-index on each word element
    • Robustness of selection querying (partial selections, non-word nodes)
    • getWordTextFieldNameByFont fallback behavior and trimming
    • Clipboard copy error handling and cross-browser behavior

Suggested reviewers

  • osamasayed

Poem

🐰 I hopped through letters, counted each one,

marked every word till the copying's done.
With a twitch and a thump the selection aligns,
then off to the clipboard — neat rows and fine lines. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: fixing Arabic verse copying to include spaces between words in the 'Quran in a Year' section.
Description check ✅ Passed The PR description includes all required sections: Summary with issue reference, Type of change, Test plan with link to test commit, complete Checklist, and Before/After screenshots demonstrating the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch QF-2769

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1741416 and e5c609c.

📒 Files selected for processing (1)
  • src/components/Verse/PlainVerseText/index.tsx (5 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.*

📄 CodeRabbit inference engine (.cursor/rules/bug-handling-with-todo-comments.mdc)

If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems.

**/*.*: Utilize Early Returns: Use early returns to avoid nested conditions and improve readability.
Conditional Classes: Prefer conditional classes over ternary operators for class attributes.

**/*.*: Use comments sparingly, and when you do, make them meaningful.
Don't comment on obvious things. Excessive or unclear comments can clutter the codebase and become outdated.
Use comments to convey the 'why' behind specific actions or explain unusual behavior and potential pitfalls.
Provide meaningful information about the function's behavior and explain unusual behavior and potential pitfalls.

**/*.*: Write short functions that only do one thing.
Follow the single responsibility principle (SRP), which means that a function should have one purpose and perform it effectively.
If a function becomes too long or complex, consider breaking it into smaller, more manageable functions.

Order functions with those that are composing other functions appearing earlier in the file. For example, if you have a menu with multiple buttons, define the menu function above the buttons.

**/*.*: Always add helpful comments to the code explaining what you are doing.
Never delete old comments, unless they are no longer relevant because the code has been rewritten or deleted.

**/*.*: Choose names for variables, functions, and classes that reflect their purpose and behavior.
A name should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
Use specific names that provide a clearer understanding of what the variables represent and how they are used.

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/next-js-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Rely on Next.js Pages Router for state changes.
Minimize 'use client' usage: Prefer server components and Next.js SSR features.
Minimize 'use client' usage: Use 'use client' only for Web API access in small components.
Minimize 'use client' usage: Avoid using 'use client' for data fetching or state management.

**/*.{js,jsx,ts,tsx}: Optimize Web Vitals (LCP, CLS, FID)
Use dynamic loading for non-critical components using @src/components/dls/Spinner/Spinner.tsx

Files:

  • src/components/Verse/PlainVerseText/index.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-functional-components.mdc)

src/components/**/*.tsx: Always use React functional components with hooks.
Use React.FC for functional components with props.

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.{tsx,jsx}: Use functional components over class components
Keep components small and focused
Extract reusable logic into custom hooks
Use composition over inheritance
Split large components into smaller, focused ones
Follow the Rules of Hooks
Use custom hooks for reusable logic
Use appropriate dependency arrays in useEffect
Implement cleanup in useEffect when needed
Avoid nested hooks
Use useState for local component state
Avoid prop drilling through proper state management
Implement proper memoization (useMemo, useCallback)
Use React.memo for expensive components
Avoid unnecessary re-renders
Implement proper lazy loading
Use proper key props in lists
Profile and optimize render performance
Show appropriate loading and error states
Handle async errors properly
Show user-friendly error messages
Implement proper fallback UI
Log errors appropriately
Handle edge cases gracefully
Use semantic HTML elements
Implement proper ARIA attributes
Ensure keyboard navigation
Handle focus management
Provide proper alt text for images
Use proper imports/exports
Document complex component logic

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement proper prop types with TypeScript

**/*.tsx: Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
Implement proper error boundaries

React: use functional components only; extract reusable logic into custom hooks; apply proper memoization (React.memo/useMemo/useCallback)

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Use xstate for complex state logic

**/*.{ts,tsx}: TypeScript: prefer interfaces over type aliases for object shapes; avoid any; add explicit return types for public functions
Error handling: define and use custom error types; use async/await correctly; show user-friendly error messages
Use Redux Toolkit for state management and XState for complex state machines
Implement i18n with next-translate in components and utilities

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,d.ts}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
Avoid using any, prefer unknown for unknown types
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Create custom error types for domain-specific errors
Use Result types for operations that can fail
Use try-catch blocks with typed catch clauses
Handle Promise rejections properly

Files:

  • src/components/Verse/PlainVerseText/index.tsx
{src,types}/**/*.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript throughout the codebase with strict configuration

Files:

  • src/components/Verse/PlainVerseText/index.tsx
src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer React functional components for UI implementation

Files:

  • src/components/Verse/PlainVerseText/index.tsx
{src,types}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use path aliases @/ for src and @/dls/* for design system imports

Files:

  • src/components/Verse/PlainVerseText/index.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Organize React components by feature and place DLS components under src/components/dls

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Order imports: React first, then external, then internal; alphabetize within groups with blank lines between groups
Naming: camelCase for variables/functions; PascalCase for types, interfaces, and React components; UPPER_CASE for constants
Functions should be <= 30 lines, prefer early returns, single responsibility, and descriptive names

Files:

  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx,js,jsx,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier formatting: single quotes, 2-space indentation, 100-character line width, and trailing commas

Files:

  • src/components/Verse/PlainVerseText/index.tsx
🧠 Learnings (17)
📓 Common learnings
Learnt from: afifvdin
Repo: quran/quran.com-frontend-next PR: 2540
File: src/components/Verse/PlainVerseText/index.tsx:4-4
Timestamp: 2025-10-19T11:34:07.609Z
Learning: In src/components/Verse/PlainVerseText/index.tsx, the translation for verse titles should be wired directly to the component using next-translate rather than passed as props from parent components, as this ensures the wording from locales is centralized and only needs to be changed in one place.
📚 Learning: 2025-10-19T11:34:07.609Z
Learnt from: afifvdin
Repo: quran/quran.com-frontend-next PR: 2540
File: src/components/Verse/PlainVerseText/index.tsx:4-4
Timestamp: 2025-10-19T11:34:07.609Z
Learning: In src/components/Verse/PlainVerseText/index.tsx, the translation for verse titles should be wired directly to the component using next-translate rather than passed as props from parent components, as this ensures the wording from locales is centralized and only needs to be changed in one place.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-07-31T15:31:22.667Z
Learnt from: AhmedCodeGuy
Repo: quran/quran.com-frontend-next PR: 2477
File: src/components/QuranReader/TranslationView/TranslationViewCell.module.scss:204-207
Timestamp: 2025-07-31T15:31:22.667Z
Learning: AhmedCodeGuy suggested that the Separator component should be refactored in the future to avoid needing !important declarations for width overrides in contexts like .verseSeparator in TranslationViewCell.module.scss.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-22T20:33:55.176Z
Learnt from: yousefdergham
Repo: quran/quran.com-frontend-next PR: 2547
File: src/components/Course/CourseDetails/Tabs/Syllabus/index.tsx:44-49
Timestamp: 2025-10-22T20:33:55.176Z
Learning: Preference: In quran.com-frontend-next, keep concise “why” comments for analytics/UX decision points (e.g., guest vs enrolled click handlers) but avoid verbose JSDoc that repeats params/types. Convert multi-line JSDoc to brief inline rationale comments, especially in src/components/Course/CourseDetails/Tabs/Syllabus/index.tsx and similar analytics handlers.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-07T08:38:28.343Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T08:38:28.343Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Functions should be <= 30 lines, prefer early returns, single responsibility, and descriptive names

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-20T15:16:53.776Z
Learnt from: afifvdin
Repo: quran/quran.com-frontend-next PR: 2540
File: src/components/QuranReader/TranslationView/TranslationText/TranslationAndReference.tsx:14-23
Timestamp: 2025-10-20T15:16:53.776Z
Learning: In the quran.com-frontend-next codebase, the `direction` property should be typed as `string` rather than using union types like `'ltr' | 'rtl'` to maintain consistency with the established codebase pattern (e.g., LanguageData interface in src/utils/locale.ts).

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-07T08:38:28.343Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T08:38:28.343Z
Learning: Applies to **/*.tsx : React: use functional components only; extract reusable logic into custom hooks; apply proper memoization (React.memo/useMemo/useCallback)

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:50:54.518Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/react.mdc:0-0
Timestamp: 2025-08-01T08:50:54.518Z
Learning: Applies to **/*.{tsx,jsx} : Implement proper memoization (useMemo, useCallback)

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:50:54.518Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/react.mdc:0-0
Timestamp: 2025-08-01T08:50:54.518Z
Learning: Applies to **/*.{tsx,jsx} : Use React.memo for expensive components

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:50:54.518Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/react.mdc:0-0
Timestamp: 2025-08-01T08:50:54.518Z
Learning: Applies to **/*.{tsx,jsx} : Use custom hooks for reusable logic

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:51:05.924Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/redux-toolkit-best-practices.mdc:0-0
Timestamp: 2025-08-01T08:51:05.924Z
Learning: Applies to src/redux/**/*.ts : Use Redux hooks (useSelector, useDispatch) in components.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:50:54.518Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/react.mdc:0-0
Timestamp: 2025-08-01T08:50:54.518Z
Learning: Applies to **/*.{tsx,jsx} : Extract reusable logic into custom hooks

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-18T11:22:53.139Z
Learnt from: jonybekov
Repo: quran/quran.com-frontend-next PR: 2537
File: src/redux/slices/session.ts:36-42
Timestamp: 2025-10-18T11:22:53.139Z
Learning: In Redux selectors, avoid calling functions that read from external state (cookies, localStorage, etc.) like `isLoggedIn()` from utils/auth/login. Instead, use reactive hooks like `useIsLoggedIn` directly in components and keep selectors focused only on Redux state to ensure proper memoization.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:50:22.602Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/react-functional-components.mdc:0-0
Timestamp: 2025-08-01T08:50:22.602Z
Learning: Applies to src/components/**/*.tsx : Always use React functional components with hooks.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-07T08:37:58.213Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T08:37:58.213Z
Learning: Applies to src/**/*.tsx : Prefer React functional components for UI implementation

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-08-01T08:50:54.518Z
Learnt from: CR
Repo: quran/quran.com-frontend-next PR: 0
File: .cursor/rules/react.mdc:0-0
Timestamp: 2025-08-01T08:50:54.518Z
Learning: Applies to **/*.{test,spec}.{tsx,jsx} : Use React Testing Library

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
📚 Learning: 2025-10-26T10:22:52.381Z
Learnt from: afifvdin
Repo: quran/quran.com-frontend-next PR: 2540
File: src/components/QuranReader/TranslationView/TranslationText/TranslationAndReference.tsx:60-60
Timestamp: 2025-10-26T10:22:52.381Z
Learning: In src/components/QuranReader/TranslationView/TranslationText/TranslationAndReference.tsx, verse references displayed alongside chapter names should use English/Western numerals only, not localized numerals, as per design specifications.

Applied to files:

  • src/components/Verse/PlainVerseText/index.tsx
🧬 Code graph analysis (1)
src/components/Verse/PlainVerseText/index.tsx (1)
src/utils/word.ts (1)
  • getWordTextFieldNameByFont (29-37)
🔇 Additional comments (3)
src/components/Verse/PlainVerseText/index.tsx (3)

1-1: LGTM: Imports are appropriate.

The added imports (useCallback, clipboardCopy, getWordTextFieldNameByFont) are correctly used for implementing the copy functionality.

Also applies to: 4-4, 19-19


117-121: LGTM: onCopy handler correctly attached.

The onCopy handler is appropriately attached to the verse text container div with proper styling and attributes.


122-122: LGTM: wordIndex correctly passed to PlainVerseTextWord.

The wordIndex from the map iteration is appropriately passed to PlainVerseTextWord in both rendering branches (QCF and non-QCF fonts), enabling the data-word-index attribute used by the copy handler.

Also applies to: 128-128, 147-147


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
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

♻️ Duplicate comments (1)
src/components/Verse/PlainVerseText/index.tsx (1)

68-72: Copy interception prevents copying translations/transliterations.

The current implementation prevents default copy behavior whenever any [data-word-index] element is in the selection. Since word-by-word translations and transliterations render inside these same containers (see PlainVerseTextWord component lines 26-31), selecting only translation text also triggers preventDefault() and copies Arabic instead. This makes it impossible to copy translation/transliteration text.

Consider checking whether the selection actually includes Arabic word nodes (e.g., by checking the selection's text content or target element classes) before preventing default copy behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a198d2 and 1741416.

📒 Files selected for processing (2)
  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx (1 hunks)
  • src/components/Verse/PlainVerseText/index.tsx (5 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.*

📄 CodeRabbit inference engine (.cursor/rules/bug-handling-with-todo-comments.mdc)

If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems.

**/*.*: Utilize Early Returns: Use early returns to avoid nested conditions and improve readability.
Conditional Classes: Prefer conditional classes over ternary operators for class attributes.

**/*.*: Use comments sparingly, and when you do, make them meaningful.
Don't comment on obvious things. Excessive or unclear comments can clutter the codebase and become outdated.
Use comments to convey the 'why' behind specific actions or explain unusual behavior and potential pitfalls.
Provide meaningful information about the function's behavior and explain unusual behavior and potential pitfalls.

**/*.*: Write short functions that only do one thing.
Follow the single responsibility principle (SRP), which means that a function should have one purpose and perform it effectively.
If a function becomes too long or complex, consider breaking it into smaller, more manageable functions.

Order functions with those that are composing other functions appearing earlier in the file. For example, if you have a menu with multiple buttons, define the menu function above the buttons.

**/*.*: Always add helpful comments to the code explaining what you are doing.
Never delete old comments, unless they are no longer relevant because the code has been rewritten or deleted.

**/*.*: Choose names for variables, functions, and classes that reflect their purpose and behavior.
A name should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
Use specific names that provide a clearer understanding of what the variables represent and how they are used.

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/next-js-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Rely on Next.js Pages Router for state changes.
Minimize 'use client' usage: Prefer server components and Next.js SSR features.
Minimize 'use client' usage: Use 'use client' only for Web API access in small components.
Minimize 'use client' usage: Avoid using 'use client' for data fetching or state management.

**/*.{js,jsx,ts,tsx}: Optimize Web Vitals (LCP, CLS, FID)
Use dynamic loading for non-critical components using @src/components/dls/Spinner/Spinner.tsx

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-functional-components.mdc)

src/components/**/*.tsx: Always use React functional components with hooks.
Use React.FC for functional components with props.

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.{tsx,jsx}: Use functional components over class components
Keep components small and focused
Extract reusable logic into custom hooks
Use composition over inheritance
Split large components into smaller, focused ones
Follow the Rules of Hooks
Use custom hooks for reusable logic
Use appropriate dependency arrays in useEffect
Implement cleanup in useEffect when needed
Avoid nested hooks
Use useState for local component state
Avoid prop drilling through proper state management
Implement proper memoization (useMemo, useCallback)
Use React.memo for expensive components
Avoid unnecessary re-renders
Implement proper lazy loading
Use proper key props in lists
Profile and optimize render performance
Show appropriate loading and error states
Handle async errors properly
Show user-friendly error messages
Implement proper fallback UI
Log errors appropriately
Handle edge cases gracefully
Use semantic HTML elements
Implement proper ARIA attributes
Ensure keyboard navigation
Handle focus management
Provide proper alt text for images
Use proper imports/exports
Document complex component logic

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement proper prop types with TypeScript

**/*.tsx: Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
Implement proper error boundaries

React: use functional components only; extract reusable logic into custom hooks; apply proper memoization (React.memo/useMemo/useCallback)

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Use xstate for complex state logic

**/*.{ts,tsx}: TypeScript: prefer interfaces over type aliases for object shapes; avoid any; add explicit return types for public functions
Error handling: define and use custom error types; use async/await correctly; show user-friendly error messages
Use Redux Toolkit for state management and XState for complex state machines
Implement i18n with next-translate in components and utilities

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,d.ts}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
Avoid using any, prefer unknown for unknown types
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Create custom error types for domain-specific errors
Use Result types for operations that can fail
Use try-catch blocks with typed catch clauses
Handle Promise rejections properly

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
{src,types}/**/*.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript throughout the codebase with strict configuration

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer React functional components for UI implementation

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
{src,types}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use path aliases @/ for src and @/dls/* for design system imports

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Organize React components by feature and place DLS components under src/components/dls

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Order imports: React first, then external, then internal; alphabetize within groups with blank lines between groups
Naming: camelCase for variables/functions; PascalCase for types, interfaces, and React components; UPPER_CASE for constants
Functions should be <= 30 lines, prefer early returns, single responsibility, and descriptive names

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
**/*.{ts,tsx,js,jsx,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier formatting: single quotes, 2-space indentation, 100-character line width, and trailing commas

Files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-19T11:34:07.609Z
Learnt from: afifvdin
PR: quran/quran.com-frontend-next#2540
File: src/components/Verse/PlainVerseText/index.tsx:4-4
Timestamp: 2025-10-19T11:34:07.609Z
Learning: In src/components/Verse/PlainVerseText/index.tsx, the translation for verse titles should be wired directly to the component using next-translate rather than passed as props from parent components, as this ensures the wording from locales is centralized and only needs to be changed in one place.

Applied to files:

  • src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx
  • src/components/Verse/PlainVerseText/index.tsx
🧬 Code graph analysis (1)
src/components/Verse/PlainVerseText/index.tsx (1)
src/utils/word.ts (1)
  • getWordTextFieldNameByFont (29-37)
🔇 Additional comments (2)
src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx (1)

10-10: LGTM! Clean integration of word indexing.

The addition of wordIndex as a prop and its exposure via the data-word-index attribute is straightforward and enables the parent component's copy functionality without modifying existing logic.

Also applies to: 18-18, 24-24

src/components/Verse/PlainVerseText/index.tsx (1)

118-122: Clean integration of copy handler and word indexing.

The onCopy handler is correctly wired to the verse container, and wordIndex is properly passed to each PlainVerseTextWord component instance. The integration is straightforward and consistent.

Also applies to: 123-123, 129-129, 148-148

Comment on lines 55 to 105
const handleCopy = useCallback(
// eslint-disable-next-line react-func/max-lines-per-function
(event: React.ClipboardEvent<HTMLDivElement>) => {
const selection = document.getSelection();
if (!selection || selection.isCollapsed) {
return;
}

const verseElement = event.currentTarget;
const selectedNodes = Array.from(
verseElement.querySelectorAll<HTMLElement>('[data-word-index]'),
).filter((node) => selection.containsNode(node, true));

if (!selectedNodes.length) {
return;
}

event.preventDefault();

const textFieldName = getWordTextFieldNameByFont(quranFont);
const copiedText = selectedNodes
.sort(
(firstNode, secondNode) =>
Number(firstNode.getAttribute('data-word-index')) -
Number(secondNode.getAttribute('data-word-index')),
)
.map((node) => {
const index = Number(node.getAttribute('data-word-index'));
if (Number.isNaN(index)) {
return '';
}
const word = words[index];
if (!word) {
return '';
}

const wordText =
word[textFieldName] ?? word.text ?? word.textUthmani ?? word.qpcUthmaniHafs ?? '';

return typeof wordText === 'string' ? wordText.trim() : '';
})
.filter((wordText) => wordText.length > 0);

if (!copiedText.length) {
return;
}

clipboardCopy(copiedText.join(' '));
},
[quranFont, words],
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Refactor handleCopy into smaller, focused functions.

The handleCopy function is 50 lines and performs multiple responsibilities: selection handling, node filtering, text extraction, and clipboard copying. This violates the single responsibility principle and the 30-line function guideline. Additionally, the complex selection logic lacks explanatory comments.

As per coding guidelines.

Consider breaking it down:

+// Extracts selected word nodes from the verse container
+const getSelectedWordNodes = (
+  verseElement: HTMLDivElement,
+  selection: Selection,
+): HTMLElement[] => {
+  return Array.from(
+    verseElement.querySelectorAll<HTMLElement>('[data-word-index]'),
+  ).filter((node) => selection.containsNode(node, true));
+};
+
+// Builds copied text from selected word nodes based on font settings
+const buildCopiedText = (
+  selectedNodes: HTMLElement[],
+  words: Word[],
+  quranFont: QuranFont,
+): string[] => {
+  const textFieldName = getWordTextFieldNameByFont(quranFont);
+  return selectedNodes
+    .sort(
+      (firstNode, secondNode) =>
+        Number(firstNode.getAttribute('data-word-index')) -
+        Number(secondNode.getAttribute('data-word-index')),
+    )
+    .map((node) => {
+      const index = Number(node.getAttribute('data-word-index'));
+      if (Number.isNaN(index)) return '';
+      
+      const word = words[index];
+      if (!word) return '';
+
+      const wordText =
+        word[textFieldName] ?? word.text ?? word.textUthmani ?? word.qpcUthmaniHafs ?? '';
+      return typeof wordText === 'string' ? wordText.trim() : '';
+    })
+    .filter((wordText) => wordText.length > 0);
+};
+
 const handleCopy = useCallback(
   (event: React.ClipboardEvent<HTMLDivElement>) => {
     const selection = document.getSelection();
-    if (!selection || selection.isCollapsed) {
-      return;
-    }
+    if (!selection || selection.isCollapsed) return;

-    const verseElement = event.currentTarget;
-    const selectedNodes = Array.from(
-      verseElement.querySelectorAll<HTMLElement>('[data-word-index]'),
-    ).filter((node) => selection.containsNode(node, true));
+    const selectedNodes = getSelectedWordNodes(event.currentTarget, selection);
+    if (!selectedNodes.length) return;

-    if (!selectedNodes.length) {
-      return;
-    }
-
     event.preventDefault();
-
-    const textFieldName = getWordTextFieldNameByFont(quranFont);
-    const copiedText = selectedNodes
-      .sort(
-        (firstNode, secondNode) =>
-          Number(firstNode.getAttribute('data-word-index')) -
-          Number(secondNode.getAttribute('data-word-index')),
-      )
-      .map((node) => {
-        const index = Number(node.getAttribute('data-word-index'));
-        if (Number.isNaN(index)) {
-          return '';
-        }
-        const word = words[index];
-        if (!word) {
-          return '';
-        }
-
-        const wordText =
-          word[textFieldName] ?? word.text ?? word.textUthmani ?? word.qpcUthmaniHafs ?? '';
-
-        return typeof wordText === 'string' ? wordText.trim() : '';
-      })
-      .filter((wordText) => wordText.length > 0);
-
-    if (!copiedText.length) {
-      return;
-    }
-
+    const copiedText = buildCopiedText(selectedNodes, words, quranFont);
+    if (!copiedText.length) return;
+    
     clipboardCopy(copiedText.join(' '));
   },
   [quranFont, words],
 );

@osamasayed osamasayed requested a review from Copilot November 15, 2025 21:19
Copilot finished reviewing on behalf of osamasayed November 15, 2025 21:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes an issue where copying Arabic verses from the 'Quran in a Year' section resulted in words being concatenated without spaces. The solution implements a custom copy handler that intercepts the copy event and reconstructs the selected text with proper spacing.

Key changes:

  • Added custom onCopy handler to intelligently copy Arabic text with spaces between words based on the selected font
  • Introduced data-word-index attributes to track word positions for accurate text reconstruction during copy operations

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/components/Verse/PlainVerseText/index.tsx Implements the custom handleCopy function that intercepts copy events, extracts selected words based on their indices, retrieves the appropriate text field based on the current font, and joins them with spaces before copying to clipboard
src/components/Verse/PlainVerseText/PlainVerseTextWord/index.tsx Adds wordIndex prop and data-word-index attribute to word containers to enable word identification during the copy operation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 91 to 92
const wordText =
word[textFieldName] ?? word.text ?? word.textUthmani ?? word.qpcUthmaniHafs ?? '';
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

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

The fallback chain for wordText may cause inconsistent behavior across different fonts. The similar implementation in onCopyQuranWords.ts (line 67) directly returns selectedWord[getWordTextFieldNameByFont(quranFont)] without fallbacks.

Consider simplifying this to match the existing pattern:

const wordText = word[textFieldName] ?? '';

The current fallback chain (word.text ?? word.textUthmani ?? word.qpcUthmaniHafs) could result in copying text from a different font than what the user is viewing, which may not match their intention.

Suggested change
const wordText =
word[textFieldName] ?? word.text ?? word.textUthmani ?? word.qpcUthmaniHafs ?? '';
const wordText = word[textFieldName] ?? '';

Copilot uses AI. Check for mistakes.
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.

2 participants