LingQ web improvements: suggestions from a heavy user

As of today, I have a streak of 445.As a heavy LingQ user
I think there are many areas for improvement in LingQ’s web

I will list the areas I think can be improved and include my assessment of the degree of impact., for the programmers reference.

  1. Very disruptive to the experience
    the video interface is too small and cannot be resized. If I use the built-in function to enlarge it(show synchonized text or expend), will lose the ability to create LingQs.

  2. Very disruptive to the experience
    in page view, I think it would be helpful to add a feature, such as a play button next to each sentence, to directly jump and play the corresponding sentence in the video( Similar to the functionality of the [Language Reactor](https://www.languagereactor.com/) browser extension). This would eliminate the need to switch to sentence view mode to play specific sentences, and would also eliminate the need for frequent page turning, which would be very convenient. Especially when self-imported books have more than 500 sentences in sentence view mode, frequent page turning is really annoying. The effect is just like what I drew in the picture, for reference only.

  1. Occasionally disruptive to the experience
    when I import books, course has many pages. Let’s say the audio for a particular course is 30 minutes long, and I’m listening to the audio at the 15-minute mark. I don’t know which page the sentence being read in the audio is on page view mode at this time… I have to keep flipping through the pages to find which page the sentence the audio is reading is on. I hope you can add a feature to mark the specific page in page view mode that corresponds to the current audio being played,It can also be indicated with simple arrows whether the audio being read is before or after the current page.
8 Likes

PixPin_2025-03-11_22-24-42

The above suggestions are based on my experience using LingQ for over a year

This is not an official one, but you can try this add-on. It mitigates the problem you mentioned.

1 Like

Thank you very much , I tried the first Tampermonkey script in your post, it works great,the video much bigger than before but the second one has bugs and the text formatting is problematic.

1 Like

the second Tampermonkey script,There are some issues, but thankfully the first script is working fine.

This is the script I’m currently using.
Since I study most of the time with Youtuve video, the lower part of the window is assigned for the video.
Press Q key to expand the video. the video will fill the blank space.

In the screen shot you provided, it seems your text is not long enough to fill the entire width.

If you don’t like the fixed layout and want to move and resize the video, use the first script. It will work as you intended!

// ==UserScript==
// @name         LingQ Addon
// @description  Custom embedded player, and sort course automatically.
// @match        https://www.lingq.com/*/learn/*/web/reader/*
// @match        https://www.lingq.com/*/learn/*/web/library
// @match        https://www.lingq.com/*/learn/*/web/library/course/*
// @version      1.3
// ==/UserScript==

(function () {
  "use strict";
  
  // Style settings
  var style = document.createElement("style");
  style.textContent = `
  :root {
    --width_small: 440px;
    --height_small: 260px;
    --right_pos: 0.5%;
    --bottom_pos: 5.5%;
    
    --width_big: calc(100vw - 424px - 40px);
    --height_big: 470px;
    
    --font_size: 1.1rem;
    --font_color: #e0e0e0;
    --line_height: 1.7;
    
    --lingq_background: hsl(41 43% 30% / 0.7);
    --lingq_border: hsl(43 99% 64% / 0.3);
    --lingq_border_learned: hsl(43 99% 64% / 0.5);
    --blue_border: hsl(213 99% 64% / 0.5);
    
    --is_playing_underline: #ffffff;
    
    --grid-layout: 1fr var(--height_big) 80px;
    --video_margin: 0 0 90px 10px !important;
    --article_height: calc(var(--app-height) - 165px - var(--height_big));
  }
  
  .main-wrapper {
    padding-top: calc(var(--spacing)* 12) !important;
  }
  
  #main-nav .navbar, #main-nav .navbar-brand {
    min-height: 2.75rem !important;
  }
  
  .main-header svg {
    width: 20px !important;
    height: 20px !important;
  }
  
  #lesson-reader {
    grid-template-rows: var(--grid-layout);
    overflow-y: hidden;
  }
  
  .sentence-text {
  height: var(--article_height) !important;
  }
  
  .reader-container-wrapper {
    height: 100% !important;
  }
  
  /*video viewer*/
  
  .main-footer.has-video {
    grid-area: 3 / 1 / 3 / 1 !important;
    align-self: end;
  }
  
  .widget-area {
    grid-area: 2 / 2 / -1 / 2 !important;
  }
  
  .main-content {
    grid-template-rows: 25px 1fr !important;
    overflow: hidden; 
    grid-area: 1 / 1 / 1 / -1 !important;
    align-items: anchor-center;
  }
  
  .modal-container .modls {
    pointer-events: none;
    justify-content: end !important;
    align-items: flex-start;
  }
  
  .modal-background {
    background-color: rgb(26 28 30 / 0%) !important;
  }
  
  .modal-section.modal-section--head {
    display: none !important;
  }
  
  .video-player .video-wrapper,
  .sent-video-player .video-wrapper {
    height: var(--height_big);
    overflow: hidden;
    pointer-events: auto;
  }

  .modal.video-player .modal-content {
    max-width: var(--width_big);
    margin: var(--video_margin);
  }

  .video-player.is-minimized .video-wrapper,
  .sent-video-player.is-minimized .video-wrapper {
    height: var(--height_small);
    width: var(--width_small);
  }

  .video-player.is-minimized .modal-content,
  .sent-video-player.is-minimized .modal-content {
    max-width: var(--width_small);
    margin-bottom: 0;
  }

  .video-player.is-minimized,
  .sent-video-player.is-minimized {
    left: auto;
    top: auto;
    right: var(--right_pos);
    bottom: var(--bottom_pos);
    z-index: 99999999;
    overflow: visible
  }

  /*make prev/next page buttons compact*/

  .reader-component {
    grid-template-columns: 0.5rem 1fr 0rem !important;
  }

  .reader-component>div>a.button>span {
    width: 0.5rem !important;
  }

  .reader-component>div>a.button>span>svg {
    width: 15px !important;
    height: 15px !important;
  }

  /*font settings*/
  
  .reader-container {
    margin: 0 !important;
    float: left !important;
    line-height: var(--line_height) !important;
    padding: 0 !important;
    font-size: var(--font_size) !important;
    columns: unset !important;
    overflow-y: scroll !important;
    max-width: unset !important;
  }

  .reader-container p {
    margin-top: 0 !important;
  }

  .reader-container p span.sentence-item {
    color: var(--font_color) !important;
  }

  .sentence.is-playing,
  .sentence.is-playing span {
    text-underline-offset: .2em !important;
    text-decoration-color: var(--is_playing_underline) !important;
  }
  
  /*LingQ highlightings*/

  .phrase-item {
    padding: 0 !important;
  }

  .phrase-item:not(.phrase-item-status--4, .phrase-item-status--4x2) {
    background-color: var(--lingq_background) !important;
  }

  .phrase-item.phrase-item-status--4, 
  .phrase-item.phrase-item-status--4x2 {
    background-color: rgba(0, 0, 0, 0) !important;
  }

  .phrase-cluster:not(:has(.phrase-item-status--4, .phrase-item-status--4x2)) {
    border: 1px solid var(--lingq_border) !important;
    border-radius: .25rem;
  }

  .phrase-cluster:has(.phrase-item-status--4, .phrase-item-status--4x2) {
    border: 1px solid var(--lingq_border_learned) !important;
    border-radius: .25rem;
  }

  .reader-container .sentence .lingq-word:not(.is-learned) {
    border: 1px solid var(--lingq_border) !important;
    background-color: var(--lingq_background) !important;
  }

  .reader-container .sentence .lingq-word.is-learned {
    border: 1px solid var(--lingq_border_learned) !important;
  }
  
  .reader-container .sentence .blue-word {
    border: 1px solid var(--blue_border) !important;
  }
  
  .phrase-cluster:hover,
  .phrase-created:hover {
    padding: 0 !important;
  }
  
  .phrase-cluster:hover .phrase-item,
  .phrase-created .phrase-item {
    padding: 0 !important;
  }
  
  .reader-container .sentence .selected-text {
    padding: 0 !important;
  }
  `;
  document.querySelector("head").appendChild(style);
  
  
  // Add new shortcuts
  document.addEventListener('keydown', function(event) {
    const targetElement = event.target;
    const isTextInput = (targetElement.type === 'text' || targetElement.type === 'textarea');
    if (isTextInput) {
      return;
    }
    
    // video full screen toggle
    if (event.key === 'q' || event.key === 'Q') {
      const full_screen_btn = document.querySelector('.modal-section > div > button:nth-child(2)');
      if (full_screen_btn) {
        full_screen_btn.click();
      }
    }
    
    // 5 sec Backward (Same as the 'Ctrl + ,')
    if (event.key === 'w' || event.key === 'W') {
      const full_screen_btn = document.querySelector('.audio-player--controllers > div:nth-child(1) > a');
      if (full_screen_btn) {
        full_screen_btn.click();
      }
      event.stopPropagation();
    }
    
    // 5 sec Forward (Same as the 'Ctrl + .')
    if (event.key === 'e' || event.key === 'E') {
      const full_screen_btn = document.querySelector('.audio-player--controllers > div:nth-child(2) > a');
      if (full_screen_btn) {
        full_screen_btn.click();
      }
      event.stopPropagation();
    }
    
    // Make the selected word Known (Same as the 'k')
    if (event.key === 'r' || event.key === 'R') {
      document.dispatchEvent(new KeyboardEvent('keydown', {key: 'k'}));
    }
  });
  

  // Custom embedded player
  function replaceNoCookie() {
    document.querySelectorAll('iframe').forEach(function(iframe) {
        let src = iframe.getAttribute('src');

        if (src && src.includes('autoplay=0')) {
            src = src.replace('autoplay=0', 'autoplay=1'); // video will automatically start to play.
            src = src.replace('disablekb=1', 'disablekb=0'); // keyboard controls are enabled.
            src = src + '&cc_load_policy=1' // caption is shown by default.
            src = src + '&controls=0' // player controls do not display in the player.
            iframe.setAttribute('src', src);
            console.log(src);
        }
    });
  }

  const iframeObserver = new MutationObserver(function(mutationsList, observer) {
      for (const mutation of mutationsList) {
          if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
              mutation.addedNodes.forEach(node => {
                  if (node.nodeName === 'IFRAME') {
                      replaceNoCookie();
                  } else if (node.querySelectorAll) {
                      node.querySelectorAll('iframe').forEach(replaceNoCookie);
                  }
              });
          } else if (mutation.type === 'attributes' && mutation.attributeName === 'src' && mutation.target.nodeName === 'IFRAME') {
              replaceNoCookie();
          }
      }
  });

  if (document.URL.includes("/reader/")) {
    iframeObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src'] });
  }


  // Change the amount of a scroll
  setTimeout(() => {
    console.log('scroll event!');
    const myDiv = document.querySelector('.reader-container');
    myDiv.addEventListener('wheel', (event) => {
      event.preventDefault();
      const delta = event.deltaY;
      const scrollAmount = 0.3;
      myDiv.scrollTop += delta * scrollAmount;
    });
  }, 3000);
  
  
  // Sort courses
  function addClickListener() {
    document.querySelectorAll("div.library-item-wrap").forEach((item) => {
      if (!item.dataset.listenerAdded) {
        item.addEventListener("click", function () {
          setTimeout(() => {
            /* Change the number in .dropdown-item:nth-child(3) by your preference
               1: All lessons
               2: Oldest to Newest
               3: Newest to Oldest
               4: Last Opened
               5: New Words %
               6: A-Z */
            document.querySelector(
              ".library-item--menu-box .collection-section--controllers .dropdown-item:nth-child(5)"
            ).click();
          }, 1000);
        });
        item.dataset.listenerAdded = true;
      }
    });
  }

  const libraryObserver = new MutationObserver(addClickListener);
  if (document.URL.includes("/library")) {
    libraryObserver.observe(document.body, { childList: true, subtree: true });
    addClickListener();
  }
  
  if (document.URL.includes("/library/course/")) {
    setTimeout(() => {
      document.querySelector(
        ".library-sections--item .dropdown-content .dropdown-item:nth-child(5)"
      ).click();
    }, 2000);
  }
})();
1 Like

Understood, all done, This script is amazing.

I used to have to open a YouTube page and a LingQ page simultaneously, but now I don’t have to anymore thanks to your script
The way it is in the picture is how I used to use LingQ.

Thank you so much indeed!

If you have any suggestions about this script, feel free to tell me. I’ll consider it.

P.S. The newly added hotkeys “q, w, e, r” and dictionary hotkey “d” will also help your learning. With them, you don’t need to move your hands very often to rewind the video.

1 Like

Sure, I’ll give you suggestions after I’ve used it for a while, so I can offer better advice. However, currently, there’s only one issue. I use a laptop with an external monitor, and when using it on the laptop screen, I’m not sure if it’s because the screen is small, but the layout gets messed up, the text is not fully displayed and the video interface is too large.



Adjust --height_big: 470px; according to your preference.
It determines the layout as well as the video size.

1 Like

Understood, thanks a lot.I’m also curious as to why LingQ’s programmers don’t make improvements; it should be simple for them.

Because they have a tendency to keep adding new features and leave the rest half baked.

2 Likes

Your assessment perfectly matches my experience of using it for over a year. The updates and optimizations are unbelievably bad.

Is there any way to adjust the position of the sidebar? Moving it closer to the left.

1 Like

I don’t think there’s any way to do it.

@DaisyGwynne Sorry, but that’s not possible at the moment.

1 Like