diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/css/display.css | 11 | ||||
| -rw-r--r-- | ext/data/schemas/options-schema.json | 8 | ||||
| -rw-r--r-- | ext/display-templates.html | 1 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 5 | ||||
| -rw-r--r-- | ext/js/comm/anki.js | 6 | ||||
| -rw-r--r-- | ext/js/comm/api.js | 4 | ||||
| -rw-r--r-- | ext/js/data/options-util.js | 3 | ||||
| -rw-r--r-- | ext/js/display/display.js | 65 | ||||
| -rw-r--r-- | ext/settings.html | 28 | 
9 files changed, 121 insertions, 10 deletions
| diff --git a/ext/css/display.css b/ext/css/display.css index c7827710..6fffc5ab 100644 --- a/ext/css/display.css +++ b/ext/css/display.css @@ -644,6 +644,9 @@ button.action-button[hidden] {          -webkit-filter var(--animation-duration) linear,          background-color var(--animation-duration) linear;  } +button.collapsible-action-button[hidden] { +    display: none; +}  button.action-button:disabled {      pointer-events: none;      cursor: default; @@ -667,7 +670,7 @@ button.action-button:active {      background-color: var(--action-button-active-color);      box-shadow: none;  } -button.action-button::before { +button.action-button[data-icon]::before {      content: '';      width: var(--action-button-size);      height: var(--action-button-size); @@ -691,6 +694,12 @@ button.action-button[data-icon=play-audio]::before {  button.action-button[data-icon=source-term]::before {      background-image: url('/images/source-term.svg');  } +.action-view-tags>.icon { +    display: block; +    width: var(--action-button-size); +    height: var(--action-button-size); +    background-color: var(--text-color); +}  :root[data-result-output-mode=merge] .entry[data-type=term] .actions>button.action-button.action-play-audio {      display: none;  } diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index 5ec54b86..69e042ea 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -806,7 +806,8 @@                                      "duplicateScope",                                      "checkForDuplicates",                                      "fieldTemplates", -                                    "suspendNewCards" +                                    "suspendNewCards", +                                    "displayTags"                                  ],                                  "properties": {                                      "enable": { @@ -912,6 +913,11 @@                                      "suspendNewCards": {                                          "type": "boolean",                                          "default": false +                                    }, +                                    "displayTags": { +                                        "type": "string", +                                        "enum": ["never", "always", "non-standard"], +                                        "default": "never"                                      }                                  }                              }, diff --git a/ext/display-templates.html b/ext/display-templates.html index 6496b73e..6637e70e 100644 --- a/ext/display-templates.html +++ b/ext/display-templates.html @@ -5,6 +5,7 @@      <div class="entry-current-indicator" title="Current entry"><span class="entry-current-indicator-inner"></span></div>      <div class="entry-header">          <div class="actions"> +            <button class="action-button collapsible-action-button action-view-tags" hidden disabled><span class="icon" data-icon="tag"></span></button>              <button class="action-button action-view-note" hidden disabled data-icon="view-note" title="View added note" data-hotkey='["viewNote","title","View added note ({0})"]'></button>              <button class="action-button action-add-note" hidden disabled data-icon="add-term-kanji" data-mode="term-kanji" title="Add expression" data-hotkey='["addNoteTermKanji","title","Add expression ({0})"]'></button>              <button class="action-button action-add-note" hidden disabled data-icon="add-term-kana" data-mode="term-kana" title="Add reading" data-hotkey='["addNoteTermKana","title","Add reading ({0})"]'></button> diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 5b133d79..e94ad065 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -458,7 +458,7 @@ class Backend {          return await this._anki.addNote(note);      } -    async _onApiGetAnkiNoteInfo({notes}) { +    async _onApiGetAnkiNoteInfo({notes, fetchAdditionalInfo}) {          const results = [];          const cannotAdd = [];          const canAddArray = await this._anki.canAddNotes(notes); @@ -482,6 +482,9 @@ class Backend {                  const noteIds = noteIdsArray[i];                  if (noteIds.length > 0) {                      cannotAdd[i].info.noteIds = noteIds; +                    if (fetchAdditionalInfo) { +                        cannotAdd[i].info.noteInfos = await this._anki.notesInfo(noteIds); +                    }                  }              }          } diff --git a/ext/js/comm/anki.js b/ext/js/comm/anki.js index da234eff..e8cf7afd 100644 --- a/ext/js/comm/anki.js +++ b/ext/js/comm/anki.js @@ -71,6 +71,12 @@ class AnkiConnect {          return await this._invoke('canAddNotes', {notes});      } +    async notesInfo(notes) { +        if (!this._enabled) { return []; } +        await this._checkVersion(); +        return await this._invoke('notesInfo', {notes}); +    } +      async getDeckNames() {          if (!this._enabled) { return []; }          await this._checkVersion(); diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index 137cda41..3795dcf4 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -52,8 +52,8 @@ class API {          return this._invoke('addAnkiNote', {note});      } -    getAnkiNoteInfo(notes) { -        return this._invoke('getAnkiNoteInfo', {notes}); +    getAnkiNoteInfo(notes, fetchAdditionalInfo) { +        return this._invoke('getAnkiNoteInfo', {notes, fetchAdditionalInfo});      }      injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) { diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index 42f9a38f..cb7946f7 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -792,6 +792,7 @@ class OptionsUtil {          // Version 11 changes:          //  Changed dictionaries to an array.          //  Changed audio.customSourceUrl's {expression} marker to {term}. +        //  Added anki.displayTags.          const customSourceUrlPattern = /\{expression\}/g;          for (const profile of options.profiles) {              const dictionariesNew = []; @@ -805,6 +806,8 @@ class OptionsUtil {                  customSourceUrl = customSourceUrl.replace(customSourceUrlPattern, '{term}');              }              profile.options.audio.customSourceUrl = customSourceUrl; + +            profile.options.anki.displayTags = 'never';          }          return options;      } diff --git a/ext/js/display/display.js b/ext/js/display/display.js index fcfa0244..720e1de5 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -113,6 +113,7 @@ class Display extends EventDispatcher {          this._displayAudio = new DisplayAudio(this);          this._ankiNoteNotification = null;          this._ankiNoteNotificationEventListeners = null; +        this._ankiTagNotification = null;          this._queryPostProcessor = null;          this._optionToggleHotkeyHandler = new OptionToggleHotkeyHandler(this);          this._elementOverflowController = new ElementOverflowController(); @@ -1081,8 +1082,8 @@ class Display extends EventDispatcher {              let states;              try {                  const noteContext = this._getNoteContext(); -                const {checkForDuplicates} = this._options.anki; -                states = await this._areDictionaryEntriesAddable(dictionaryEntries, modes, noteContext, checkForDuplicates ? null : true); +                const {checkForDuplicates, displayTags} = this._options.anki; +                states = await this._areDictionaryEntriesAddable(dictionaryEntries, modes, noteContext, checkForDuplicates ? null : true, displayTags !== 'never');              } catch (e) {                  return;              } @@ -1096,11 +1097,12 @@ class Display extends EventDispatcher {      }      _updateAdderButtons2(states, modes) { +        const {displayTags} = this._options.anki;          for (let i = 0, ii = states.length; i < ii; ++i) {              const infos = states[i];              let noteId = null;              for (let j = 0, jj = infos.length; j < jj; ++j) { -                const {canAdd, noteIds} = infos[j]; +                const {canAdd, noteIds, noteInfos} = infos[j];                  const mode = modes[j];                  const button = this._adderButtonFind(i, mode);                  if (button === null) { @@ -1112,6 +1114,10 @@ class Display extends EventDispatcher {                  }                  button.disabled = !canAdd;                  button.hidden = false; + +                if (displayTags !== 'never' && Array.isArray(noteInfos)) { +                    this._setupTagsIndicator(i, noteInfos); +                }              }              if (noteId !== null) {                  this._viewerButtonShow(i, noteId); @@ -1119,6 +1125,49 @@ class Display extends EventDispatcher {          }      } +    _setupTagsIndicator(i, noteInfos) { +        const tagsIndicator = this._tagsIndicatorFind(i); +        if (tagsIndicator === null) { +            return; +        } + +        const {tags: optionTags, displayTags} = this._options.anki; +        const noteTags = new Set(); +        for (const {tags} of noteInfos) { +            for (const tag of tags) { +                noteTags.add(tag); +            } +        } +        if (displayTags === 'non-standard') { +            for (const tag of optionTags) { +                noteTags.delete(tag); +            } +        } + +        if (noteTags.size > 0) { +            tagsIndicator.disabled = false; +            tagsIndicator.hidden = false; +            tagsIndicator.title = `Card tags: ${[...noteTags].join(', ')}`; +        } +    } + +    _onShowTags(e) { +        e.preventDefault(); +        const tags = e.currentTarget.title; +        this._showAnkiTagsNotification(tags); +    } + +    _showAnkiTagsNotification(message) { +        if (this._ankiTagNotification === null) { +            const node = this._displayGenerator.createEmptyFooterNotification(); +            node.classList.add('click-scannable'); +            this._ankiTagNotification = new DisplayNotification(this._footerNotificationContainer, node); +        } + +        this._ankiTagNotification.setContent(message); +        this._ankiTagNotification.open(); +    } +      _entrySetCurrent(index) {          const entryPre = this._getEntry(this._index);          if (entryPre !== null) { @@ -1320,6 +1369,11 @@ class Display extends EventDispatcher {          return entry !== null ? entry.querySelector(`.action-add-note[data-mode="${mode}"]`) : null;      } +    _tagsIndicatorFind(index) { +        const entry = this._getEntry(index); +        return entry !== null ? entry.querySelector('.action-view-tags') : null; +    } +      _viewerButtonFind(index) {          const entry = this._getEntry(index);          return entry !== null ? entry.querySelector('.action-view-note') : null; @@ -1424,7 +1478,7 @@ class Display extends EventDispatcher {          return templates;      } -    async _areDictionaryEntriesAddable(dictionaryEntries, modes, context, forceCanAddValue) { +    async _areDictionaryEntriesAddable(dictionaryEntries, modes, context, forceCanAddValue, fetchAdditionalInfo) {          const modeCount = modes.length;          const notePromises = [];          for (const dictionaryEntry of dictionaryEntries) { @@ -1442,7 +1496,7 @@ class Display extends EventDispatcher {              }              infos = this._getAnkiNoteInfoForceValue(notes, forceCanAddValue);          } else { -            infos = await yomichan.api.getAnkiNoteInfo(notes); +            infos = await yomichan.api.getAnkiNoteInfo(notes, fetchAdditionalInfo);          }          const results = []; @@ -1703,6 +1757,7 @@ class Display extends EventDispatcher {      _addEntryEventListeners(entry) {          this._eventListeners.addEventListener(entry, 'click', this._onEntryClick.bind(this)); +        this._addMultipleEventListeners(entry, '.action-view-tags', 'click', this._onShowTags.bind(this));          this._addMultipleEventListeners(entry, '.action-add-note', 'click', this._onNoteAdd.bind(this));          this._addMultipleEventListeners(entry, '.action-view-note', 'click', this._onNoteView.bind(this));          this._addMultipleEventListeners(entry, '.headword-kanji-link', 'click', this._onKanjiLookup.bind(this)); diff --git a/ext/settings.html b/ext/settings.html index 7f9cc8ec..872dac4d 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -1565,6 +1565,34 @@                  <label class="toggle"><input type="checkbox" data-setting="anki.suspendNewCards"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>              </div>          </div></div> +        <div class="settings-item advanced-only"> +            <div class="settings-item-inner"> +                <div class="settings-item-left"> +                    <div class="settings-item-label"> +                        Show card tags +                        <a class="more-toggle more-only" data-parent-distance="4">(?)</a> +                    </div> +                </div> +                <div class="settings-item-right"> +                    <select data-setting="anki.displayTags"> +                        <option value="never">Never</option> +                        <option value="always">Always</option> +                        <option value="non-standard">Non-Standard</option> +                    </select> +                </div> +            </div> +            <div class="settings-item-children more" hidden> +                <p> +                    When coming across a word that is already in an Anki deck, a button will appear that shows +                    the tags the card has. If set to <em>Non-Standard</em>, all tags that are included in the +                    <em>Card tags</em> option will be filtered out from the list. If no tags remain after filtering, +                    then the button will not be shown. +                </p> +                <p> +                    <a class="more-toggle" data-parent-distance="3">Less…</a> +                </p> +            </div> +        </div>          <div class="settings-item settings-item-button" data-modal-action="show,anki-cards"><div class="settings-item-inner">              <div class="settings-item-left">                  <div class="settings-item-label">Configure Anki card format…</div> |