Skip to content

[Feat]: Word-Level Navigation for Text-Heavy HTML in Live Preview #2244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

atondapu
Copy link

PR Type: Feature

#2239

Fixes #2239

Feature Description
This PR adds the ability to navigate directly to individual words in Live Preview rather than just sections. When enabled, clicking on a word in the Live Preview will position the editor cursor at the exact character offset of that word in the source code, making it easier to find and edit specific content in text-heavy documents.

Screenshots: Find attached

Screenshot 2025-04-25 at 5 41 36 PM

Breaking Change: No

Tests Done: Passes all listing, existing tests, underwent manual general and edge-case testing to ensure correct word is found when button is toggled. (works across different browsers, and OS as before)

Additional Information
The implementation extends the existing section-level click handler in Live Preview to calculate the exact word position. A new toggle button with a translucent document glyph allows users to switch between section-level and word-level navigation modes. Special care was taken to handle edge cases with duplicate words by using click coordinates to identify the precise text node and word instance.

Based on customer request to jump directly to individual words in Live Preview rather than just sections.
@abose
Copy link
Member

abose commented Apr 26, 2025

Thanks for the contributing @atondapu

To clarify the licensing situation, we'd like to request your explicit permission to distribute the changes you've submitted in this pull request.

Can you do one of the following to help me continue with this change?

  1. Sign the CLA at https://phcode-dev.github.io/contributor-license-agreement/ OR
  2. Just comment, "I grant permission to distribute my contributions in this pull request under MIT License Terms."

Copy link
Member

@abose abose left a comment

Choose a reason for hiding this comment

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

Thanks for this feature! I have left a few comments mainly to help me understand how this is implemented for reviewing.

Also how was this tested(In desktop app/browser)? Did you run the test suite?

I tested the code out and its working pretty well. But putting an extra button for this may not be the best UX discover-ability wise.

One way i think is more natural is to remove the text location button, and if the user clicks on an element that is not already selected, we select that element. and if an element is already selected, we do the word selection logic. This is similar to double click to edit text workflow(or click again on a selected element to locate text).

@@ -3,6 +3,7 @@
<div style="width: 20%;display: flex;">
<button id="reloadLivePreviewButton" title="{{clickToReload}}" class="btn-alt-quiet toolbar-button reload-icon"></button>
<button id="highlightLPButton" title="{{toggleLiveHighlight}}" class="btn-alt-quiet toolbar-button pointer-fill-icon"></button>
<button id="wordNavigationLPButton" title="{{toggleWordNavigation}}" class="btn-alt-quiet toolbar-button text-icon"></button>
Copy link
Member

Choose a reason for hiding this comment

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

We tend to avoid placing new buttons on the live preview unless its absolutely required to keep the UI clutter in check.

Since this can be the default behavior, please tie this to the already existing toggleLiveHighlight flag and remove the new button.

@@ -389,6 +411,7 @@ define(function (require, exports, module) {
$iframe = $panel.find("#panel-live-preview-frame");
$pinUrlBtn = $panel.find("#pinURLButton");
$highlightBtn = $panel.find("#highlightLPButton");
$wordNavigationBtn = $panel.find("#wordNavigationLPButton");
Copy link
Member

Choose a reason for hiding this comment

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

We don't need a new button for this, we can remove all these code as it can be tied to toggleLiveHighlight button.

@@ -31,6 +31,9 @@
"file.previewHighlight": [
"Ctrl-Shift-C"
],
"file.previewWordNavigation": [
Copy link
Member

Choose a reason for hiding this comment

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

We usually add shortcuts only if it is very frequently used. since people are not likely to toggle this feature, a shortcut isn't necessary.

@@ -302,13 +315,22 @@ define(function main(require, exports, module) {
_updateHighlightCheckmark();
});

PreferencesManager.stateManager.definePreference("livedevWordNavigation", "boolean", false)
Copy link
Member

Choose a reason for hiding this comment

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

reuse the livedevHighlight button that already exists for this flow.

@@ -147,31 +147,335 @@ define(function (require, exports, module) {
}
}

function _tagSelectedInLivePreview(tagId, nodeName, contentEditable, allSelectors) {
function _findWordInEditor(editor, word, nodeName, tagId, wordContext, wordOccurrenceIndex) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you give a high level overview of how this works in plain text? It will help me understand the changes. Specifically:

  1. How we identify the text in the remote live preview.
  2. How it communicates this position and finds the correct text position in the editor.

const nodeText = textNode.textContent;

// Function to extract a word and its occurrence index
function extractWordAndOccurrence(text, wordStart, wordEnd) {
Copy link
Member

Choose a reason for hiding this comment

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

what will happen if we don't do word search? is the text not not always the correct target on click?

}

// If occurrence index search failed or no occurrence index available, try context as a fallback
if (wordContext) {
Copy link
Member

Choose a reason for hiding this comment

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

i see that there are a few fallbacks here, What cases triggers these fullbacks? A few examples can help me understand the edge cases better. We may also need to add live preview integration tests for those cases later.

function getClickedWord(element, event) {

// Try to find the clicked position within the element
const range = document.caretRangeFromPoint(event.clientX, event.clientY);
Copy link
Member

Choose a reason for hiding this comment

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

This api doesn't work in Firefox, so maybe we can fallback to caretPositionFromPoint in Firefox(caretPositionFromPoint doesnt work in safari), or disable the feature in Firefox.

@abose
Copy link
Member

abose commented May 2, 2025

@atondapu This is a cool feature to have. Would you be able to check this out or need any assistance?

bartzjegr

This comment was marked as off-topic.

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.

[Feat]: Word-Level Navigation for Text-Heavy HTML in Live Preview
3 participants