Show SRS and leech breakdown on dashboard
当前为
// ==UserScript==
// @name Araigoshi's Wanikani Stage Breakdown
// @namespace https://www.wanikani.com
// @description Show SRS and leech breakdown on dashboard
// @author araigoshi
// @version 1.0.1
// @include https://www.wanikani.com/dashboard
// @include https://www.wanikani.com/
// @license MIT
// @grant none
// ==/UserScript==
(async function () {
'use strict';
if (!window.wkof) {
let response = confirm('WaniKani Dashboard SRS and Leech Breakdown script requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');
if (response) {
window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
}
return;
}
let style =
`<style>
.dashboard section.srs-progress .srs-progress-details-group {
display: flex;
margin: 0;
list-style-type: none;
gap: 0.3em;
justify-content: center;
padding-top: var(--spacing-xtight);
}
.dashboard section.srs-progress li.grouped-list {
padding: var(--spacing-xtight) 0.3em;
border-radius: 5px;
background-color: white;
color: black;
flex-grow: 1;
flex-basis: 0;
display: flex;
justify-content: space-between;
}
.dashboard section.srs-progress li.grouped-list:hover {
background-color: #f88;
}
.dashboard section.srs-progress li.grouped-list .group-item-label {
flex-grow: 1;
}
.dashboard section.srs-progress li.grouped-list:hover .group-item-label {
content: 'L';
}
.dashboard section.srs-progress li.grouped-list .group-item-value {
font-weight: bold;
}
.dashboard section.srs-progress li.grouped-list:hover .group-item-value {
display: none;
}
.dashboard section.srs-progress li.grouped-list .group-item-leeches {
font-weight: bold;
display: none;
}
.dashboard section.srs-progress li.grouped-list:hover .group-item-leeches {
display: initial;
}
</style>`
$('head').append(style);
const leechThreshold = 1;
const config = {
wk_items: {
options: {
review_statistics: true,
assignments: true
}
}
};
wkof.include('ItemData');
wkof.include('Menu');
wkof.include('Settings');
await wkof.ready('Menu,ItemData,Settings');
wkof.Settings.load('araistages', {
compactItemTypes: true
});
let items = await wkof.ItemData.get_items(config);
let filteredItems = items.filter(item => item?.assignments?.srs_stage > 0);
let groupedItems = mapItemsToSrs(filteredItems);
updatePage(groupedItems);
wkof.Menu.insert_script_link({
name: 'araistages_settings_open',
submenu: 'Settings',
title: "Araigoshi's Dashboard Stage Breakdown",
on_click() {
new wkof.Settings({
script_id: 'araistages',
title: "Araigoshi's Dashboard Stage Breakdown",
content: {
compactItemTypes: {
type: 'checkbox',
label: 'Compact Item Types',
}
}
}).open()
}
});
function mapItemsToSrs(items) {
let itemsBySrs = [1, 2, 3, 4, 5, 6, 7, 8, 9].reduce((result, srs) => {
result[srs] = {
totals: {
all: 0,
radical: 0,
vocabulary: 0,
kanji: 0,
},
leeches: {
all: 0,
radical: 0,
vocabulary: 0,
kanji: 0,
}
};
return result;
}, {});
items.forEach(function (item) {
let srsStage = item.assignments.srs_stage;
let subjectType = item.assignments.subject_type;
if (subjectType === 'kana_vocabulary') {
subjectType = 'vocabulary'
}
itemsBySrs[srsStage].totals[subjectType]++;
itemsBySrs[srsStage].totals.all++;
if (isLeech(item)) {
itemsBySrs[srsStage].leeches[subjectType]++;
itemsBySrs[srsStage].leeches.all++;
}
});
return itemsBySrs;
}
function isLeech(item) {
if (item.review_statistics === undefined) {
return false;
}
let reviewStats = item.review_statistics;
let meaningScore = getLeechScore(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak);
let readingScore = getLeechScore(reviewStats.reading_incorrect, reviewStats.reading_current_streak);
return meaningScore >= leechThreshold || readingScore >= leechThreshold;
}
function getLeechScore(incorrect, currentStreak) {
return incorrect / Math.pow((currentStreak || 0.5), 1.5);
}
function updatePage(itemsBySrs) {
replaceTypes('apprentice', itemsBySrs, [1, 2, 3, 4]);
addSubStages('apprentice', itemsBySrs, [1, 2, 3, 4]);
replaceTypes('guru', itemsBySrs, [5, 6]);
addSubStages('guru', itemsBySrs, [5, 6]);
replaceTypes('master', itemsBySrs, [7]);
replaceTypes('enlightened', itemsBySrs, [8]);
replaceTypes('burned', itemsBySrs, [9]);
}
function makeGroupedListItem(labelText, valueCount, leechesCount, color) {
let li = document.createElement('li');
li.classList.add('grouped-list');
let label = document.createElement('span');
label.classList.add('group-item-label');
label.textContent = labelText;
li.appendChild(label);
let value = document.createElement('span');
value.classList.add('group-item-value');
value.textContent = valueCount;
value.style.color = color;
li.appendChild(value);
let leeches = document.createElement('span');
leeches.textContent = leechesCount;
leeches.classList.add('group-item-leeches');
li.appendChild(leeches);
return li;
}
function addSubStages(srsSectionId, itemsBySrs, srsLevelsArray) {
let romanNumerals = ['I', 'II', 'III', 'IV', 'V'];
let items = srsLevelsArray.map((srs, i) => makeGroupedListItem(
romanNumerals[i],
itemsBySrs[srs].totals.all,
itemsBySrs[srs].leeches.all,
`var(--color-srs-progress-${srsSectionId})`
));
let wrapper = document.createElement('ol');
wrapper.classList.add('srs-progress-details-group');
for (let item of items) {
wrapper.appendChild(item);
};
document.querySelector(`.srs-progress__stage--${srsSectionId}`).appendChild(wrapper);
}
function sumAll(itemsBySrs, srsLevelsArray, group, field) {
return srsLevelsArray.reduce((n, stage) => n + itemsBySrs[stage][group][field], 0);
}
function makeTypeListItem(itemsBySrs, srsLevelsArray, abbr, itemType, color) {
return makeGroupedListItem(
abbr,
sumAll(itemsBySrs, srsLevelsArray, 'totals', itemType),
sumAll(itemsBySrs, srsLevelsArray, 'leeches', itemType),
color
);
}
function replaceTypes(srsSectionId, itemsBySrs, srsLevelsArray) {
if (!wkof.settings.araistages?.compactItemTypes) {
return;
}
let color = `var(--color-srs-progress-${srsSectionId})`;
let items = [
makeTypeListItem(itemsBySrs, srsLevelsArray, 'R', 'radical', color),
makeTypeListItem(itemsBySrs, srsLevelsArray, 'K', 'kanji', color),
makeTypeListItem(itemsBySrs, srsLevelsArray, 'V', 'vocabulary', color),
];
let wrapper = document.createElement('ol');
wrapper.classList.add('srs-progress-details-group');
for (let item of items) {
wrapper.appendChild(item);
};
document.querySelector(`.srs-progress__stage--${srsSectionId} .srs-progress__subject-types`).style.display = 'none';
document.querySelector(`.srs-progress__stage--${srsSectionId}`).appendChild(wrapper);
}
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址