LingQ Addon: AI Tools & Custom Layouts for an Enhanced Learning Experience

LingQ Addon

A userscript for Tampermonkey that significantly enhances the LingQ language learning experience.

Customizable layouts, AI-powered chat & TTS, flashcard system, local video player, and more — all in one script.

:open_book: Looking for in-depth documentation?
Check out the complete Feature Guide for settings breakdown, local video setup, Supabase cloud sync, and Anki integration.

Quick Start

Desktop

  1. Install Tampermonkey or GreaseMonkey.
  2. Install the script from the Project page.
  3. Visit LingQ — the addon activates automatically on reader pages.

Android

  1. Install Firefox and add Tampermonkey.
  2. Install the script from the Project page.
  3. Visit LingQ — refresh the browser if the addon doesn’t activate.

Features

  • Reader Layouts:
    Preset layouts optimized for video, audio, or text-focused study. Includes a resizable video option.

  • Local Video Player:
    Load local video files and sync them with your lesson.

  • AI Chat Widget:
    Select a word or sentence, AI generates pronunciation, meaning, and example sentences instantly.

    ezgif-1f72b6d6f1f04d

  • Quick Summary:
    AI-generated lesson summaries with adjustable difficulty.

  • Flashcard System:
    Create flashcards from AI responses in a single click. Use either the built-in local database or connect your own Supabase DB for cloud storage. Export to Anki via CSV.

  • AI TTS:
    AI-based text-to-speech for words, sentences, and full lesson audio generation.

  • Appearance Customization:
    Dark/Light themes, custom color palette, adjustable font size and line height, and custom font support.

  • Vocabulary Tools:
    Download Entire known/unknown words and phrases as CSV.

  • Print a Lesson:
    Print lessons with word highlighting and vocabulary list.

Supported AI Providers

To use AI features, create an API key from any supported provider and enter it in :gear: Settings.

Provider API Key Guide
OpenAI (GPT) Tutorial
Google Gemini Tutorial
Google Vertex —
Anthropic —
DeepSeek Tutorial
Cerebras —

Recommendation: Google - Gemini 3.1 Flash-Light


Download Script


If it was helpful to you, you can
Buy Me A Coffee

16 Likes
LingQ UI Feature Ideas
Video playback in lessons should be dockable on sidebar
Save LingQs from Youtube Subtitles without having to import as a lesson
Unable to See ALL Known Words in Vocabulary Section
Mandarin auto-generate audio is terrible
Size of video is too small
In web reader non-synchronized, how to click on a sentence to hear it?
Streamlining translation with better and more complete word meanings
Video covering vocabulary information
Allow the Video Player to Be Minimized While Keeping Audio Playing
I request a method for learning YouTube resources on mobile devices
Vocabulary learning report generated by AI (prompt included)
Defining the 30,000 word benchmark
The pinyin and audio on the Chinese lessons in LingQ is frequently incorrect.
Suggestion for YouTube Videos (computer version)
BUG that makes it impossible to use the site
What to do with Lynx?
Looking for effective ways to improve hearing ability?
Sentence Splitting Across Pages in Imported Lessons
New Gemini TTS models review
YouTube suggested videos appear every time the video is paused
Youtube video box in the reading page
Youtube video in way of text
LingQ is unusable for me today
Does the LingQ team have a roadmap plan for this year?
Unknown Word Lists for courses - feature request
Multiple problems with Lingq today
Quickly turn translation on and off shortcut
Lynx Beta Feedback - Main Thread
[request] option to always show translation in sentence mode
Size of video is too small
Lynx Beta Feedback - Main Thread
This one feature would revolutionize Lingq

I made some adjustments to see a large-sized video with its script without clicking the page flip button.

Adjust the value of variables to fit your resolution or env.

Old Code
// ==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
// @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: 460px; /*video height*/
    
    --font_size: 1.1rem; /*reader font size*/
    --font_color: #e0e0e0; /*font color*/
    --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 (q key)
    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 (w key)
    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 (e key)
    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();
    }
  });
  

  // 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();
          }, 500);
        });
        item.dataset.listenerAdded = true;
      }
    });
  }

  const libraryObserver = new MutationObserver(addClickListener);
  if (document.URL.includes("/library")) {
    libraryObserver.observe(document.body, { childList: true, subtree: true });
    addClickListener();
  }
})();

You can adjust video size and layout by changing “–height_big” variable’s value only, others are automatically adjusted.

If it’s useful, plz hit the like button below.


  • You need to change the background color to dark manually or change the color settings.
  • If the video size is small, you need to expand the video window by pressing Q key.
6 Likes

Any way to change the punctuation color as well? The white really stands out if the font color is set to gray.

Change

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

to

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

Now it works.


Before


After

1 Like

Yes, that works, thank you.

1 Like

New Feature:

  • Now the sentence playing is located at the center of the screen. You don’t need to scrol acording to the playing video.

If you don’t want the sentence is fixed on the exact center use nearest instead of center in this part of the code playingSentence.scrollIntoView({behavior: 'smooth', block: 'center'});
It always locates the sentence inside a screen but not center.

Old Code
// ==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.4
// ==/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);


  // Focus on playing sentence
  function focusPlayingSentence() {
    const playingSentence = document.querySelector('.sentence.is-playing');

    if (playingSentence) {
    playingSentence.scrollIntoView({behavior: 'smooth', block: 'center'});
    }
  }

  const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
    if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target.classList.contains('sentence')) {
      focusPlayingSentence();
    }
    });
  });

  const container = document.querySelector('.sentence-text'); 
  if (container) {
  	console.log('observer setted!')
    observer.observe(container, { attributes: true, subtree: true }); 
  }

  
  // 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);
  }
})();

New hotkeys

  • Move the cursor to the reference input (`): it directly moves the keyboard cursor to the text field in the widget.
  • Open Dictionary (d or f): it works when multiple words are selected
  • Open Translator (t): it works when multiple words are selected
  • Copy selected text (c): copy selected text in a clipboard (I use it when using LLM)

Of course, you can change the hotkeys in your favor.


The Open Dictionary key clicks the first dictionary and the Open Translator selects the last. So you need to set them properly



You can change the order of the dicts here.


Old Code
// ==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.5
// ==/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') {
      const backward_btn = document.querySelector('.audio-player--controllers > div:nth-child(1) > a');
      if (backward_btn) {
        backward_btn.click();
      }
      event.stopPropagation();
    }
    
    // 5 sec Forward (Same as the 'Ctrl + .')
    if (event.key === 'e') {
      const forkward_btn = document.querySelector('.audio-player--controllers > div:nth-child(2) > a');
      if (forkward_btn) {
        forkward_btn.click();
      }
      event.stopPropagation();
    }
    
    // Make the selected word Known (Same as the 'k')
    if (event.key === 'r') {
      document.dispatchEvent(new KeyboardEvent('keydown', {key: 'k'}));
      event.stopPropagation();
    }
    
    // Move cursor to the reference input
    if (event.key === '`') {
      const reference_input = document.querySelector('.reference-input-text');

      if (reference_input) {
          reference_input.focus();
          reference_input.setSelectionRange(reference_input.value.length, reference_input.value.length);
      }
      event.preventDefault();
      event.stopPropagation();  
    }
    
    // Open Dictionary
    if (event.key === 'd' || event.key === 'f') {
      const dict_btn = document.querySelector('.dictionary-resources > a:nth-child(1)');
      if (dict_btn) {
        dict_btn.click();
      }
      event.stopPropagation();
    }
    
    // Open Translator
    if (event.key === 't') {
      const translator_btn = document.querySelector('.dictionary-resources > a:nth-last-child(1)');
      if (translator_btn) {
        translator_btn.click();
      }
      event.stopPropagation();
    }
    
    // Copy selected text
    if (event.key === 'c') {
      const selected_text = document.querySelector('.reference-word');
      if (selected_text) {
        navigator.clipboard.writeText(selected_text.textContent);
      }
      event.stopPropagation();
    }
    
  });
  

  // 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);


  // Focus on playing sentence
  function focusPlayingSentence() {
    const playingSentence = document.querySelector('.sentence.is-playing');

    if (playingSentence) {
    playingSentence.scrollIntoView({behavior: 'smooth', block: 'center'});
    }
  }

  const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
    if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target.classList.contains('sentence')) {
      focusPlayingSentence();
    }
    });
  });

  const container = document.querySelector('.sentence-text'); 
  if (container) {
  	console.log('observer setted!')
    observer.observe(container, { attributes: true, subtree: true }); 
  }

  
  // 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);
  }
})();

I’ve yet to figure out how to enable this script via tampermonkey. Tampermonkey us enabled, I pasted in the script provided and saved it, that’s now in the extension an turned on, I’ve reloaded, nothing works. I’ve enabled developer mode for TM too. I watched a couple videos, nothing is showing the missing step unfortunatly.

1 Like

I’m not sure what the problem is with it.
This is the process i had and works. Uninstall the extension and follow the procedure.

  1. install Tampermonkey from chrome store
  2. open Extension from the upper navbar and click ‘Create a new script’ button.
  3. Copy and paste the script I provide.
  4. refresh LingQ reader page.

image

It could be the issue with the URL format. Let me know the URL of your reader page. Browser also.

Ok I followed those instructions. This time something change a little but nowhere close to what it shoud look like, and I tried it on a clean account with no other extensions which could possibly interfere. I tried it within firefox too and the same weird / incomplete effect happens.

Here is the url I’m on for reader mode:
image

What it looks like for me:

What I pasted in (beginning and end):


Enabled:


image

image

This is the layout I intended. It seems to work on your PC.
Hit the ‘Q’ key after loading the video to expand the video and fill its area.

If the video size is too large, adjust –height_big variable

If your monitor’s resolution is too low, you might need to change the fontsize and video size to reserve enough space for text.

This is 1920x1080 setup

1 Like

Q Key! Ok that resolved it and then I feel like I dope, I was expecting darkmode to be turned on via the script and I realized I needed to enable that seperatly in LingQ settings. Anyway, all good now, thanks for the help!

Yup. Have a good night or day!

1 Like

How can I change this situation??

Click the Aa button in the upper right corner and select the black background color. If you prefer light mode, change color values in the source code.

This css is for audio lesson users.

Old Code
  :root {
    --width_small: 440px;
    --height_small: 260px;
    --right_pos: 0.5%;
    --bottom_pos: 5.5%;
    
    --width_big: calc(100vw - 424px - 5px);
    --height_big: 10px;
    
    --font_size: 1.1rem;
    --font_color: #e0e0e0;
    --line_height: 2.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) - 175px - 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 {
    grid-area: 3 / 1 / 3 / 1 !important;
    align-self: end;
  }
  
  .widget-area {
    grid-area: 1 / 2 / -1 / 2 !important;
}
  
  .main-content {
    grid-template-rows: 45px 1fr !important;
    overflow: hidden;
    grid-area: 1 / 1 / 2 / 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;
  }

It hides the annoying “more videos” popup and the YT logo from the mini video player.
You can read subtitles when a video is paused.
Use it together with my LingQ Add-on script.

Before

After

Old Code
// ==UserScript==
// @name         Collapse More-videos
// @description  Automatically collapse More videos popup in YouTube.
// @match        https://www.youtube-nocookie.com/*
// @version      1.5
// ==/UserScript==

(function () {
  "use strict";
  
  document.addEventListener('keydown', function(event) {
    const targetElement = event.target;

    // dismiss subtitle background opacity setting key (w)
    if (event.key === 'w') {
    console.log('w');
    event.preventDefault(); 
    event.stopPropagation();
    }
  }, true); 
  
  function clickPlayButton() {
    document.querySelector(".ytp-button.ytp-large-play-button").click();
  }

  function clickCollapseButton() {
    document.querySelector(".ytp-button.ytp-collapse").click();
  }

  function clickSubtitleButton() {
    document.querySelector(".ytp-button.ytp-subtitles-button").click();
  }

  window.addEventListener("load", function () {
    setTimeout(() => {
      clickPlayButton();
      clickCollapseButton();
      clickPlayButton();
    }, 100);
  });

  var style = document.createElement("style");
  style.textContent = `
  div.ytp-pause-overlay{
    visibility: hidden !important;
  }
  a.ytp-watermark{
    visibility: hidden !important;
  }
`;

  document.querySelector("head").appendChild(style);
})();


In the default lingQ interface, the state 4 of the word will hide the yellow prompt, but the yellow prompt will still be displayed under the script, how can I change the target, and the yellow prompt will be hidden when the state 4 is hidden?

I intentionally maintained the border of the known words. If you don’t like it, change the code.

--lingq_border_learned: hsl(43 99% 64% / 0.5);
to
--lingq_border_learned: hsl(43 99% 64% / 0);


Additional Tip

You can open DevTools with F12 key. There, you can change the variables interatively. Use it when you test UI elements.
(The change made here is not saved, so make sure that you change the corresponding script’s values.)

Change Log

v1.6: Now, LingQ uses “youtube.com” player, not “youtube-nocookie.com”. Some code is updated for that change.

Also, now the videos you watch in LingQ show up in YouTube watch history (previously not). In other words, YT algorithm will consider the video played in LingQ.

LingQ automatically play the video, and auto-played video doesn’t counted as a view. So, not appear in YT history page.

Old Code

Script for YouTube Player

// ==UserScript==
// @name         Collapse More-videos
// @description  Automatically collapse More videos popup in YouTube.
// @match        https://www.youtube-nocookie.com/*
// @match        https://www.youtube.com/embed/*
// @version      1.6
// ==/UserScript==

(function () {
  "use strict";
  
  document.addEventListener('keydown', function(event) {
    const targetElement = event.target;

    // dismiss subtitle background opacity setting key (w)
    if (event.key === 'w') {
    console.log('w');
    event.preventDefault(); 
    event.stopPropagation();
    }
  }, true); 
  
  function clickPlayButton() {
    document.querySelector(".ytp-button.ytp-large-play-button").click();
  }

  function clickCollapseButton() {
    document.querySelector(".ytp-button.ytp-collapse").click();
  }

  function clickSubtitleButton() {
    document.querySelector(".ytp-button.ytp-subtitles-button").click();
  }

  window.addEventListener("load", function () {
    setTimeout(() => {
      clickPlayButton();
      clickCollapseButton();
      clickPlayButton();
    }, 100);
  });

  var style = document.createElement("style");
  style.textContent = `
  div.ytp-pause-overlay{
    visibility: hidden !important;
  }
  a.ytp-watermark{
    visibility: hidden !important;
  }
`;

  document.querySelector("head").appendChild(style);
})();

Script for LingQ website

// ==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.6
// ==/UserScript==

(function () {
  "use strict";

  // Style settings
  var style = document.createElement("style");
  style.textContent = `
  //PASTE THE CSS HERE
  `;
  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();
      }
      event.preventDefault();
      event.stopPropagation();
    }
    
    // 5 sec Backward (Same as the 'Ctrl + ,')
    if (event.key === 'w') {
      const backward_btn = document.querySelector('.audio-player--controllers > div:nth-child(1) > a');
      if (backward_btn) {
        backward_btn.click();
      }
      event.preventDefault();
      event.stopPropagation();
    }
    
    // 5 sec Forward (Same as the 'Ctrl + .')
    if (event.key === 'e') {
      const forkward_btn = document.querySelector('.audio-player--controllers > div:nth-child(2) > a');
      if (forkward_btn) {
        forkward_btn.click();
      }
      event.preventDefault();
      event.stopPropagation();
    }
    
    // Make the selected word Known (Same as the 'k')
    if (event.key === 'r') {
      document.dispatchEvent(new KeyboardEvent('keydown', {key: 'k'}));
      event.preventDefault();
      event.stopPropagation();
    }
    
    // Move cursor to the reference input
    if (event.key === '`') {
      const reference_input = document.querySelector('.reference-input-text');

      if (reference_input) {
          reference_input.focus();
          reference_input.setSelectionRange(reference_input.value.length, reference_input.value.length);
      }
      event.preventDefault();
      event.stopPropagation();  
    }
    
    // Open Dictionary
    if (event.key === 'd' || event.key === 'f') {
      const dict_btn = document.querySelector('.dictionary-resources > a:nth-child(1)');
      if (dict_btn) {
        dict_btn.click();
      }
      event.preventDefault();
      event.stopPropagation();
    }
    
    // Open Translator
    if (event.key === 't') {
      const translator_btn = document.querySelector('.dictionary-resources > a:nth-last-child(1)');
      if (translator_btn) {
        translator_btn.click();
      }
      event.preventDefault();
      event.stopPropagation();
    }
    
    // Copy selected text
    if (event.key === 'c') {
      const selected_text = document.querySelector('.reference-word');
      if (selected_text) {
        navigator.clipboard.writeText(selected_text.textContent);
      }
      event.preventDefault();
      event.stopPropagation();
    }
    
  });
  

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

        if (src && src.includes('disablekb=1')) {
            // 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);


  // Focus on playing sentence
  function focusPlayingSentence() {
    const playingSentence = document.querySelector('.sentence.is-playing');

    if (playingSentence) {
    playingSentence.scrollIntoView({behavior: 'smooth', block: 'center'});
    }
  }

  const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
    if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target.classList.contains('sentence')) {
      focusPlayingSentence();
    }
    });
  });

  const container = document.querySelector('.sentence-text'); 
  if (container) {
  	console.log('observer setted!')
    observer.observe(container, { attributes: true, subtree: true }); 
  }

  
  // 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);
  }
})();