This commit is contained in:
Hatser 2021-05-01 20:18:25 +09:00
commit 4c48e1664f
46 changed files with 2375 additions and 94 deletions

View file

@ -24,6 +24,7 @@
{ id: 'en', label: 'English' },
{ id: 'id', label: 'Indonesia' },
{ id: 'ru', label: 'Русский' },
{ id: 'ko', label: '한국어' },
];
$: currentLocale = languages.find((e) => e.id === $locale.substring(0, 2)) || { id: 'en', label: 'English' };
$: locales = languages.filter((e) => e.id !== currentLocale.id);
@ -140,7 +141,7 @@
<div class="locale-dropdown" style="top: {locales.length * -45}px;">
{#each locales as locale}
<div
class="flex items-center justify-center py-2 cursor-pointer rounded-xl bg-opacity-50 bg-black hover:bg-opacity-75"
class="flex items-center justify-center py-2 cursor-pointer rounded-xl bg-black hover:bg-gray-900"
on:click={() => changeLocale(locale.id)}
>
<img class="w-4 h-4 rounded-full mr-2" alt={locale.label} src="/images/locales/{locale.id}.svg" />

View file

@ -1,6 +1,6 @@
<script>
import { createEventDispatcher } from 'svelte';
import { slide } from 'svelte/transition';
import { fly } from 'svelte/transition';
export let image;
export let label;
@ -14,7 +14,6 @@
const dispatch = createEventDispatcher();
function expand() {
if (!mobile) return;
expandMenu = !expandMenu;
}
@ -58,8 +57,8 @@
</span>
</div>
</div>
{#if mobile && expandMenu}
<div class="py-2 flex flex-col w-full" transition:slide={{ duration: 100 }}>
{#if expandMenu}
<div class="py-2 flex flex-col w-full" transition:fly={{ y: -100, duration: 100 }}>
{#each items as item}
<a
on:click={clicked}

View file

@ -31,6 +31,7 @@
onMount(() => {
if (!isEdit) return;
if (editType === 'unknown_3_star') return;
type = selectOptions.find((e) => e.value === editType);

View file

@ -73,11 +73,6 @@
let currentBanner = '';
let currentPage = 1;
let lastPull = {
id: '0',
time: '',
};
function cancel() {
fetchController.abort();
cancelled = true;
@ -229,11 +224,6 @@
const name = row.name;
const type = row.item_type.replace(/ /g, '');
if (row.id > lastPull.id) {
lastPull.id = row.id;
lastPull.time = row.time;
}
if (time.unix() <= newestPullTime) {
return;
}
@ -409,7 +399,7 @@
pushToast($t('wish.import.success'));
if (wishTallyChecked) {
submitWishTally(lastPull);
submitWishTally();
}
const prefix = getAccountPrefix();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -2264,6 +2264,77 @@ export const characters = {
boss: itemList.tusk_of_monoceros_caeli,
},
},
yanfei: {
name: 'Yanfei',
id: 'yanfei',
rarity: 4,
element: elements.pyro,
weapon: weapons.catalyst,
sex: 'female',
nation: 'liyue',
ascension: [
{
items: [
{ item: itemList.agnidus_agate_sliver, amount: 1 },
{ item: itemList.none, amount: null },
{ item: itemList.noctilucous_jade, amount: 3 },
{ item: itemList.treasure_hoarder_insignia, amount: 3 },
],
mora: 20000,
},
{
items: [
{ item: itemList.agnidus_agate_fragment, amount: 3 },
{ item: itemList.juvenile_jade, amount: 2 },
{ item: itemList.noctilucous_jade, amount: 10 },
{ item: itemList.treasure_hoarder_insignia, amount: 15 },
],
mora: 40000,
},
{
items: [
{ item: itemList.agnidus_agate_fragment, amount: 6 },
{ item: itemList.juvenile_jade, amount: 4 },
{ item: itemList.noctilucous_jade, amount: 20 },
{ item: itemList.silver_raven_insignia, amount: 12 },
],
mora: 60000,
},
{
items: [
{ item: itemList.agnidus_agate_chunk, amount: 3 },
{ item: itemList.juvenile_jade, amount: 8 },
{ item: itemList.noctilucous_jade, amount: 30 },
{ item: itemList.silver_raven_insignia, amount: 18 },
],
mora: 80000,
},
{
items: [
{ item: itemList.agnidus_agate_chunk, amount: 6 },
{ item: itemList.juvenile_jade, amount: 12 },
{ item: itemList.noctilucous_jade, amount: 45 },
{ item: itemList.golden_raven_insignia, amount: 12 },
],
mora: 100000,
},
{
items: [
{ item: itemList.agnidus_agate_gemstone, amount: 6 },
{ item: itemList.juvenile_jade, amount: 20 },
{ item: itemList.noctilucous_jade, amount: 60 },
{ item: itemList.golden_raven_insignia, amount: 24 },
],
mora: 120000,
},
],
stats: { hp: 10425, atk: 231, def: 743 },
material: {
book: [itemList.teachings_of_gold, itemList.guide_to_gold, itemList.philosophies_of_gold],
material: [itemList.treasure_hoarder_insignia, itemList.silver_raven_insignia, itemList.golden_raven_insignia],
boss: itemList.bloodjade_branch,
},
},
zhongli: {
name: 'Zhongli',
id: 'zhongli',

View file

@ -635,4 +635,5 @@ export const itemList = {
qingxin: { id: 'qingxin', name: 'Qingxin' },
shadow_of_the_warrior: { id: 'shadow_of_the_warrior', name: 'Shadow of the Warrior' },
juvenile_jade: { id: 'juvenile_jade', name: 'Juvenile Jade', rarity: 5 },
bloodjade_branch: { id: 'bloodjade_branch', name: 'Bloodjade Branch', rarity: 5 }
};

View file

@ -33,6 +33,17 @@ export const eventsData = [
url: 'https://webstatic-sea.mihoyo.com/ys/event/e20210421-homeland/index.html',
showOnHome: true,
},
{
name: 'To the Stars Once More',
pos: '20% 15%',
image: 'to_the_stars_once_more.jpg',
start: '2021-04-28 13:00:00',
end: '2021-05-17 23:59:59',
color: '#DDC59A',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/12368',
showOnHome: true,
},
],
[
{
@ -68,18 +79,6 @@ export const eventsData = [
url: 'https://www.hoyolab.com/genshin/article/295568',
showOnHome: true,
},
{
name: 'Update 1.5!',
pos: '0% 25%',
image: 'update15.jpg',
start: '2021-04-28 06:00:00',
end: '2021-05-05 06:00:00',
color: '#FFD768',
zoom: '130%',
url: 'https://www.youtube.com/watch?v=wgNiGj1nGYE',
showOnHome: true,
startOnly: true,
},
{
name: 'Windtrace',
pos: '0% 80%',
@ -117,13 +116,13 @@ export const eventsData = [
},
{
name: 'Energy Amplifier Initiation',
pos: '0% 20%',
image: 'energy_amplifier.jpg',
pos: '0% 25%',
image: 'energy_amplifier_initiation.jpg',
start: '2021-04-30 10:00:00',
end: '2021-05-17 04:00:00',
color: '#5CB8FE',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/12281',
zoom: '220%',
url: 'https://www.hoyolab.com/genshin/article/329821',
showOnHome: true,
},
],
@ -171,6 +170,33 @@ export const eventsData = [
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
startOnly: true,
},
{
name: 'Act I',
start: '2021-04-30 10:00:00',
end: '2021-05-03 04:00:00',
color: '#5CB8FE',
url: 'https://www.hoyolab.com/genshin/article/329821',
startOnly: true,
},
{
name: 'Act II',
start: '2021-05-03 04:00:00',
end: '2021-05-06 04:00:00',
color: '#5CB8FE',
url: 'https://www.hoyolab.com/genshin/article/329821',
startOnly: true,
},
{
name: 'Act III',
start: '2021-05-06 04:00:00',
end: '2021-05-17 04:00:00',
color: '#5CB8FE',
url: 'https://www.hoyolab.com/genshin/article/329821',
zoom: '220%',
pos: '0% 25%',
image: 'energy_amplifier_initiation.jpg',
startOnly: true,
},
],
[
{
@ -300,6 +326,16 @@ export const eventsData = [
image: 'paimon_bargain.png',
description: 'Now selling: Razor, Amber, and Royal Weapons.',
},
{
name: "Paimon's Bargain - Bennett, Lisa, and Blackcliff",
pos: '0% 50%',
zoom: '150%',
start: '2021-05-01 04:00:00',
end: '2021-06-01 04:00:00',
color: '#B6A1EA',
image: 'paimon_bargain.png',
description: 'Now selling: Bennett, Lisa, and Blackcliff Weapons.',
},
],
[
{

View file

@ -72,6 +72,7 @@ export function process(id) {
image,
total: 0,
legendary: [],
pityCount: [],
rarePity: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
rare: {
character: [],
@ -97,6 +98,7 @@ export function process(id) {
let currentBannerIndex = -1;
let hasManualInput = false;
let pity = 0;
for (let i = 0; i < pullData.length; i++) {
const pull = pullData[i];
const next = pullData[i + 1] || { time: dayjs().year(2000).unix() };
@ -128,6 +130,9 @@ export function process(id) {
selectedBanners[currentBannerIndex].total++;
const currentPity = selectedBanners[currentBannerIndex].pityCount[pity];
selectedBanners[currentBannerIndex].pityCount[pity] = (currentPity || 0) + 1;
const newPull = {
...pull,
formattedTime: formatTime(pull.time),
@ -136,6 +141,7 @@ export function process(id) {
banner: currentBanner,
start: startBanner,
at: selectedBanners[currentBannerIndex].total,
currentPity: ++pity,
};
if (item.rarity === 5) {
@ -146,6 +152,7 @@ export function process(id) {
selectedBanners[currentBannerIndex].legendary.push(newPull);
allLegendary.push(newPull);
pity = 0;
} else if (item.rarity === 4) {
allRare.push(newPull);
selectedBanners[currentBannerIndex].rarePity[newPull.pity - 1]++;

View file

@ -1,6 +1,10 @@
import { process } from './wish';
const bannerCategories = ['beginners', 'standard', 'character-event', 'weapon-event'];
const rareInclude = {
300011: ['rosaria'],
300012: ['yanfei', 'noelle', 'diona'],
};
async function sendWish(data) {
try {
@ -14,7 +18,7 @@ async function sendWish(data) {
}
}
export async function submitWishTally(lastPull) {
export async function submitWishTally() {
let prefixId = 0;
for (const id of bannerCategories) {
prefixId += 100000;
@ -33,6 +37,9 @@ export async function submitWishTally(lastPull) {
const total = banner[i].total;
if (total === 0) continue;
const pityCount = [...banner[i].pityCount].map((e) => e || 0);
if (pityCount.length > 90) continue;
const rarePity = banner[i].rarePity;
const legendaryCount = banner[i].legendary.length;
const rareCount = banner[i].rare.character.length + banner[i].rare.weapon.length;
@ -46,15 +53,18 @@ export async function submitWishTally(lastPull) {
5,
]);
// rosaria only
const rosariaPulls = banner[i].rare.character
.filter((e) => e.id === 'rosaria')
.map((e) => [e.time.toString(), e.id, e.type, e.pity, e.group === 'group', true, 4]);
legendaryPulls.push(...rosariaPulls);
// specific 4star include
if (rareInclude[prefixId + i + 1]) {
const includedRarePulls = banner[i].rare.character
.filter((e) => rareInclude[prefixId + i + 1].includes(e.id))
.map((e) => [e.time.toString(), e.id, e.type, e.pity, e.group === 'group', true, 4]);
legendaryPulls.push(...includedRarePulls);
}
console.log(legendaryPulls);
console.log(rarePity);
console.log(legendaryCount, rareCount, total);
// console.log(pityCount);
// console.log(legendaryPulls);
// console.log(rarePity);
// console.log(legendaryCount, rareCount, total);
await sendWish({
firstPulls: firstFivePulls,
@ -64,7 +74,7 @@ export async function submitWishTally(lastPull) {
total,
legendary: legendaryCount,
rare: rareCount,
lastPull,
pityCount,
});
}
}

View file

@ -18,8 +18,8 @@
"message": "Your best Genshin Impact companion! Help you plan what to farm with ascension calculator, also track your progress with todo and wish counter.",
"banner": {
"featured": [
"Rosaria",
"Tartaglia"
"Zhongli",
"Yanfei"
],
"summoned": "Summoned",
"percentage": "from all {rarity}",
@ -117,6 +117,10 @@
"manualButton": "Enable Manual Input",
"errorBanner": "Banner time missmatch! Please adjust your server on settings page. Still not working? please leave a message on Discord 😅",
"globalWishTally": "Global Wish Tally",
"pityTooltip": [
"Shows your current {rarity} pity",
"{count} pulls to guaranteed {rarity}"
],
"import": {
"title": "Import Wish History",
"faqsButton": "FAQ - READ FIRST",
@ -300,7 +304,8 @@
"totalThisBanner": "Total pull on this banner",
"worth": "Worth",
"loading": "Loading... (If this stuck, change your server on settings page)",
"guaranteed": "The next 5★ is guaranteed to be the promotional character or weapon"
"guaranteed": "The next 5★ is guaranteed to be the promotional character or weapon",
"unknown_3_star": "Unknown"
},
"tally": {
"title": "Wish Tally",
@ -310,6 +315,7 @@
"wonFiftyFifty": "won 50:50",
"fromFiveStar": "from all 5★",
"fromFourStar": "from all 4★",
"fromFourStarFeatured": "from featured 4★",
"show": "Show",
"name": "Name",
"total": "Total",
@ -575,4 +581,4 @@
"title": "Achievement",
"of": "of"
}
}
}

View file

@ -17,10 +17,6 @@
"welcome": "Selamat Datang di Paimon.moe! 👋",
"message": "Your best Genshin Impact companion! Membantu kamu merencanakan apa yang harus di farm dengan kalkulator ascension, juga catat progress mu dengan todo dan wish counter.",
"banner": {
"featured": [
"Rosaria",
"Tartaglia"
],
"summoned": "Pulang",
"percentage": "dari semua {rarity}",
"avg": "Pity rata-rata",
@ -92,6 +88,10 @@
"manualButton": "Nyalakan Input Manual",
"errorBanner": "Waktu banner salah! Coba sesuaikan server di halaman settings. Masih gak bisa? tolong chat di Discord 😅",
"globalWishTally": "Perhitungan Pity Wish Global",
"pityTooltip": [
"Ini adalah pity {rarity} sekarang",
"{count}x wish lagi untuk dijamin {rarity}"
],
"import": {
"title": "Import Riwayat Wish",
"faqsButton": "FAQS - BACA DULU",
@ -285,6 +285,7 @@
"wonFiftyFifty": "menang 50:50",
"fromFiveStar": "dari semua 5★",
"fromFourStar": "dari semua 4★",
"fromFourStarFeatured": "dari featured 4★",
"show": "Tampilkan",
"name": "Nama",
"total": "Total",

View file

@ -17,10 +17,6 @@
"welcome": "Paimon.moe에 어서오세요! 👋",
"message": "최고의 원신 동반자! 돌파 계산기로 파밍 계획을 도와주고, 할 일 목록과 기원 통계를 통해 진척도를 확인해줍니다.",
"banner": {
"featured": [
"로자리아",
"타르탈리아"
],
"summoned": "획득",
"percentage": "(모든 {rarity} 중)",
"avg": "평균 천장",

View file

@ -14,10 +14,6 @@
"welcome": "Добро пожаловать на Paimon.moe! 👋",
"message": "Твой лучший компаньон в Genshin Impact! Поможет тебе распланировать что фармить в калькуляторе возвышения, а также отслеживать свой прогресс с помощью счетчика молитв и cписка дел",
"banner": {
"featured": [
"Rosaria",
"Tartaglia"
],
"summoned": "Призывов",
"percentage": "из всех {rarity}",
"avg": "Среднее число молитв",

View file

@ -13,21 +13,21 @@
const dispatch = createEventDispatcher();
const featured = {
rosaria: {
rarity: 'rare',
count: 0,
average: '...',
percentage: '...',
},
tartaglia: {
zhongli: {
rarity: 'legendary',
count: 0,
average: '...',
percentage: '...',
},
yanfei: {
rarity: 'rare',
count: 0,
average: '...',
percentage: '...',
},
};
const bannerId = 300011;
const image = 'childerosaria.png';
const bannerId = 300012;
const image = 'zhongliyanfei.png';
let loading = true;
let user = '';

View file

@ -11,6 +11,7 @@
import debounce from 'lodash/debounce';
import Check from '../../components/Check.svelte';
import Checkbox from '../../components/Checkbox.svelte';
import { getAccountPrefix } from '../../stores/account';
import { readSave, updateSave } from '../../stores/saveManager';
@ -29,6 +30,9 @@
let obtainedPrimogem = 0;
let categories = [];
let originalList = [];
let sort = false;
function parseCategories() {
categories = Object.entries(achievement).map(([id, data]) => ({
id,
@ -66,6 +70,28 @@
}));
}
function orderAchievement() {
if (!sort) {
if (originalList.length === 0) return;
list = originalList;
return;
}
originalList = list.slice();
list = list.sort((a, b) => {
let first = a;
let second = b;
if (Array.isArray(a)) first = a[a.length - 1];
if (Array.isArray(b)) second = b[b.length - 1];
return first.checked === second.checked ? 0 : first.checked ? 1 : -1;
});
}
function changeSort(val) {
sort = val;
orderAchievement();
}
const saveData = debounce(() => {
const data = JSON.stringify(checkList);
@ -93,6 +119,17 @@
}
});
if (sort) {
originalList = list.slice();
list = list.sort((a, b) => {
let first = a;
let second = b;
if (Array.isArray(a)) first = a[a.length - 1];
if (Array.isArray(b)) second = b[b.length - 1];
return first.checked === second.checked ? 0 : first.checked ? 1 : -1;
});
}
if (firstLoad) return;
await tick();
achievementContainer.scrollIntoView({
@ -134,6 +171,31 @@
const achievementData = readSave(`${prefix}achievement`);
if (achievementData !== null) {
checkList = JSON.parse(achievementData);
migrateNewVersion();
}
}
function migrateNewVersion() {
// version 1.5
// prettier-ignore
const movedIds = [
84026, 84100, 84101, 84104, 84028,
84107, 84102, 84108, 84105, 84103,
84106, 84109, 84110, 84111, 84112,
84113, 84114, 84115
];
if (checkList['17'] === undefined) {
checkList['17'] = {};
}
if (checkList['0']) {
for (const item of movedIds) {
if (checkList['0'][item] === true) {
checkList['17'][item] = true;
delete checkList['0'][item];
}
}
}
}
@ -168,6 +230,9 @@
<img src="/images/primogem.png" class="w-4 h-4 ml-1" alt="primogem" />
</div>
</div>
<div class="lg:pl-4 text-white">
<Checkbox checked={sort} on:change={() => changeSort(!sort)}>Show not achieved first</Checkbox>
</div>
</div>
<div class="flex flex-col lg:flex-row space-y-3 lg:space-y-0 lg:space-x-3">
<div class="flex flex-col space-y-2 lg:h-screen lg:overflow-auto lg:sticky lg:pr-1 pb-4 category">

View file

@ -8,8 +8,8 @@
const name = data.name;
</script>
<div class="py-4 rounded-xl bg-item flex flex-col mb-4" style="{fade ? 'filter: grayscale(30%);' : ''}">
<div class="flex mb-2 items-start px-4">
<div class="py-4 rounded-xl bg-item flex flex-col mb-2" style="{fade ? 'filter: grayscale(30%);' : ''}">
<div class="flex items-start px-4">
<img src="/images/skills/{id}/{image}.png" alt={name} class="w-16 h-16 mr-4" />
<div>
<p class="font-black font-display text-xl">{name}</p>

View file

@ -247,7 +247,7 @@
<div class="lg:ml-64 pt-20 px-4 md:px-8 lg:pt-8">
<div class="bg-item rounded-xl mb-4 p-4">
<p class="text-white">{$t('settings.version')} <b>1.4</b></p>
<p class="text-white">{$t('settings.version')} <b>1.5</b></p>
</div>
<div class="bg-item rounded-xl mb-4 p-4 flex flex-col">
<p class="text-white">{$t('settings.multiple')}</p>

View file

@ -75,6 +75,11 @@
index: i,
...e,
});
} else if (e.type === 'unknown_3_star' && showRarity[2]) {
sorted.push({
index: i,
...e,
});
}
}
@ -228,6 +233,7 @@
id: 'unknown_3_star',
time: dayjs().unix(),
pity: 1,
manualInput: true,
})),
];
}
@ -278,7 +284,7 @@
<div
class={`${
isEdit ? 'bg-item flex-col py-2' : 'bg-background flex-row items-center justify-center mb-2 p-4'
} rounded-xl flex`}
} rounded-xl flex relative`}
>
<span class="text-gray-200 whitespace-no-wrap flex-1">
{$t('wish.lifetimePulls')}<br />
@ -294,9 +300,16 @@
<div
class={`${
isEdit ? 'bg-item flex-col py-2' : 'bg-background flex-row items-center justify-center mb-2 p-4'
} rounded-xl flex`}
title={`${legendaryPity - legendary} pulls to guaranteed 5 star`}
} rounded-xl flex group relative`}
>
{#if !isEdit}
<div class="group-hover:flex hidden absolute left-0 pl-2 items-center z-10" style="max-width: 80%;">
<div class="bg-gray-200 text-gray-900 px-4 py-2 rounded-xl text-sm md:text-base">
<p>{$t('wish.pityTooltip.0', { values: { rarity: '5★' } })}</p>
<p>{$t('wish.pityTooltip.1', { values: { count: legendaryPity - legendary, rarity: '5★' } })}</p>
</div>
</div>
{/if}
<span class="text-gray-200 whitespace-no-wrap flex-1">
5
<Icon path={mdiStar} size={0.75} className="mb-1" />
@ -310,9 +323,16 @@
<div
class={`${
isEdit ? 'bg-item flex-col py-2' : 'bg-background flex-row items-center justify-center mb-2 p-4'
} rounded-xl flex`}
title={`${10 - rare} pulls to guaranteed 4 star`}
} rounded-xl flex group relative`}
>
{#if !isEdit}
<div class="group-hover:flex hidden absolute left-0 pl-2 items-center z-10" style="max-width: 80%;">
<div class="bg-gray-200 text-gray-900 px-4 py-2 rounded-xl text-sm md:text-base">
<p>{$t('wish.pityTooltip.0', { values: { rarity: '4★' } })}</p>
<p>{$t('wish.pityTooltip.1', { values: { count: 10 - rare, rarity: '4★' } })}</p>
</div>
</div>
{/if}
<span class="text-gray-200 whitespace-no-wrap flex-1">
4
<Icon path={mdiStar} size={0.75} className="mb-1" />
@ -370,6 +390,9 @@
<button on:click={() => toggleShowRarity(2)} class={`pill normal ${showRarity[2] ? 'active' : ''}`}>
3 <Icon path={mdiStar} size={0.75} className="mb-1" />
</button>
<a href="/wish/{id}" class="pill normal">
<Icon path={mdiTableOfContents} color="white" />
</a>
</div>
<table class="w-full">
<tr>
@ -378,7 +401,11 @@
<th class="border-b border-gray-700 text-gray-400 font-display text-right">Pity</th>
</tr>
{#each sortedPull as pull}
<tr on:click={manualInput ? () => openEditModal(pull.index, pull.type, pull.id, pull.time, pull.pity) : () => {}}>
<tr
on:click={manualInput
? () => openEditModal(pull.index, pull.type, pull.id, pull.time, pull.pity)
: () => {}}
>
{#if pull.type === 'character'}
<td
class={`border-b border-gray-700 py-1 pl-2 font-semibold ${
@ -399,6 +426,8 @@
: 'text-primary'
}`}>{weaponList[pull.id].name}</td
>
{:else if pull.type === 'unknown_3_star'}
<td class="border-b border-gray-700 py-1 pl-2 font-semibold text-primary">Unknown</td>
{/if}
<td class="border-b border-gray-700 text-sm py-1 px-2 whitespace-no-wrap" style="font-family: monospace;"
>{dayjs.unix(pull.time).format('YYYY-MM-DD HH:mm:ss')}</td

View file

@ -104,6 +104,12 @@
min: 0,
max: 0,
};
const rareInclude = {
300011: ['rosaria'],
300012: ['yanfei', 'noelle', 'diona'],
};
let promotedRarePercentage = 0;
let legendaryList = [];
let legendaryPity = [];
@ -137,14 +143,14 @@
guaranteed: item.guaranteed,
percentage:
(item.count /
(type === 'character' && id !== 200001 && i === 1 ? data.total.rare : data.total.legendary)) *
(type === 'character' && id !== 200002 && i === 1 ? data.total.rare : data.total.legendary)) *
100,
};
}
}
// only for standard banner
if (id === 200001) {
if (id === 200002) {
const values = [0, 0];
for (let i = 0; i < data.list.length; i++) {
@ -165,6 +171,23 @@
];
}
// only for zhongli banner upward
if (id > 300011 && id < 400000) {
const totalRare = data.list.reduce((prev, current) => {
if (rareInclude[id].includes(current.name)) {
prev.total += current.count;
}
if (featured[1] === current.name) {
prev.featured = current.count;
}
return prev;
}, {
total: 0,
featured: 0,
});
promotedRarePercentage = totalRare.featured / totalRare.total * 100
}
legendary = {
total: data.total.legendary,
percentage: (data.total.legendary / data.total.all) * 100,
@ -257,7 +280,7 @@
class="flex flex-row items-center bg-background rounded-xl py-2 relative px-4 mb-2 flex-1 cursor-pointer"
on:click={toggleLegendaryList}
>
{#if id === 200001}
{#if id === 200002}
<table class="flex-1">
<tr>
<td
@ -310,13 +333,16 @@
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
</p>
<p class="text-gray-400">
{#if id === 300011 && i === 0}
{#if id >= 300011 && id < 400000 && i === 0}
{numberFormat.format(
((featuredValues[i].total - featuredValues[i].guaranteed) /
(data.total.legendary - featuredValues[i].guaranteed)) *
100,
)}%
{$t('wish.tally.wonFiftyFifty')}
{:else if id > 300011 && id < 400000 && i === 1}
{numberFormat.format(promotedRarePercentage)}%
{$t('wish.tally.fromFourStarFeatured')}
{:else}
{numberFormat.format(featuredValues[i].percentage)}% {$t(
type === 'character' && i === 1 ? 'wish.tally.fromFourStar' : 'wish.tally.fromFiveStar',

View file

@ -0,0 +1,638 @@
<script>
import { t, locale } from 'svelte-i18n';
import { mdiChevronDown, mdiLoading, mdiStar } from '@mdi/js';
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/id';
import 'dayjs/locale/en';
import 'dayjs/locale/ru';
dayjs.extend(duration);
dayjs.extend(relativeTime);
import Icon from '../../../components/Icon.svelte';
import { characters } from '../../../data/characters';
import { weaponList } from '../../../data/weaponList';
const numberFormat = Intl.NumberFormat('en', {
maximumFractionDigits: 2,
minimumFractionDigits: 0,
});
const numberFormatSecondary = Intl.NumberFormat('en', {
maximumFractionDigits: 1,
minimumFractionDigits: 0,
});
// prettier-ignore
const legendaryMapping = {
character: [
0, 1, 1, 1, 1, 2, 2, 2, 2, 2, // 1
3, 3, 3, 3, 3, 4, 4, 4, 4, 4, // 2
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, // 3
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 5
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 6
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // 7
11, 11, 11, 12, 13, 14, 15, 16, 17, 18, // 8
19, 20, 20, 20, 20, 21, 21, 21, 21, 22, // 9
],
weapon: [
0, 1, 1, 1, 1, 2, 2, 2, 2, 2, // 1
3, 3, 3, 3, 3, 4, 4, 4, 4, 4, // 2
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, // 3
7, 7, 7, 7, 7, 8, 8, 8, 8, 8, // 4
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 5
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // 6
11, 11, 11, 12, 13, 14, 15, 16, 17, 18, // 7
19, 20, 20, 20, 20, 21, 21, 21, 21, 22, // 8
],
};
// prettier-ignore
const legendaryLabels = {
character: [
1, [2, 5], [6, 10], [11, 15], [16, 20], [21, 25],
[26, 30], [31, 40], [41, 50], [51, 60], [61, 70],
[71, 73], 74, 75, 76, 77, 78, 79, 80, 81,
[82, 85], [86, 89], 90,
],
weapon: [
1, [2, 5], [6, 10], [11, 15], [16, 20], [21, 25],
[26, 30], [31, 35], [36, 40], [41, 50], [51, 60],
[61, 63], 64, 65, 66, 67, 68, 69, 70, 71,
[72, 75], [76, 79], 80,
],
};
export let type;
export let id;
const legendaryMap = legendaryMapping[type];
const legendaryStep = legendaryLabels[type];
let openLegendaryList = false;
let loading = true;
let error = false;
let data = {};
export let banner;
export let featured = [];
let featuredValues = featured.map(() => ({
total: 0,
percentage: 0,
}));
let legendary = {
total: 0,
percentage: 0,
};
let rare = {
total: 0,
percentage: 0,
};
let rarePercentage = {
min: 0,
max: 0,
};
let legendaryPercentage = {
min: 0,
max: 0,
};
const rareInclude = {
300011: ['rosaria'],
300012: ['yanfei', 'noelle', 'diona'],
};
let promotedRarePercentage = 0;
let legendaryList = [];
let legendaryPity = [];
let rarePity = [];
let updateTime = '';
locale.subscribe((val) => {
dayjs.locale(val.substring(0, 2));
updateTime = dayjs(data.time).fromNow();
});
async function getData() {
const url = new URL(`${__paimon.env.API_HOST}/wish`);
const query = new URLSearchParams({ banner: id });
url.search = query.toString();
try {
const res = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
});
data = await res.json();
for (let i = 0; i < featured.length; i++) {
const feat = featured[i];
const item = data.list.find((e) => e.name === feat);
if (item) {
featuredValues[i] = {
total: item.count,
guaranteed: item.guaranteed,
percentage:
(item.count /
(type === 'character' && id !== 200002 && i === 1 ? data.total.rare : data.total.legendary)) *
100,
};
}
}
// only for standard banner
if (id === 200002) {
const values = [0, 0];
for (let i = 0; i < data.list.length; i++) {
const feat = data.list[i];
const index = feat.type === 'character' ? 0 : 1;
values[index] += feat.count;
}
featuredValues = [
{
total: values[0],
percentage: (values[0] / data.total.legendary) * 100,
},
{
total: values[1],
percentage: (values[1] / data.total.legendary) * 100,
},
];
}
// only for zhongli banner upward
if (id > 300011 && id < 400000) {
const totalRare = data.list.reduce(
(prev, current) => {
if (rareInclude[id].includes(current.name)) {
prev.total += current.count;
}
if (featured[1] === current.name) {
prev.featured = current.count;
}
return prev;
},
{
total: 0,
featured: 0,
},
);
promotedRarePercentage = (totalRare.featured / totalRare.total) * 100;
}
legendary = {
total: data.total.legendary,
percentage: (data.total.legendary / data.total.all) * 100,
};
rare = {
total: data.total.rare,
percentage: (data.total.rare / data.total.all) * 100,
};
rarePity = data.pityCount.rare.map((e) => ({
total: e,
percentage: e / data.total.rare,
}));
rarePity.forEach((e) => {
if (rarePercentage.min > e.percentage) {
rarePercentage.min = e.percentage;
}
if (rarePercentage.max < e.percentage) {
rarePercentage.max = e.percentage;
}
});
const mapped = new Array(legendaryMap[legendaryMap.length - 1] + 1).fill(0);
const legendaryPityData = data.pityCount.legendary.slice(1, 91);
for (let i = 0; i < legendaryPityData.length; i++) {
const label = legendaryMap[i];
const pity = legendaryPityData[i];
mapped[label] += pity;
}
const percentageEachPity = new Array(90).fill(0);
for (let i = 0; i < 90; i++) {
percentageEachPity[i] = legendaryPityData[i] / data.countEachPity[i];
}
console.log(percentageEachPity);
const mappedPercentage = new Array(legendaryMap[legendaryMap.length - 1] + 1).fill(0);
for (let i = 0; i < 90; i++) {
const label = legendaryMap[i];
const percentage = percentageEachPity[i];
if (mappedPercentage[label] < percentage) {
mappedPercentage[label] = percentage;
}
}
legendaryPity = mapped.map((e, i) => ({
total: e,
percentage: mappedPercentage[i],
}));
legendaryList = data.list.sort((a, b) => {
if (featured.includes(a.name) && featured.includes(b.name)) {
return 0;
} else if (featured.includes(a.name)) {
return -1;
} else if (featured.includes(b.name)) {
return 1;
} else {
return b.count - a.count;
}
});
legendaryPity.forEach((e) => {
if (legendaryPercentage.min > e.percentage) {
legendaryPercentage.min = e.percentage;
}
if (legendaryPercentage.max < e.percentage) {
legendaryPercentage.max = e.percentage;
}
});
updateTime = dayjs(data.time).fromNow();
loading = false;
} catch (err) {
console.error(err);
loading = false;
error = true;
}
}
function mapVal(value, x1, y1, x2, y2) {
return ((value - x1) * (y2 - x2)) / (y1 - x1) + x2;
}
function toggleLegendaryList() {
openLegendaryList = !openLegendaryList;
}
onMount(() => {
getData();
});
</script>
<div class="flex flex-col bg-item rounded-xl px-4 pt-4 pb-2 mb-4 space-y-4">
<div class="flex flex-col xl:flex-row">
<img src="/images/banners/{banner.name} {banner.image}.png" alt={banner.name} class="rounded-xl xl:h-64 xl:mr-4" />
<div class="h-4 xl:h-0" />
{#if loading}
<Icon className="m-4" path={mdiLoading} color="white" size={2} spin />
{:else if !error}
<div class="flex flex-col pity-summary" style="min-height: 16rem;">
<div
class="flex flex-row items-center bg-background rounded-xl py-2 relative px-4 mb-2 flex-1 cursor-pointer"
on:click={toggleLegendaryList}
>
{#if id === 200002}
<table class="flex-1">
<tr>
<td
class="font-black text-white text-5xl leading-10 pr-2 text-center"
style="padding-top: 4px; width: 1%;"
>
{featuredValues[0].total}
</td>
<td>
<p class="text-white font-semibold">
{$t('wish.tally.character')}
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
</p>
<p class="text-gray-400">
{numberFormat.format(featuredValues[0].percentage)}% {$t('wish.tally.fromFiveStar')}
</p>
</td>
</tr>
<tr>
<td
class="font-black text-white text-5xl leading-10 pr-2 text-center"
style="padding-top: 4px; width: 1%;"
>
{featuredValues[1].total}
</td>
<td>
<p class="text-white font-semibold">
{$t('wish.tally.weapon')}
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
</p>
<p class="text-gray-400">
{numberFormat.format(featuredValues[1].percentage)}% {$t('wish.tally.fromFiveStar')}
</p>
</td>
</tr>
</table>
{:else}
<table class="flex-1">
{#each featured as feat, i}
<tr>
<td
class="font-black text-white text-5xl leading-10 pr-2 text-center"
style="padding-top: 4px; width: 1%;"
>
{featuredValues[i].total}
</td>
<td>
<p class="text-white font-semibold">
{type === 'character' ? characters[feat].name : weaponList[feat].name}
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
</p>
<p class="text-gray-400">
{#if id >= 300011 && id < 400000 && i === 0}
{numberFormat.format(
((featuredValues[i].total - featuredValues[i].guaranteed) /
(data.total.legendary - featuredValues[i].guaranteed)) *
100,
)}%
{$t('wish.tally.wonFiftyFifty')}
{:else if id > 300011 && id < 400000 && i === 1}
{numberFormat.format(promotedRarePercentage)}%
{$t('wish.tally.fromFourStarFeatured')}
{:else}
{numberFormat.format(featuredValues[i].percentage)}% {$t(
type === 'character' && i === 1 ? 'wish.tally.fromFourStar' : 'wish.tally.fromFiveStar',
)}
{/if}
</p>
</td>
</tr>
{/each}
</table>
{/if}
<div class="text-gray-400 chevron-detail">
<Icon path={mdiChevronDown} />
</div>
</div>
<div
class="flex items-center justify-center bg-background rounded-xl px-4 py-2 relative mb-2 flex-1 text-legendary-from"
>
<p class="font-black mr-2 text-5xl leading-10" style="margin-top: 4px;">
{numberFormat.format(legendary.percentage)}%
</p>
<div class="flex flex-col flex-1">
<p class="font-semibold whitespace-no-wrap">
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
</p>
<p>Total {numberFormat.format(legendary.total)}</p>
</div>
</div>
<div class="flex items-center justify-center bg-background rounded-xl px-4 py-2 relative flex-1 text-rare-from">
<p class="font-black mr-2 text-5xl leading-10" style="margin-top: 4px;">
{numberFormatSecondary.format(rare.percentage)}%
</p>
<div class="flex flex-col flex-1">
<p class="font-semibold whitespace-no-wrap">
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
<Icon path={mdiStar} size={0.8} />
</p>
<p>Total {numberFormat.format(rare.total)}</p>
</div>
</div>
</div>
{#if openLegendaryList}
<div transition:fade class="bg-background rounded-xl mt-2 xl:mt-0 py-2 px-4 xl:ml-2">
<table class="text-white w-full">
<tr>
<td class="border-b border-gray-700 px-2">{$t('wish.tally.name')}</td>
<td class="border-b border-gray-700 px-2 text-center">{$t('wish.tally.total')}</td>
</tr>
{#each legendaryList as item}
<tr>
<td class="border-b border-gray-700 px-2">
{item.type === 'character' ? characters[item.name].name : weaponList[item.name].name}
</td>
<td class="border-b border-gray-700 px-2 text-center">{item.count}</td>
</tr>
{/each}
</table>
<p class="text-gray-400 mt-2 text-sm text-center">
{$t('wish.tally.update')}
{updateTime}
</p>
</div>
{/if}
{/if}
</div>
{#if !loading && !error}
<div class="border border-background rounded-xl xl:hidden overflow-hidden">
<table class="text-white w-full table-fixed">
<tr>
<th class="font-display text-gray-200 font-semibold px-2 pt-2">5★ {$t('wish.tally.pity')}</th>
<th class="font-display text-gray-200 font-semibold border-l border-r border-background px-2 pt-2"
>{$t('wish.tally.total')}</th
>
<th class="font-display text-gray-200 font-semibold px-2 pt-2">Chance%</th>
</tr>
{#each legendaryStep as label, i}
<tr>
<td class="text-center px-2 border-t border-background">
{#if Array.isArray(label)}
{label[0]} - {label[1]}
{:else}
{label}
{/if}
</td>
<td class="text-center px-2 border-t border-l border-r border-background">
{numberFormat.format(legendaryPity[i].total)}
</td>
<td
class="text-center py-1 border-t border-background"
style="background: rgba(25, 142, 81, {mapVal(
legendaryPity[i].percentage,
legendaryPercentage.min,
legendaryPercentage.max,
0,
1,
)}); width: 60px;"
>
{numberFormat.format(legendaryPity[i].percentage * 100)}
</td>
</tr>
{/each}
</table>
</div>
<div class="border border-background rounded-xl xl:hidden overflow-hidden">
<table class="text-white w-full table-fixed">
<tr>
<th class="font-display text-gray-200 font-semibold px-2 pt-2">4★ {$t('wish.tally.pity')}</th>
<th class="font-display text-gray-200 font-semibold border-l border-r border-background px-2 pt-2"
>{$t('wish.tally.total')}</th
>
<th class="font-display text-gray-200 font-semibold px-2 pt-2">%</th>
</tr>
{#each rarePity as pity, i}
<tr>
<td class="text-center px-2 border-t border-background">
{i + 1}
</td>
<td class="text-center px-2 border-t border-l border-r border-background">
{numberFormat.format(pity.total)}
</td>
<td
class="text-center py-1 border-t border-background"
style="background: rgba(25, 142, 81, {mapVal(
pity.percentage,
rarePercentage.min,
rarePercentage.max,
0,
1,
)}); width: 60px;"
>
{numberFormat.format(pity.percentage * 100)}
</td>
</tr>
{/each}
</table>
</div>
<div class="border border-background rounded-xl hidden xl:block">
<table class="text-white w-full table-fixed text-sm">
<tr>
<td
class="font-display text-gray-200 font-semibold px-2 py-1 whitespace-no-wrap text-right border-b border-background"
>
5★<br />{$t('wish.tally.pity')}
</td>
{#each legendaryStep as label}
<td class="text-center py-1 border-l border-b border-background">
{#if Array.isArray(label)}
<div>
<span class="block">{label[0]}</span>
<span class="block" style="height: 4px; overflow: hidden; line-height: 4px;">|</span>
<span class="block">{label[1]}</span>
</div>
{:else}
{label}
{/if}
</td>
{/each}
</tr>
<tr>
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-b border-background">
Total
</td>
{#each legendaryPity as pity}
<td class="text-center py-1 border-l border-b border-background">{numberFormat.format(pity.total)}</td>
{/each}
</tr>
<tr>
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-background" title="Chance %">Chc%</td>
{#each legendaryPity as pity}
<td
class="text-center py-1 border-l border-background"
style="background: rgba(25, 142, 81, {mapVal(
pity.percentage,
legendaryPercentage.min,
legendaryPercentage.max,
0,
1,
)}); width: 60px;"
>
{numberFormat.format(pity.percentage * 100)}
</td>
{/each}
</tr>
</table>
</div>
<div class="flex flex-wrap">
<div
class="border border-background rounded-xl hidden xl:block overflow-hidden mr-4 mb-2"
style="width: fit-content;"
>
<table class="text-white text-sm">
<tr>
<td
class="font-display text-gray-200 font-semibold px-2 py-1 whitespace-no-wrap text-right border-b border-background"
>
4★ {$t('wish.tally.pity')}
</td>
{#each rarePity as _, i}
<td class="text-center px-2 py-1 border-l border-b border-background">{i + 1}</td>
{/each}
</tr>
<tr>
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-b border-background"
>Total</td
>
{#each rarePity as pity}
<td class="text-center px-2 py-1 border-l border-b border-background"
>{numberFormat.format(pity.total)}</td
>
{/each}
</tr>
<tr>
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-background">%</td>
{#each rarePity as pity}
<td
class="text-center px-2 py-1 border-l border-background"
style="background: rgba(25, 142, 81, {mapVal(
pity.percentage,
rarePercentage.min,
rarePercentage.max,
0,
1,
)}); width: 60px;"
>
{numberFormat.format(pity.percentage * 100)}
</td>
{/each}
</tr>
</table>
</div>
<div class="flex flex-wrap text-white -mt-2 mb-2">
<div class="space-y-2 flex flex-col flex-wrap mr-2 mt-2">
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
{$t('wish.tally.wishTotal')} <span class="font-semibold ml-2">{numberFormat.format(data.total.all)}</span>
</div>
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
{$t('wish.tally.worth')} <img class="w-4 h-4 inline mx-1" src="/images/primogem.png" alt="primogem" />
<span class="font-semibold">{numberFormat.format(data.total.all * 160)}</span>
</div>
</div>
<div class="space-y-2 flex flex-col flex-wrap mt-2">
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
{$t('wish.tally.median')}
<span class="font-semibold ml-2">{numberFormat.format(data.median.legendary)}</span>
</div>
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
{$t('wish.tally.user')} <span class="font-semibold ml-2">{numberFormat.format(data.total.users)}</span>
</div>
</div>
</div>
</div>
{/if}
{#if error}
<div class="text-white mt-4">{$t('wish.tally.error')}</div>
{/if}
</div>
<style>
@screen xl {
.pity-summary {
min-width: 320px;
}
.chevron-detail {
@apply transform;
@apply -rotate-90;
}
}
</style>

View file

@ -1,13 +1,14 @@
<script>
import { t } from 'svelte-i18n';
import Button from '../../../components/Button.svelte';
import Button from '../../../components/Button.svelte';
import { banners } from '../../../data/banners';
import Item from './_item.svelte';
import ItemNew from './_itemNew.svelte';
let showOld = false;
let showOld = [false, false];
function showOldTally() {
showOld = true;
function showOldTally(index) {
showOld[index] = true;
}
</script>
@ -30,6 +31,8 @@ import Button from '../../../components/Button.svelte';
</p>
<div class="px-4 md:px-8">
<ItemNew type="character" banner={banners.characters[11]} id={300012} featured={['zhongli', 'yanfei']} />
<ItemNew type="weapon" banner={banners.weapons[10]} id={400011} featured={['summit_shaper', 'memory_of_dust']} />
<Item type="character" banner={banners.characters[10]} id={300011} featured={['tartaglia', 'rosaria']} />
<Item
type="weapon"
@ -37,14 +40,36 @@ import Button from '../../../components/Button.svelte';
id={400010}
featured={['skyward_harp', 'lost_prayer_to_the_sacred_winds']}
/>
<Item type="character" banner={banners.characters[9]} id={300010} featured={['venti']} />
<Item type="weapon" banner={banners.weapons[8]} id={400009} featured={['elegy_for_the_end', 'skyward_blade']} />
<Item type="character" banner={banners.standard[0]} id={200001} />
{#if showOld}
<Item type="character" banner={banners.characters[8]} id={300009} featured={['hu_tao']} />
<Item type="weapon" banner={banners.weapons[7]} id={400008} featured={['wolfs_gravestone', 'staff_of_homa']} />
<Item type="character" banner={banners.standard[0]} id={200002} />
{#if showOld[0]}
<Item type="character" banner={banners.characters[9]} id={300010} featured={['venti']} />
<Item
type="weapon"
banner={banners.weapons[8]}
id={400009}
featured={['skyward_harp', 'lost_prayer_to_the_sacred_winds']}
/>
{:else}
<Button on:click={showOldTally}>{$t('wish.tally.show')} {banners.characters[8].name} & {banners.weapons[7].name}</Button>
<Button on:click={() => showOldTally(0)}>
{$t('wish.tally.show')}
{banners.characters[9].name} & {banners.weapons[8].name}
</Button>
<div class="mb-2" />
{/if}
{#if showOld[1]}
<Item type="character" banner={banners.characters[8]} id={300009} featured={['hu_tao']} />
<Item
type="weapon"
banner={banners.weapons[7]}
id={400008}
featured={['wolfs_gravestone', 'staff_of_homa']}
/>
{:else}
<Button on:click={() => showOldTally(1)}>
{$t('wish.tally.show')}
{banners.characters[8].name} & {banners.weapons[7].name}
</Button>
<div class="mb-2" />
{/if}
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -54,6 +54,7 @@ module.exports = {
borderColor: ['responsive', 'hover', 'focus', 'focus-within', 'disabled'],
opacity: ['group-hover', 'responsive', 'hover', 'focus', 'disabled'],
borderWidth: ['responsive', 'last', 'hover', 'focus'],
display: ['responsive', 'group-hover'],
},
plugins: [],
future: {