Merge branch 'MadeBaruna:main' into main

pull/1/head
Broccoli-nyu 2021-06-13 12:50:08 +03:00
commit 8e66bc859b
106 changed files with 961 additions and 331 deletions

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Wall","type":"interior"},{"category":"Flooring","name":"Flooring","type":"interior"},{"category":"Ceiling","name":"Ceiling","type":"interior"},{"category":"Ceiling Lamp","name":"Ceiling Lamp","type":"interior"},{"category":"Room Door","name":"Room Door","type":"interior"},{"category":"Stairs","name":"Stairs","type":"interior"},{"category":"Cabinet","name":"Cabinet","type":"interior"},{"category":"Bookcase","name":"Bookcase","type":"interior"},{"category":"Table","name":"Table","type":"interior"},{"category":"Counter","name":"Counter","type":"interior"},{"category":"Bed","name":"Bed","type":"interior"},{"category":"Seating","name":"Seating","type":"interior"},{"category":"Ornament","name":"Ornament","type":"interior"},{"category":"Carpet","name":"Carpet","type":"interior"},{"category":"Lighting","name":"Lighting","type":"interior"},{"category":"Potted Plant","name":"Potted Plant","type":"interior"},{"category":"Utensil","name":"Utensil","type":"interior"},{"category":"Artwork","name":"Artwork","type":"interior"},{"category":"Hanging Ornament","name":"Hanging Ornament","type":"interior"},{"category":"Courtyard Wall","name":"Courtyard Wall","type":"exterior"},{"category":"Large Ornament","name":"Large Ornament","type":"exterior"},{"category":"Liyue","name":"Liyue","type":"exterior"},{"category":"Mondstadt","name":"Mondstadt","type":"exterior"},{"category":"Hilichurl Style","name":"Hilichurl Style","type":"exterior"},{"category":"Mercantile","name":"Mercantile","type":"exterior"},{"category":"Free Booth","name":"Free Booth","type":"exterior"},{"category":"Mountain","name":"Mountain","type":"exterior"},{"category":"Rock","name":"Rock","type":"exterior"},{"category":"Tree","name":"Tree","type":"exterior"},{"category":"Shrub","name":"Shrub","type":"exterior"},{"category":"Item","name":"Item","type":"exterior"},{"category":"Terrace","name":"Terrace","type":"exterior"},{"category":"Ornament","name":"Ornament","type":"exterior"},{"category":"Lighting","name":"Lighting","type":"exterior"},{"category":"Indoor Creature","name":"Indoor Creature","type":"interior"},{"category":"Outdoor Creature","name":"Outdoor Creature","type":"exterior"},{"category":"Mansion","name":"Mansion","type":"exterior"},{"category":"Fence","name":"Fence","type":"exterior"},{"category":"Seating","name":"Seating","type":"exterior"},{"category":"Table","name":"Table","type":"exterior"},{"category":"Cabinet","name":"Cabinet","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Murs","type":"interior"},{"category":"Flooring","name":"Sols","type":"interior"},{"category":"Ceiling","name":"Plafonds","type":"interior"},{"category":"Ceiling Lamp","name":"Lustres","type":"interior"},{"category":"Room Door","name":"Portes","type":"interior"},{"category":"Stairs","name":"Escaliers","type":"interior"},{"category":"Cabinet","name":"Rangements","type":"interior"},{"category":"Bookcase","name":"Bibliothèques","type":"interior"},{"category":"Table","name":"Tables","type":"interior"},{"category":"Counter","name":"Comptoirs","type":"interior"},{"category":"Bed","name":"Lits","type":"interior"},{"category":"Seating","name":"Chaises","type":"interior"},{"category":"Ornament","name":"Objets d'agrément","type":"interior"},{"category":"Carpet","name":"Tapis","type":"interior"},{"category":"Lighting","name":"Éclairages","type":"interior"},{"category":"Potted Plant","name":"Plantes en pot","type":"interior"},{"category":"Utensil","name":"Objets divers","type":"interior"},{"category":"Artwork","name":"Œuvres d'art","type":"interior"},{"category":"Hanging Ornament","name":"Éléments suspendus","type":"interior"},{"category":"Courtyard Wall","name":"Murs de cour","type":"exterior"},{"category":"Large Ornament","name":"Grands ornements","type":"exterior"},{"category":"Liyue","name":"Style liyuéen","type":"exterior"},{"category":"Mondstadt","name":"Style mondstadtois","type":"exterior"},{"category":"Hilichurl Style","name":"Style Brutocollinus","type":"exterior"},{"category":"Mercantile","name":"Étals de marchands","type":"exterior"},{"category":"Free Booth","name":"Étals libres","type":"exterior"},{"category":"Mountain","name":"Reliefs","type":"exterior"},{"category":"Rock","name":"Roches","type":"exterior"},{"category":"Tree","name":"Arbres","type":"exterior"},{"category":"Shrub","name":"Buissons","type":"exterior"},{"category":"Item","name":"Objets divers","type":"exterior"},{"category":"Terrace","name":"Végétation en pot","type":"exterior"},{"category":"Ornament","name":"Aménagements divers","type":"exterior"},{"category":"Lighting","name":"Éclairages","type":"exterior"},{"category":"Indoor Creature","name":"Animaux d'intérieur","type":"interior"},{"category":"Outdoor Creature","name":"Animaux d'extérieur","type":"exterior"},{"category":"Mansion","name":"Résidences","type":"exterior"},{"category":"Fence","name":"Clôtures","type":"exterior"},{"category":"Seating","name":"Chaises","type":"exterior"},{"category":"Table","name":"Tables","type":"exterior"},{"category":"Cabinet","name":"Rangements","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Dinding","type":"interior"},{"category":"Flooring","name":"Lantai","type":"interior"},{"category":"Ceiling","name":"Langit-Langit","type":"interior"},{"category":"Ceiling Lamp","name":"Lampu Gantung","type":"interior"},{"category":"Room Door","name":"Pintu","type":"interior"},{"category":"Stairs","name":"Tangga","type":"interior"},{"category":"Cabinet","name":"Kabinet","type":"interior"},{"category":"Bookcase","name":"Rak Buku","type":"interior"},{"category":"Table","name":"Meja","type":"interior"},{"category":"Counter","name":"Konter","type":"interior"},{"category":"Bed","name":"Ranjang","type":"interior"},{"category":"Seating","name":"Kursi","type":"interior"},{"category":"Ornament","name":"Ornamen","type":"interior"},{"category":"Carpet","name":"Karpet","type":"interior"},{"category":"Lighting","name":"Lampu","type":"interior"},{"category":"Potted Plant","name":"Tanaman Pot","type":"interior"},{"category":"Utensil","name":"Perkakas","type":"interior"},{"category":"Artwork","name":"Karya Seni","type":"interior"},{"category":"Hanging Ornament","name":"Hiasan Gantung","type":"interior"},{"category":"Courtyard Wall","name":"Dinding Halaman","type":"exterior"},{"category":"Large Ornament","name":"Ornamen Besar","type":"exterior"},{"category":"Liyue","name":"Model Liyue","type":"exterior"},{"category":"Mondstadt","name":"Model Mondstadt","type":"exterior"},{"category":"Hilichurl Style","name":"Gaya Hilichurl","type":"exterior"},{"category":"Mercantile","name":"Model Perdagangan","type":"exterior"},{"category":"Free Booth","name":"Stan Cuma-Cuma","type":"exterior"},{"category":"Mountain","name":"Gunung","type":"exterior"},{"category":"Rock","name":"Batu","type":"exterior"},{"category":"Tree","name":"Pohon","type":"exterior"},{"category":"Shrub","name":"Semak Belukar","type":"exterior"},{"category":"Item","name":"Benda","type":"exterior"},{"category":"Terrace","name":"Teras Berbunga","type":"exterior"},{"category":"Ornament","name":"Ornamen","type":"exterior"},{"category":"Lighting","name":"Lampu","type":"exterior"},{"category":"Indoor Creature","name":"Makhluk Dalam Ruangan","type":"interior"},{"category":"Outdoor Creature","name":"Makhluk Luar Ruangan","type":"exterior"},{"category":"Mansion","name":"Rumah Mewah","type":"exterior"},{"category":"Fence","name":"Pagar","type":"exterior"},{"category":"Seating","name":"Kursi","type":"exterior"},{"category":"Table","name":"Meja","type":"exterior"},{"category":"Cabinet","name":"Kabinet","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"벽지","type":"interior"},{"category":"Flooring","name":"바닥","type":"interior"},{"category":"Ceiling","name":"천장","type":"interior"},{"category":"Ceiling Lamp","name":"조명","type":"interior"},{"category":"Room Door","name":"문","type":"interior"},{"category":"Stairs","name":"계단","type":"interior"},{"category":"Cabinet","name":"수납장","type":"interior"},{"category":"Bookcase","name":"책장","type":"interior"},{"category":"Table","name":"탁자","type":"interior"},{"category":"Counter","name":"카운터","type":"interior"},{"category":"Bed","name":"침대","type":"interior"},{"category":"Seating","name":"의자","type":"interior"},{"category":"Ornament","name":"장식","type":"interior"},{"category":"Carpet","name":"카펫","type":"interior"},{"category":"Lighting","name":"조명","type":"interior"},{"category":"Potted Plant","name":"분재","type":"interior"},{"category":"Utensil","name":"기물","type":"interior"},{"category":"Artwork","name":"그림","type":"interior"},{"category":"Hanging Ornament","name":"인테리어 모빌","type":"interior"},{"category":"Courtyard Wall","name":"담장","type":"exterior"},{"category":"Large Ornament","name":"대형 장식","type":"exterior"},{"category":"Liyue","name":"리월 테마","type":"exterior"},{"category":"Mondstadt","name":"몬드 테마","type":"exterior"},{"category":"Hilichurl Style","name":"츄츄 테마","type":"exterior"},{"category":"Mercantile","name":"상가 테마","type":"exterior"},{"category":"Free Booth","name":"자유 가판대","type":"exterior"},{"category":"Mountain","name":"산","type":"exterior"},{"category":"Rock","name":"암석","type":"exterior"},{"category":"Tree","name":"교목","type":"exterior"},{"category":"Shrub","name":"관목","type":"exterior"},{"category":"Item","name":"물품","type":"exterior"},{"category":"Terrace","name":"화단","type":"exterior"},{"category":"Ornament","name":"장식","type":"exterior"},{"category":"Lighting","name":"조명","type":"exterior"},{"category":"Indoor Creature","name":"실내 동물","type":"interior"},{"category":"Outdoor Creature","name":"실외 동물","type":"exterior"},{"category":"Mansion","name":"저택","type":"exterior"},{"category":"Fence","name":"울타리","type":"exterior"},{"category":"Seating","name":"의자","type":"exterior"},{"category":"Table","name":"탁자","type":"exterior"},{"category":"Cabinet","name":"수납장","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Parede","type":"interior"},{"category":"Flooring","name":"Assoalho","type":"interior"},{"category":"Ceiling","name":"Teto","type":"interior"},{"category":"Ceiling Lamp","name":"Luminária","type":"interior"},{"category":"Room Door","name":"Portas","type":"interior"},{"category":"Stairs","name":"Escadas","type":"interior"},{"category":"Cabinet","name":"Armário","type":"interior"},{"category":"Bookcase","name":"Estante","type":"interior"},{"category":"Table","name":"Mesa","type":"interior"},{"category":"Counter","name":"Balcão","type":"interior"},{"category":"Bed","name":"Cama","type":"interior"},{"category":"Seating","name":"Cadeira","type":"interior"},{"category":"Ornament","name":"Ornamentos","type":"interior"},{"category":"Carpet","name":"Carpete","type":"interior"},{"category":"Lighting","name":"Iluminação","type":"interior"},{"category":"Potted Plant","name":"Planta em Vaso","type":"interior"},{"category":"Utensil","name":"Utensílio","type":"interior"},{"category":"Artwork","name":"Obra de Arte","type":"interior"},{"category":"Hanging Ornament","name":"Ornamento Suspenso","type":"interior"},{"category":"Courtyard Wall","name":"Muro do Pátio","type":"exterior"},{"category":"Large Ornament","name":"Ornamento Largo","type":"exterior"},{"category":"Liyue","name":"Estilo de Liyue","type":"exterior"},{"category":"Mondstadt","name":"Estilo de Mondstadt","type":"exterior"},{"category":"Hilichurl Style","name":"Estilo de Hilichurls","type":"exterior"},{"category":"Mercantile","name":"Estilo de Comercial","type":"exterior"},{"category":"Free Booth","name":"Estande Livre","type":"exterior"},{"category":"Mountain","name":"Montanha","type":"exterior"},{"category":"Rock","name":"Selo de Pedra","type":"exterior"},{"category":"Tree","name":"Arborea","type":"exterior"},{"category":"Shrub","name":"Arbusto","type":"exterior"},{"category":"Item","name":"Item","type":"exterior"},{"category":"Terrace","name":"Terraço Florido","type":"exterior"},{"category":"Ornament","name":"Ornamentos","type":"exterior"},{"category":"Lighting","name":"Iluminação","type":"exterior"},{"category":"Indoor Creature","name":"Criaturas Domésticas","type":"interior"},{"category":"Outdoor Creature","name":"Criaturas Silvestres","type":"exterior"},{"category":"Mansion","name":"Residência","type":"exterior"},{"category":"Fence","name":"Cercas","type":"exterior"},{"category":"Seating","name":"Cadeira","type":"exterior"},{"category":"Table","name":"Mesa","type":"exterior"},{"category":"Cabinet","name":"Armário","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Стены","type":"interior"},{"category":"Flooring","name":"Полы","type":"interior"},{"category":"Ceiling","name":"Потолки","type":"interior"},{"category":"Ceiling Lamp","name":"Люстры","type":"interior"},{"category":"Room Door","name":"Двери","type":"interior"},{"category":"Stairs","name":"Лестницы","type":"interior"},{"category":"Cabinet","name":"Шкафы","type":"interior"},{"category":"Bookcase","name":"Книжные полки","type":"interior"},{"category":"Table","name":"Столы","type":"interior"},{"category":"Counter","name":"Стойки","type":"interior"},{"category":"Bed","name":"Кровати","type":"interior"},{"category":"Seating","name":"Стулья","type":"interior"},{"category":"Ornament","name":"Украшения","type":"interior"},{"category":"Carpet","name":"Ковры","type":"interior"},{"category":"Lighting","name":"Освещение","type":"interior"},{"category":"Potted Plant","name":"Цветки","type":"interior"},{"category":"Utensil","name":"Утварь","type":"interior"},{"category":"Artwork","name":"Картины","type":"interior"},{"category":"Hanging Ornament","name":"Подвесные украшения","type":"interior"},{"category":"Courtyard Wall","name":"Ограды","type":"exterior"},{"category":"Large Ornament","name":"Крупные украшения","type":"exterior"},{"category":"Liyue","name":"Стиль Ли Юэ","type":"exterior"},{"category":"Mondstadt","name":"Стиль Мондштадта","type":"exterior"},{"category":"Hilichurl Style","name":"Стиль хиличурлов","type":"exterior"},{"category":"Mercantile","name":"Купеческий стиль","type":"exterior"},{"category":"Free Booth","name":"Свободный ларёк","type":"exterior"},{"category":"Mountain","name":"Скалы","type":"exterior"},{"category":"Rock","name":"Камни","type":"exterior"},{"category":"Tree","name":"Деревья","type":"exterior"},{"category":"Shrub","name":"Кусты","type":"exterior"},{"category":"Item","name":"Предметы","type":"exterior"},{"category":"Terrace","name":"Клумбы","type":"exterior"},{"category":"Ornament","name":"Украшения","type":"exterior"},{"category":"Lighting","name":"Лампы","type":"exterior"},{"category":"Indoor Creature","name":"Домашние животные","type":"interior"},{"category":"Outdoor Creature","name":"Домашние животные","type":"exterior"},{"category":"Mansion","name":"Дом","type":"exterior"},{"category":"Fence","name":"Изгороди","type":"exterior"},{"category":"Seating","name":"Стулья","type":"exterior"},{"category":"Table","name":"Столы","type":"exterior"},{"category":"Cabinet","name":"Шкафы","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"墙面","type":"interior"},{"category":"Flooring","name":"地板","type":"interior"},{"category":"Ceiling","name":"天花板","type":"interior"},{"category":"Ceiling Lamp","name":"吊灯","type":"interior"},{"category":"Room Door","name":"房门","type":"interior"},{"category":"Stairs","name":"楼梯","type":"interior"},{"category":"Cabinet","name":"柜子","type":"interior"},{"category":"Bookcase","name":"书柜","type":"interior"},{"category":"Table","name":"桌子","type":"interior"},{"category":"Counter","name":"柜台","type":"interior"},{"category":"Bed","name":"床","type":"interior"},{"category":"Seating","name":"椅子","type":"interior"},{"category":"Ornament","name":"饰品","type":"interior"},{"category":"Carpet","name":"地毯","type":"interior"},{"category":"Lighting","name":"灯具","type":"interior"},{"category":"Potted Plant","name":"盆景","type":"interior"},{"category":"Utensil","name":"器物","type":"interior"},{"category":"Artwork","name":"字画","type":"interior"},{"category":"Hanging Ornament","name":"挂饰","type":"interior"},{"category":"Courtyard Wall","name":"院墙","type":"exterior"},{"category":"Large Ornament","name":"大型饰品","type":"exterior"},{"category":"Liyue","name":"璃月风格","type":"exterior"},{"category":"Mondstadt","name":"蒙德风格","type":"exterior"},{"category":"Hilichurl Style","name":"丘丘风格","type":"exterior"},{"category":"Mercantile","name":"商户风格","type":"exterior"},{"category":"Free Booth","name":"自由摊位","type":"exterior"},{"category":"Mountain","name":"山体","type":"exterior"},{"category":"Rock","name":"岩石","type":"exterior"},{"category":"Tree","name":"乔木","type":"exterior"},{"category":"Shrub","name":"灌木","type":"exterior"},{"category":"Item","name":"物件","type":"exterior"},{"category":"Terrace","name":"花坛","type":"exterior"},{"category":"Ornament","name":"饰品","type":"exterior"},{"category":"Lighting","name":"灯具","type":"exterior"},{"category":"Indoor Creature","name":"室内动物","type":"interior"},{"category":"Outdoor Creature","name":"室外动物","type":"exterior"},{"category":"Mansion","name":"宅邸","type":"exterior"},{"category":"Fence","name":"围栏","type":"exterior"},{"category":"Seating","name":"椅子","type":"exterior"},{"category":"Table","name":"桌子","type":"exterior"},{"category":"Cabinet","name":"柜子","type":"exterior"}]

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 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 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 one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,7 @@ $locale.subscribe((value) => {
} }
}); });
const supportedLanguage = ['en', 'id', 'ru', 'ko', 'fr', 'zh', 'pt'];
addMessages('en', en); addMessages('en', en);
addMessages('id', id); addMessages('id', id);
addMessages('ru', ru); addMessages('ru', ru);
@ -34,11 +35,22 @@ addMessages('zh', zh);
addMessages('pt', pt); addMessages('pt', pt);
export function startClient() { export function startClient() {
let used = 'en';
const savedLocale = localStorage.getItem('locale'); const savedLocale = localStorage.getItem('locale');
const detectedLocale = getLocaleFromNavigator().substring(0, 2);
if (savedLocale !== null) {
if (!supportedLanguage.includes(savedLocale)) {
localStorage.setItem('locale', 'en');
} else {
used = savedLocale;
}
} else if (supportedLanguage.includes(detectedLocale)) {
used = detectedLocale;
}
init({ init({
...INIT_OPTIONS, ...INIT_OPTIONS,
initialLocale: savedLocale !== null ? savedLocale : getLocaleFromNavigator().substring(0, 2), initialLocale: used,
}); });
} }

View File

@ -65,6 +65,10 @@
"achievement": { "achievement": {
"title": "🏆 View and track your achievement list here", "title": "🏆 View and track your achievement list here",
"detail": "Achievement" "detail": "Achievement"
},
"furnishing": {
"title": "Check what furnishing you need to make to complete a sets. And also you can view the load of each furnishing.",
"detail": "Furnishing"
} }
}, },
"characters": { "characters": {
@ -604,7 +608,24 @@
"hall": "Hall", "hall": "Hall",
"room": "Room {number}", "room": "Room {number}",
"exteriorNum": "Area {number}", "exteriorNum": "Area {number}",
"corridor": "Corridor" "corridor": "Corridor",
"inventoryButton": "Inventory",
"listButton": "List",
"inventory": {
"title": "Furnishing Inventory",
"subtitle": "Some special categories like wall are not shown",
"all": "All",
"openSets": "Open Sets"
},
"sets": {
"title": "Furnishing Sets",
"subtitle": "Click the furnishing icon for detail, and you can click the character icon to mark them! Open the Inventory menu above to fill your furnishing items count.",
"setPlaced": "Set as placed",
"setUnplaced": "Set as unplaced",
"inInventory": "In inventory",
"available": "Available",
"used": "Used in other set"
}
}, },
"weapon": { "weapon": {
"title": "Weapon List", "title": "Weapon List",

View File

@ -60,6 +60,10 @@
"twitter": { "twitter": {
"title": "Follow my Twitter, akan post tentang apa yang lagi di develop dan update terbaru tentang paimon.moe!", "title": "Follow my Twitter, akan post tentang apa yang lagi di develop dan update terbaru tentang paimon.moe!",
"detail": "Follow Twitter" "detail": "Follow Twitter"
},
"furnishing": {
"title": "Cek furnitur apa saja yang kamu perlukan untuk menyelesaikan suatu set. Dan kamu juga bisa melihat beban masing-masing furnitur.",
"detail": "Furnitur"
} }
}, },
"characters": { "characters": {
@ -568,6 +572,27 @@
"info": [ "info": [
"Ini menunjukkan berapa beban yang bisa ditampung dalam pulau. Masing-masing furnitur mempunyai nilai beban yang tersembunyi yang bisa dilihat di bawah.", "Ini menunjukkan berapa beban yang bisa ditampung dalam pulau. Masing-masing furnitur mempunyai nilai beban yang tersembunyi yang bisa dilihat di bawah.",
"(Beban maximum belum dikonfirmasi!)" "(Beban maximum belum dikonfirmasi!)"
] ],
"hall": "Aula Utama",
"room": "Kamar {number}",
"exteriorNum": "Area {number}",
"corridor": "Koridor",
"inventoryButton": "Inventory",
"listButton": "List",
"inventory": {
"title": "Furnishing Inventory",
"subtitle": "Beberapa kategori special seperti dinding tidak ditampilkan",
"all": "Semua",
"openSets": "Buka Set"
},
"sets": {
"title": "Furnishing Set",
"subtitle": "Klik icon furnitur untuk detail, dan kamu bisa klik icon karakter untuk menandai mereka! Buka menu inventory diatas untuk mengisi jumlah furnitur yang kamu punya.",
"setPlaced": "Set ditempatkan",
"setUnplaced": "Set belum ditempatkan",
"inInventory": "Di inventory",
"available": "Tersedia",
"used": "Digunakan di set lain"
}
} }
} }

View File

@ -16,7 +16,7 @@
}, },
"home": { "home": {
"welcome": "Boas-vindas ao Paimon.moe! 👋", "welcome": "Boas-vindas ao Paimon.moe! 👋",
"message": "Seu melhor companheiro de Genshin Impact! Planeje seu farm com a calculadora de ascenção e acompanhe seu progresso com a lista de afazeres e o histórico de orações.", "message": "Seu melhor companheiro de Genshin Impact! Planeje seu farm com a calculadora de ascensão e acompanhe seu progresso com a lista de afazeres e o histórico de orações.",
"banner": { "banner": {
"featured": [ "featured": [
"Eula" "Eula"
@ -51,12 +51,12 @@
"join": "Junte-se ao Discord" "join": "Junte-se ao Discord"
}, },
"items": { "items": {
"title": "Farmáveis Hoje", "title": "Farmaveis Hoje",
"detail": "Itens", "detail": "Itens",
"sunday": "Todos os itens podem ser farmados aos domingos 😁" "sunday": "Todos os itens podem ser farmados aos domingos 😁"
}, },
"calculator": { "calculator": {
"title": "🧮 Calcule materiais de ascenção de Personagens, Armas e elevação de talento! Todos os cálculos podem ser adicionados na Lista de Afazeres. Também mostra quantas resinas você irá precisar!", "title": "🧮 Calcule materiais de ascensão de Personagens, Armas e elevação de talento! Todos os cálculos podem ser adicionados na Lista de Afazeres. Também mostra quantas resinas você irá precisar!",
"detail": "Calculadora" "detail": "Calculadora"
}, },
"twitter": { "twitter": {
@ -70,7 +70,7 @@
}, },
"characters": { "characters": {
"title": "Personagens", "title": "Personagens",
"subtitle": "Os números dos status são do Nível 80, Ascenção 6. Você pode clicar nos índices da tabela para ordenar!", "subtitle": "Os números dos status são do Nível 80, Ascensão 6. Você pode clicar nos índices da tabela para ordenar!",
"name": "Nome", "name": "Nome",
"element": "Elemento", "element": "Elemento",
"rarity": "Raridade", "rarity": "Raridade",
@ -115,7 +115,7 @@
"welcomeStart2": "acima", "welcomeStart2": "acima",
"manual": "Se você quiser informar os dados manualmente, você pode fazer isso aqui:", "manual": "Se você quiser informar os dados manualmente, você pode fazer isso aqui:",
"manualButton": "Habilitar Inserção Manual", "manualButton": "Habilitar Inserção Manual",
"errorBanner": "Incompatibilidade no tempo do banner! Ajuste seu servidor na página de configurações. Ainda não está funcionando? Por favor, deixa uma mensagem no Discord 😅", "errorBanner": "Incompatibilidade no tempo do banner! Ajuste seu servidor na página de configurações. Ainda não está funcionando? Por favor, deixe uma mensagem no Discord 😅",
"globalWishTally": "Contagem Global de Orações", "globalWishTally": "Contagem Global de Orações",
"pityTooltip": [ "pityTooltip": [
"Informa seu pity atual para {rarity}", "Informa seu pity atual para {rarity}",
@ -179,7 +179,7 @@
"Se você não deseja compartilhar sua URL, você pode utilizar um pequeno script de importação para salvar seu histórico de orações em seu PC (opção para PC Local)" "Se você não deseja compartilhar sua URL, você pode utilizar um pequeno script de importação para salvar seu histórico de orações em seu PC (opção para PC Local)"
], ],
"q6": "Eu fiz todos os procedimentos, mas tive um erro na API?", "q6": "Eu fiz todos os procedimentos, mas tive um erro na API?",
"a6": "Certifique-se de copiar todo o texto (segura e pressione Selecionar Tudo para dispositivos móveis); talvez você tenha esquecido alguns caracteres que não necessários para o funcionamento da importação.", "a6": "Certifique-se de copiar todo o texto (segure e pressione Selecionar Tudo para dispositivos móveis); talvez você tenha esquecido alguns caracteres que não necessários para o funcionamento da importação.",
"q7": "Não leu todo meu histórico!?", "q7": "Não leu todo meu histórico!?",
"a7": "Histórico de orações mais antigos que 6 meses são deletados dos servidores da MiHoYo, então se você não possui um backup em algum lugar, infelizmente se foi" "a7": "Histórico de orações mais antigos que 6 meses são deletados dos servidores da MiHoYo, então se você não possui um backup em algum lugar, infelizmente se foi"
}, },
@ -225,7 +225,7 @@
"pclocal": [ "pclocal": [
"Se você não se sente confortável em compartilhar sua URL de feedback, você pode usar esta opção para processar seu histórico de orações em seu PC localmente. Este script irá ler o log em seu PC para gerar a URL do histórico de orações.", "Se você não se sente confortável em compartilhar sua URL de feedback, você pode usar esta opção para processar seu histórico de orações em seu PC localmente. Este script irá ler o log em seu PC para gerar a URL do histórico de orações.",
"Abra o menu Iniciar, e então procure por Powershell", "Abra o menu Iniciar, e então procure por Powershell",
"Abra o Powershell do Widows, e então copie & cole o script abaixo no Powershell", "Abra o Powershell do Windows, e então copie & cole o script abaixo no Powershell",
"iex ((New-Object System.Net.WebClient).DownloadString('https://gist.githubusercontent.com/MadeBaruna/9ff8b7a2af11f3002395af7963b5ed18/raw/cdfead30f830b897e8822a40f98fea5340dbd62e/importer.ps1'))", "iex ((New-Object System.Net.WebClient).DownloadString('https://gist.githubusercontent.com/MadeBaruna/9ff8b7a2af11f3002395af7963b5ed18/raw/cdfead30f830b897e8822a40f98fea5340dbd62e/importer.ps1'))",
"Você pode revisar o script", "Você pode revisar o script",
"aqui", "aqui",
@ -244,7 +244,7 @@
"export": "Exportar para Excel", "export": "Exportar para Excel",
"exporting": "Exportando...", "exporting": "Exportando...",
"import": "Importar", "import": "Importar",
"exportFinish": "Exportado com successo, aguarde até que o navegador realize o download do arquivo!", "exportFinish": "Exportado com sucesso, aguarde até que o navegador realize o download do arquivo!",
"wishTallyTitle": "Enviar Contagem de Orações", "wishTallyTitle": "Enviar Contagem de Orações",
"wishTally": "Estamos fazendo uma contagem global de orações! Você pode enviar sua contagem de orações para participar. Todas as informações do pity serão agregadas para saber qual a média de pity dos usuários do paimon.moe.", "wishTally": "Estamos fazendo uma contagem global de orações! Você pode enviar sua contagem de orações para participar. Todas as informações do pity serão agregadas para saber qual a média de pity dos usuários do paimon.moe.",
"wishTallyCollected": [ "wishTallyCollected": [
@ -280,7 +280,7 @@
"para editar os valores manualmente!" "para editar os valores manualmente!"
], ],
"p6": [ "p6": [
"Pressione a seta embaixo para visualizar os detalhes das orações. Um pop-up irá aparecer quando você conseguir um item", "Pressione a seta abaixo para visualizar os detalhes das orações. Um pop-up irá aparecer quando você conseguir um item",
"ou", "ou",
"Você também pode adicionar ou editar a tabela manualmente." "Você também pode adicionar ou editar a tabela manualmente."
] ]
@ -342,7 +342,7 @@
"howToCharacter": "Como usar a Calculadora de Personagens" "howToCharacter": "Como usar a Calculadora de Personagens"
}, },
"weapon": { "weapon": {
"calculateAscension": "Calcular Materiais de Ascenção?", "calculateAscension": "Calcular Materiais de Ascensão?",
"selectRarity": "Selecione a raridade da arma", "selectRarity": "Selecione a raridade da arma",
"selectWeapon": "Selecione a arma", "selectWeapon": "Selecione a arma",
"current": "Nível, EXP, & Ascenção Atual da Arma", "current": "Nível, EXP, & Ascenção Atual da Arma",
@ -353,14 +353,14 @@
"resource": "Recursos para Usar", "resource": "Recursos para Usar",
"calculate": "Calcular", "calculate": "Calcular",
"unknownInformation": "Há alguma informação desconhecida", "unknownInformation": "Há alguma informação desconhecida",
"ascensionLevel": "Nível de ascenção", "ascensionLevel": "Nível de ascensão",
"mora": "Mora (aproximadamente ±40)", "mora": "Mora (aproximadamente ±40)",
"expWasted": "EXP Desperdiçada", "expWasted": "EXP Desperdiçada",
"addToTodo": "Adicionar na Lista de Afazeres", "addToTodo": "Adicionar na Lista de Afazeres",
"addedToTodo": "Adicionado na Lista de Afazeres" "addedToTodo": "Adicionado na Lista de Afazeres"
}, },
"character": { "character": {
"calculateAscension": "Calcular Materiais de Ascenção?", "calculateAscension": "Calcular Materiais de Ascensão?",
"selectCharacter": "Selecione o personagem", "selectCharacter": "Selecione o personagem",
"current": "Nível, EXP, & Ascenção Atual do Personagem", "current": "Nível, EXP, & Ascenção Atual do Personagem",
"inputCurrentLevel": "Informe o nível atual do personagem...", "inputCurrentLevel": "Informe o nível atual do personagem...",
@ -379,7 +379,7 @@
"talentToLevel": "para o nível", "talentToLevel": "para o nível",
"calculate": "Calcular", "calculate": "Calcular",
"unknownInformation": "Há alguma informação desconhecida", "unknownInformation": "Há alguma informação desconhecida",
"ascensionLevel": "Nível de ascenção", "ascensionLevel": "Nível de ascensão",
"mora": "Mora (aproximadamente ±40)", "mora": "Mora (aproximadamente ±40)",
"expWasted": "EXP Desperdiçada", "expWasted": "EXP Desperdiçada",
"addToTodo": "Adicionar na Lista de Afazeres", "addToTodo": "Adicionar na Lista de Afazeres",
@ -440,12 +440,12 @@
"summary": "Sumário", "summary": "Sumário",
"empty": [ "empty": [
"Nada para fazer ainda 😀", "Nada para fazer ainda 😀",
"Adicione algumas coisas a partir da Cálculadora ou da página de Itens!" "Adicione algumas coisas a partir da Calculadora ou da página de Itens!"
], ],
"farmableToday": "Farmáveis hoje", "farmableToday": "Farmáveis hoje",
"resin": "Resinas necessárias", "resin": "Resinas necessárias",
"based": "Baseado no AR:{ar} e WL:{wl}", "based": "Baseado no AR:{ar} e WL:{wl}",
"change": "(altera nas configurações)", "change": "(alterar nas configurações)",
"approximation": "Aproximação calculada a partir da taxa de drops por", "approximation": "Aproximação calculada a partir da taxa de drops por",
"delete": { "delete": {
"title": "Deletar esse afazer?", "title": "Deletar esse afazer?",
@ -549,7 +549,7 @@
"donate": { "donate": {
"message": [ "message": [
"Muito obrigado! Espero que ache isso útil.", "Muito obrigado! Espero que ache isso útil.",
"Fiz este site para me divertir quando tenha um tempo livre. Obviamente irei adicionar mais recursos e atualizarei os dados a cada nova atualização, pois gosto de explorar o jogo e tentar maximizar minha experiência", "Fiz este site para me divertir quando tenho um tempo livre. Obviamente irei adicionar mais recursos e atualizarei os dados a cada nova atualização, pois gosto de explorar o jogo e tentar maximizar minha experiência",
"Se você quiser doar, sua doação definitivamente irá ajudar a melhorar o site e me motivar a adicionar mais conteúdo para Paimon.moe! Muito obrigado e divirta-se, espero que goste!", "Se você quiser doar, sua doação definitivamente irá ajudar a melhorar o site e me motivar a adicionar mais conteúdo para Paimon.moe! Muito obrigado e divirta-se, espero que goste!",
"Se você gosta de codificar, você também pode contribuir para o projeto em" "Se você gosta de codificar, você também pode contribuir para o projeto em"
], ],

View File

@ -11,7 +11,7 @@
"todoList": "Список дел", "todoList": "Список дел",
"timeline": "Лента событий", "timeline": "Лента событий",
"furnishing": "Мебель", "furnishing": "Мебель",
"weapons": "Оружие", "weapons": "Оружие",
"settings": "Настройки", "settings": "Настройки",
"donate": "Поддержать" "donate": "Поддержать"
}, },
@ -390,8 +390,8 @@
"mora": "Мора (приблизительно ±40)", "mora": "Мора (приблизительно ±40)",
"expWasted": "EXP потеряно", "expWasted": "EXP потеряно",
"addToTodo": "Добавить в список дел", "addToTodo": "Добавить в список дел",
"addedToTodo": "Добавлено в список дел" "addedToTodo": "Добавлено в список дел",
"talent": ["Обычн.Атака", "Элем.Навык", "Взрыв Стихии"] "talent": ["Обычн.Атака", "Элем.Навык", "Взрыв Стихии"]
}, },
"expTable": { "expTable": {
"level": "Уровень", "level": "Уровень",

View File

@ -0,0 +1,18 @@
<script>
import { mdiChevronRight } from '@mdi/js';
import { t } from 'svelte-i18n';
import Icon from '../../components/Icon.svelte';
</script>
<div class="bg-item rounded-xl p-4 flex flex-col">
<p class="text-white">{$t('home.furnishing.title')}</p>
<a
href="/furnishing"
class="flex justify-end items-center self-end lg:self-start text-white mt-4
bg-background-secondary rounded-xl py-2 px-4 hover:bg-background transition-colors duration-100"
>
{$t('home.furnishing.detail')}
<Icon path={mdiChevronRight} />
</a>
</div>

View File

@ -1,161 +1,128 @@
<script context="module"> <script context="module">
import setsData from '../../data/furnishing/sets/en.json';
import data from '../../data/furnishing/en.json'; import data from '../../data/furnishing/en.json';
export async function preload() { export async function preload() {
return { data }; return { data, setsData };
} }
</script> </script>
<script> <script>
import { onMount } from 'svelte';
import { locale, t } from 'svelte-i18n'; import { locale, t } from 'svelte-i18n';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { mdiInformationOutline, mdiMinus, mdiPlus } from '@mdi/js';
import TableHeader from '../../components/Table/TableHeader.svelte'; import Button from '../../components/Button.svelte';
import CharacterSelect from '../../components/CharacterSelect.svelte';
import { onMount } from 'svelte';
import Icon from '../../components/Icon.svelte'; import Icon from '../../components/Icon.svelte';
import { mdiCheckCircleOutline, mdiClose } from '@mdi/js';
import { getAccountPrefix } from '../../stores/account'; import { getAccountPrefix } from '../../stores/account';
import { readSave, updateSave } from '../../stores/saveManager'; import { readSave, updateSave } from '../../stores/saveManager';
export let data; export let data;
export let setsData;
let type = 'hall'; let loading = true;
let items = []; let furnishing = {};
let max = 0; let sets = [];
let saved = {};
let _saved = {};
let used = {};
let placed = {};
let character = {};
let charFilter = null;
const minEnergy = { function parseSets() {
exterior: 0, const _sets = [];
exterior2: 0, furnishing = data;
exterior3: 0, used = {};
exterior4: 0, saved = { ..._saved };
hall: 150, for (const set of setsData) {
room1: 150, for (const [item, amount] of Object.entries(set.items)) {
room2: 150, if (placed[set.id] === true) {
room3: 150, if (used[item] === undefined) {
corridor: 150, used[item] = 0;
}; }
const maxLoad = {
exterior: 10000,
exterior2: 10000,
exterior3: 10000,
exterior4: 10000,
hall: 10000,
room1: 4000,
room2: 4000,
room3: 4000,
corridor: 4000,
};
let currentUsage = {
exterior: {},
exterior2: {},
exterior3: {},
exterior4: {},
hall: {},
room1: {},
room2: {},
room3: {},
corridor: {},
};
$: currentLoad = Object.entries(currentUsage[type]).reduce(
(prev, [id, val]) => {
prev.load += data[id].load * val;
prev.energy += data[id].energy * val;
return prev;
},
{
load: 0,
energy: minEnergy[type],
},
);
let sortBy = 'ratio'; used[item] += amount;
let sortOrder = false; saved[item] -= amount;
async function parseFurnishing() {
const currentType = type.startsWith('exterior') ? 'exterior' : 'interior';
items = Object.values(data)
.filter((e) => e.type === currentType || e.type === '')
.sort((a, b) => {
switch (sortBy) {
case 'ratio':
if (sortOrder) return a.ratio - b.ratio;
else return b.ratio - a.ratio;
case 'energy':
if (sortOrder) return a.energy - b.energy;
else return b.energy - a.energy;
case 'load':
if (sortOrder) return a.load - b.load;
else return b.load - a.load;
case 'using':
if (sortOrder) return (currentUsage[type][a.id] || 0) - (currentUsage[type][b.id] || 0);
else return (currentUsage[type][b.id] || 0) - (currentUsage[type][a.id] || 0);
case 'name':
if (sortOrder) return a.name.localeCompare(b.name);
else return b.name.localeCompare(a.name);
} }
}); }
}
function sort(by) {
if (sortBy === by) {
sortOrder = !sortOrder;
} else {
sortBy = by;
} }
parseFurnishing();
for (const set of setsData.sort((a, b) => b.gift - a.gift)) {
set.enough = {};
set.canBePlaced = true;
for (const [item, amount] of Object.entries(set.items)) {
let enough = true;
if (placed[set.id] !== true) {
enough = saved[item] >= amount;
}
set.enough[item] = enough;
if (!enough) set.canBePlaced = false;
}
_sets.push(set);
}
sets = _sets;
console.log(sets);
loading = false;
} }
function changeType(_type) { function place(id) {
type = _type; placed[id] = !placed[id];
sortBy = 'ratio';
sortOrder = false;
parseFurnishing();
max = items[0].ratio;
}
function changeUsage(id, val) {
if (currentUsage[type][id] === undefined) currentUsage[type][id] = 0;
currentUsage[type][id] = Math.max(0, currentUsage[type][id] + val);
if (currentUsage[type][id] === 0) delete currentUsage[type][id];
saveData(); saveData();
parseSets();
}
function checkCharacter(setId, id) {
if (character[setId] === undefined) {
character[setId] = {};
}
character[setId][id] = !character[setId][id];
saveData();
}
async function readLocalData() {
const prefix = getAccountPrefix();
const savedIventory = await readSave(`${prefix}furnishing-inventory`);
const savedSet = await readSave(`${prefix}furnishing-set-placed`);
const savedSetCharacters = await readSave(`${prefix}furnishing-set-character`);
if (savedIventory !== null) {
_saved = { ...savedIventory };
saved = { ...savedIventory };
}
if (savedSet !== null) {
placed = savedSet;
}
if (savedSetCharacters !== null) {
character = savedSetCharacters;
}
} }
async function changeLocale(locale) { async function changeLocale(locale) {
data = (await import(`../../data/furnishing/${locale}.json`)).default; data = (await import(`../../data/furnishing/${locale}.json`)).default;
parseFurnishing(); setsData = (await import(`../../data/furnishing/sets/${locale}.json`)).default;
} parseSets();
function calculateColor(percentage) {
const hue = (percentage / max) * 120;
return `color: hsl(${hue}, 100%, 60%);`;
}
function calculateColorLoad(percentage) {
const hue = Math.max((1 - percentage) * 120, 0);
return `color: hsl(${hue}, 100%, 60%);`;
} }
const saveData = debounce(async () => { const saveData = debounce(async () => {
const data = currentUsage;
const prefix = getAccountPrefix(); const prefix = getAccountPrefix();
await updateSave(`${prefix}furnishing`, data); await updateSave(`${prefix}furnishing-set-placed`, placed);
}, 2000); await updateSave(`${prefix}furnishing-set-character`, character);
}, 1000);
async function readLocalData() { function onCharFilterChanged() {
const prefix = getAccountPrefix(); if (!loading) {
const furnishingData = await readSave(`${prefix}furnishing`); parseSets();
if (furnishingData !== null) {
currentUsage = {
...currentUsage,
...furnishingData,
};
} }
} }
onMount(async () => { onMount(async () => {
parseFurnishing();
max = items[0].ratio;
await readLocalData(); await readLocalData();
locale.subscribe((val) => { locale.subscribe((val) => {
@ -163,203 +130,127 @@
}); });
}); });
// $: charFilter, onCharFilterChanged();
</script> </script>
<svelte:head> <svelte:head>
<title>Furnishing - Paimon.moe</title> <title>Furnishing Sets - Paimon.moe</title>
<meta name="description" content="Genshin Impact Furnishing list with the load and energy values" /> <meta name="description" content="Genshin Impact Furnishing list with the load and energy values" />
<meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" /> <meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" />
</svelte:head> </svelte:head>
<div class="lg:ml-64 pt-20 lg:px-8 lg:pt-8 max-w-screen-xl"> <div class="lg:ml-64 pt-20 px-4 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="px-4 flex md:space-x-2 items-start md:items-center flex-col md:flex-row"> <div class="flex flex-col md:flex-row items-center md:space-x-2 space-y-2 md:space-y-0">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.title')}</h1> <h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.sets.title')}</h1>
<div class="flex items-center space-x-2"> <div>
<div <a href="/furnishing/inventory">
class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2 group relative" <Button>{$t('furnishing.inventoryButton')}</Button>
style={calculateColorLoad(currentLoad.load / maxLoad[type])} </a>
> <a href="/furnishing/list">
<Icon size={0.7} path={mdiInformationOutline} /> <Button>{$t('furnishing.listButton')}</Button>
{$t('furnishing.load')} </a>
{currentLoad.load} / {maxLoad[type]}
<div
class="hidden group-hover:block absolute left-0 transform translate-y-full
bg-white rounded-xl z-50 text-gray-900 px-4 py-2 w-screen max-w-xs md:max-w-sm"
style="bottom: -10px;"
>
<p>{$t('furnishing.info.0')}</p>
<p>{$t('furnishing.info.1')}</p>
</div>
</div>
<div class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2">
{$t('furnishing.energy')}
{currentLoad.energy}
</div>
</div> </div>
</div> </div>
<div class="px-4 flex space-x-2 mt-2 mb-2"> {#if loading}
<button on:click={() => changeType('hall')} class="pill {type.startsWith('exterior') ? '' : 'active'}"> <p class="text-white">Loading...</p>
{$t('furnishing.interior')}
</button>
<button on:click={() => changeType('exterior')} class="pill {type.startsWith('exterior') ? 'active' : ''}">
{$t('furnishing.exterior')}
</button>
</div>
{#if type.startsWith('exterior')}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('exterior')} class="pill {type === 'exterior' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('exterior2')} class="pill {type === 'exterior2' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('exterior3')} class="pill {type === 'exterior3' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('exterior4')} class="pill {type === 'exterior4' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 4 } })}
</button>
</div>
{:else} {:else}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto"> <p class="text-gray-400 font-medium pb-2">
<button on:click={() => changeType('hall')} class="pill {type === 'hall' ? 'active' : ''}"> {$t('furnishing.sets.subtitle')}
{$t('furnishing.hall')} </p>
</button> <div class="w-full md:w-64">
<button on:click={() => changeType('room1')} class="pill {type === 'room1' ? 'active' : ''}"> <CharacterSelect bind:selected={charFilter} />
{$t('furnishing.room', { values: { number: 1 } })} </div>
</button> <div
<button on:click={() => changeType('room2')} class="pill {type === 'room2' ? 'active' : ''}"> class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-4 gap-2 flex-1 mt-2"
{$t('furnishing.room', { values: { number: 2 } })} style="height: fit-content;"
</button> >
<button on:click={() => changeType('room3')} class="pill {type === 'room3' ? 'active' : ''}"> {#each sets as set (set.id)}
{$t('furnishing.room', { values: { number: 3 } })} {#if charFilter === null || (charFilter !== null && set.characters && set.characters.includes(charFilter.id))}
</button> <div class="text-white p-2 rounded-xl flex flex-col bg-item">
<button on:click={() => changeType('corridor')} class="pill {type === 'corridor' ? 'active' : ''}"> <div class="w-full flex items-center justify-center relative">
{$t('furnishing.corridor')} <img
</button> src="/images/furnishing/sets/{set.id}.png"
alt=""
class="w-full image relative object-contain rounded-xl"
/>
{#if placed[set.id]}
<div class="text-green-300 absolute bottom-0 right-0 p-2">
<Icon path={mdiCheckCircleOutline} size={1.5} />
</div>
{/if}
</div>
<div class="mt-1 flex flex-col h-full">
<p class="text-white mb-1">{set.name}</p>
{#if set.gift}
<div class="flex -m-1 py-1">
{#each set.characters as char}
<div class="relative cursor-pointer" on:click={() => checkCharacter(set.id, char)}>
<img src="/images/characters/{char}.png" class="w-10 h-10 rounded-full m-1" alt={char} />
{#if character[set.id]?.[char]}
<div class="text-green-300 absolute bottom-0 right-0">
<Icon path={mdiCheckCircleOutline} size={1} />
</div>
{/if}
</div>
{/each}
</div>
{/if}
<div class="flex -m-1 flex-wrap pt-1">
{#each Object.entries(set.items) as [item, amount]}
<button
class="rounded-xl {set.enough[item] === true
? 'bg-background'
: 'bg-red-900'} text-white px-2 furnishing-item focus:outline-none filter"
style="margin: 2px;"
>
<div class="flex items-center">
<img src="/images/furnishing/{item}.png" class="w-8 h-8" alt={item} />
<Icon path={mdiClose} size={0.5} />
<span class="inline-block w-2">{amount}</span>
</div>
<div class="popup text-left">
<p class="text-left mb-1">{furnishing[item].name}</p>
<table>
<tr>
<td>{$t('furnishing.sets.inInventory')}</td>
<td class="pl-2 text-center">{_saved[item] || 0}</td>
</tr>
<tr>
<td>{$t('furnishing.sets.used')}</td>
<td class="pl-2 text-center">{used[item] || 0}</td>
</tr>
<tr>
<td>{$t('furnishing.sets.available')}</td>
<td class="pl-2 text-center">{saved[item] || 0}</td>
</tr>
</table>
</div>
</button>
{/each}
</div>
<div class="flex-1" />
<Button className="mt-2" disabled={!set.canBePlaced} on:click={() => place(set.id)}>
{placed[set.id] ? $t('furnishing.sets.setUnplaced') : $t('furnishing.sets.setPlaced')}
</Button>
</div>
</div>
{/if}
{/each}
</div> </div>
{/if} {/if}
<div class="flex mt-4 wrapper">
<div class="block overflow-x-auto xl:overflow-x-visible whitespace-no-wrap">
<div class="px-4 table">
<table class="w-full block pl-4 pr-4 py-2 md:pl-8 md:py-4 bg-item rounded-xl">
<tr>
<th />
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('name')}
sort={sortBy === 'name'}
order={sortOrder}
align="left"
>
{$t('furnishing.name')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('energy')}
sort={sortBy === 'energy'}
order={sortOrder}
align="center"
>
{$t('furnishing.energy')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('load')}
sort={sortBy === 'load'}
order={sortOrder}
align="center"
>
{$t('furnishing.load')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('ratio')}
sort={sortBy === 'ratio'}
order={sortOrder}
align="center"
>
{$t('furnishing.ratio')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('using')}
sort={sortBy === 'using'}
order={sortOrder}
align="center"
>
{$t('furnishing.using')}
</TableHeader>
</tr>
{#each items as item (item.id)}
<tr>
<td class="pr-4 h-12">
<img
src="/images/furnishing/{item.id}.png"
alt=""
class="h-12 w-12 image relative"
style="min-width: 3rem;"
/>
</td>
<td class="px-4 text-gray-200">{item.name}</td>
<td class="px-4 text-gray-200 text-center">{item.energy}</td>
<td class="px-4 text-gray-200 text-center">{item.load}</td>
<td class="px-4 text-gray-200 text-center" style={calculateColor(item.ratio)}>{item.ratio.toFixed(2)}</td>
<td class="px-4">
<div
class="flex justify-between items-center border-2 border-white border-opacity-25 rounded-xl text-gray-400"
>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, 1)}>
<Icon path={mdiPlus} />
</button>
<p
class="h-full px-2 text-center {currentUsage[type][item.id] > 0
? 'text-gray-200'
: 'text-gray-700'}"
style="min-width: 40px;"
>
{currentUsage[type][item.id] || 0}
</p>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, -1)}>
<Icon path={mdiMinus} />
</button>
</div>
</td>
</tr>
{/each}
</table>
</div>
</div>
</div>
</div> </div>
<style> <style>
.pill { .popup {
@apply rounded-2xl; @apply text-sm pt-1 hidden p-2 rounded-xl;
@apply border-2;
@apply border-white;
@apply border-opacity-25;
@apply text-white;
@apply px-4;
@apply py-1;
@apply outline-none;
@apply transition;
@apply duration-100;
@apply whitespace-no-wrap;
&:hover {
@apply border-primary;
}
&.active {
@apply bg-primary;
@apply border-primary;
@apply text-background;
}
} }
.image[alt]:after { .furnishing-item:focus {
@apply block absolute top-0 left-0 w-full h-full bg-item; @apply w-full;
content: '';
.popup {
@apply block;
}
} }
</style> </style>

View File

@ -0,0 +1,266 @@
<script context="module">
import data from '../../data/furnishing/en.json';
import categories from '../../data/furnishing/category/en.json';
export async function preload() {
return { data, categories };
}
</script>
<script>
import { locale, t } from 'svelte-i18n';
import { onMount, tick } from 'svelte';
import { mdiMinus, mdiPlus } from '@mdi/js';
import debounce from 'lodash/debounce';
import Icon from '../../components/Icon.svelte';
import { readSave, updateSave } from '../../stores/saveManager';
import { getAccountPrefix } from '../../stores/account';
import Button from '../../components/Button.svelte';
export let data;
export let categories;
let loading = true;
let active = {
name: 'all',
type: 'all',
};
let inventoryContainer;
let categoryList = [];
let categoryData = {
'all-all': {
items: [],
count: {},
},
};
let saved = {};
async function parseFurnishing() {
categoryList = categories;
categoryData = {
'all-all': {
items: [],
count: {},
},
};
for (const category of categories) {
const categoryId = `${category.category}-${category.type}`;
categoryData[categoryId] = {
items: [],
count: {},
};
}
for (const item of Object.values(data)) {
if (item.category.category === 'Companion') continue;
if (item.category.type !== '') {
const categoryId = `${item.category.category}-${item.category.type}`;
categoryData[categoryId].items.push(item);
categoryData['all-all'].items.push(item);
if (saved[item.id] && saved[item.id] > 0) {
categoryData[categoryId].count[item.id] = true;
categoryData['all-all'].count[item.id] = true;
}
} else {
const categoryIdEx = `${item.category.category}-exterior`;
const categoryIdIn = `${item.category.category}-interior`;
categoryData[categoryIdEx]?.items.push(item);
categoryData[categoryIdIn]?.items.push(item);
categoryData['all-all'].items.push(item);
if (saved[item.id] && saved[item.id] > 0) {
if (categoryData[categoryIdEx]) categoryData[categoryIdEx].count[item.id] = true;
if (categoryData[categoryIdIn]) categoryData[categoryIdIn].count[item.id] = true;
categoryData['all-all'].count[item.id] = true;
}
}
}
loading = false;
}
async function changeCategory(category) {
active = category;
await tick();
inventoryContainer.scrollIntoView({
behavior: 'smooth',
});
}
async function readLocalData() {
const prefix = getAccountPrefix();
const savedIventory = await readSave(`${prefix}furnishing-inventory`);
if (savedIventory !== null) {
saved = savedIventory;
}
}
const saveData = debounce(async () => {
const prefix = getAccountPrefix();
await updateSave(`${prefix}furnishing-inventory`, saved);
}, 1000);
function changeItemValue(id, val) {
saved[id] = Math.max(0, val);
if (saved[id] === 0) {
delete categoryData[`${active.name}-${active.type}`].count[id];
categoryData = categoryData;
} else {
categoryData[`${active.name}-${active.type}`].count[id] = true;
}
saveData();
}
async function changeLocale(locale) {
categories = (await import(`../../data/furnishing/category/${locale}.json`)).default;
data = (await import(`../../data/furnishing/${locale}.json`)).default;
parseFurnishing();
}
onMount(async () => {
await readLocalData();
locale.subscribe((val) => {
changeLocale(val);
});
});
</script>
<svelte:head>
<title>Furnishing Inventory - Paimon.moe</title>
<meta name="description" content="Genshin Impact Furnishing list with the load and energy values" />
<meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" />
</svelte:head>
<div class="lg:ml-64 pt-20 px-4 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="flex flex-col md:flex-row items-center md:space-x-2 space-y-2 md:space-y-0">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.inventory.title')}</h1>
<a href="/furnishing">
<Button>{$t('furnishing.inventory.openSets')}</Button>
</a>
</div>
{#if loading}
<p class="text-white">Loading...</p>
{:else}
<p class="text-gray-400 font-medium pb-2">
{$t('furnishing.inventory.subtitle')}
</p>
<div class="flex flex-col lg:flex-row space-y-3 lg:space-y-0 lg:space-x-3 mt-4 md:mt-0">
<div class="flex flex-col space-y-2 lg:h-screen lg:overflow-auto lg:sticky lg:pr-1 pb-4 category">
<div
on:click={() => changeCategory({ name: 'all', type: 'all' })}
class="rounded-xl p-2 cursor-pointer flex text-white {active.name === 'all' ? 'bg-primary' : 'bg-item'}"
>
<p class="flex-1">{$t('furnishing.inventory.all')}</p>
<!-- <p class={Object.keys(categoryData['all-all'].count).length === 0 ? 'text-gray-600' : 'text-white'}>
{Object.keys(categoryData['all-all'].count).length}
</p> -->
</div>
{#each categoryList as category (`${category.category}-${category.type}`)}
{#if categoryData[`${category.category}-${category.type}`].items.length > 0}
<div
on:click={() => changeCategory({ name: category.category, type: category.type })}
class="rounded-xl p-2 cursor-pointer flex text-white {category.category === active.name &&
category.type === active.type
? 'bg-primary'
: 'bg-item'}"
>
<p class="flex-1">{category.name}</p>
<p
class={Object.keys(categoryData[`${category.category}-${category.type}`].count).length === 0
? 'text-gray-600'
: 'text-white'}
>
{Object.keys(categoryData[`${category.category}-${category.type}`].count).length}
</p>
</div>
{/if}
{/each}
</div>
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-4 gap-2 flex-1 pt-20 lg:pt-2"
style="height: max-content;"
bind:this={inventoryContainer}
>
{#each categoryData[`${active.name}-${active.type}`].items as item (item.id)}
<div class="text-white p-2 rounded-xl flex bg-item">
<div class="h-16 w-16 flex items-center justify-center">
<img
src="/images/furnishing/{item.id}.png"
alt=""
class="h-16 w-16 image relative"
style="min-width: 4rem;"
/>
</div>
<div class="ml-2 flex flex-col">
<p class="text-sm flex-1">
{item.name}
</p>
<div class="flex items-center bg-black bg-opacity-25 rounded-md" style="width: max-content;">
<button class="hover:text-primary" on:click={() => changeItemValue(item.id, (saved[item.id] || 0) + 1)}>
<Icon path={mdiPlus} />
</button>
<input
type="number"
value={saved[item.id] || 0}
on:change={(e) => changeItemValue(item.id, e.target.value)}
class="bg-transparent w-12 text-center {(saved[item.id] || 0) > 0 ? 'text-white' : 'text-gray-600'}"
/>
<button class="hover:text-primary" on:click={() => changeItemValue(item.id, (saved[item.id] || 0) - 1)}>
<Icon path={mdiMinus} />
</button>
</div>
</div>
</div>
{/each}
</div>
</div>
{/if}
</div>
<style>
.category {
width: 100%;
}
@screen lg {
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
@apply bg-transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.35);
@apply rounded-xl;
}
.category {
min-width: 15rem;
width: 15rem;
top: 0px;
padding-top: 8px;
}
}
.image[alt]:after {
@apply block absolute top-0 left-0 w-full h-full bg-item;
content: '';
}
input[type='number'] {
-moz-appearance: textfield;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
</style>

View File

@ -0,0 +1,381 @@
<script context="module">
import data from '../../data/furnishing/en.json';
export async function preload() {
return { data };
}
</script>
<script>
import { onMount } from 'svelte';
import { locale, t } from 'svelte-i18n';
import debounce from 'lodash/debounce';
import { mdiInformationOutline, mdiMinus, mdiPlus } from '@mdi/js';
import TableHeader from '../../components/Table/TableHeader.svelte';
import Icon from '../../components/Icon.svelte';
import Button from '../../components/Button.svelte';
import { getAccountPrefix } from '../../stores/account';
import { readSave, updateSave } from '../../stores/saveManager';
export let data;
let type = 'hall';
let items = [];
let max = 0;
const minEnergy = {
exterior: 0,
exterior2: 0,
exterior3: 0,
exterior4: 0,
hall: 150,
room1: 150,
room2: 150,
room3: 150,
room4: 150,
room5: 150,
corridor: 150,
};
const maxLoad = {
exterior: 10000,
exterior2: 10000,
exterior3: 10000,
exterior4: 10000,
hall: 10000,
room1: 4000,
room2: 4000,
room3: 4000,
room4: 4000,
room5: 4000,
corridor: 4000,
};
let currentUsage = {
exterior: {},
exterior2: {},
exterior3: {},
exterior4: {},
hall: {},
room1: {},
room2: {},
room3: {},
room4: {},
room5: {},
corridor: {},
};
$: currentLoad = Object.entries(currentUsage[type]).reduce(
(prev, [id, val]) => {
prev.load += data[id].load * val;
prev.energy += data[id].energy * val;
return prev;
},
{
load: 0,
energy: minEnergy[type],
},
);
let sortBy = 'ratio';
let sortOrder = false;
async function parseFurnishing() {
const currentType = type.startsWith('exterior') ? 'exterior' : 'interior';
items = Object.values(data)
.filter((e) => e.type === currentType || e.type === '')
.sort((a, b) => {
switch (sortBy) {
case 'ratio':
if (sortOrder) return a.ratio - b.ratio;
else return b.ratio - a.ratio;
case 'energy':
if (sortOrder) return a.energy - b.energy;
else return b.energy - a.energy;
case 'load':
if (sortOrder) return a.load - b.load;
else return b.load - a.load;
case 'using':
if (sortOrder) return (currentUsage[type][a.id] || 0) - (currentUsage[type][b.id] || 0);
else return (currentUsage[type][b.id] || 0) - (currentUsage[type][a.id] || 0);
case 'name':
if (sortOrder) return a.name.localeCompare(b.name);
else return b.name.localeCompare(a.name);
}
});
}
function sort(by) {
if (sortBy === by) {
sortOrder = !sortOrder;
} else {
sortBy = by;
}
parseFurnishing();
}
function changeType(_type) {
type = _type;
sortBy = 'ratio';
sortOrder = false;
parseFurnishing();
max = items[0].ratio;
}
function changeUsage(id, val) {
if (currentUsage[type][id] === undefined) currentUsage[type][id] = 0;
currentUsage[type][id] = Math.max(0, currentUsage[type][id] + val);
if (currentUsage[type][id] === 0) delete currentUsage[type][id];
saveData();
}
async function changeLocale(locale) {
data = (await import(`../../data/furnishing/${locale}.json`)).default;
parseFurnishing();
}
function calculateColor(percentage) {
const hue = (percentage / max) * 120;
return `color: hsl(${hue}, 100%, 60%);`;
}
function calculateColorLoad(percentage) {
const hue = Math.max((1 - percentage) * 120, 0);
return `color: hsl(${hue}, 100%, 60%);`;
}
const saveData = debounce(async () => {
const data = currentUsage;
const prefix = getAccountPrefix();
await updateSave(`${prefix}furnishing`, data);
}, 2000);
async function readLocalData() {
const prefix = getAccountPrefix();
const furnishingData = await readSave(`${prefix}furnishing`);
if (furnishingData !== null) {
currentUsage = {
...currentUsage,
...furnishingData,
};
}
}
onMount(async () => {
parseFurnishing();
max = items[0].ratio;
await readLocalData();
locale.subscribe((val) => {
changeLocale(val);
});
});
</script>
<svelte:head>
<title>Furnishing - Paimon.moe</title>
<meta name="description" content="Genshin Impact Furnishing list with the load and energy values" />
<meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" />
</svelte:head>
<div class="lg:ml-64 pt-20 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="px-4 flex md:space-x-2 items-start md:items-center flex-col md:flex-row">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.title')}</h1>
<div class="flex items-center space-x-2">
<div
class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2 group relative"
style={calculateColorLoad(currentLoad.load / maxLoad[type])}
>
<Icon size={0.7} path={mdiInformationOutline} />
{$t('furnishing.load')}
{currentLoad.load} / {maxLoad[type]}
<div
class="hidden group-hover:block absolute left-0 transform translate-y-full
bg-white rounded-xl z-50 text-gray-900 px-4 py-2 w-screen max-w-xs md:max-w-sm"
style="bottom: -10px;"
>
<p>{$t('furnishing.info.0')}</p>
<p>{$t('furnishing.info.1')}</p>
</div>
</div>
<div class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2">
{$t('furnishing.energy')}
{currentLoad.energy}
</div>
<a href="/furnishing">
<Button>{$t('furnishing.inventory.openSets')}</Button>
</a>
</div>
</div>
<div class="px-4 flex space-x-2 mt-2 mb-2">
<button on:click={() => changeType('hall')} class="pill {type.startsWith('exterior') ? '' : 'active'}">
{$t('furnishing.interior')}
</button>
<button on:click={() => changeType('exterior')} class="pill {type.startsWith('exterior') ? 'active' : ''}">
{$t('furnishing.exterior')}
</button>
</div>
{#if type.startsWith('exterior')}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('exterior')} class="pill {type === 'exterior' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('exterior2')} class="pill {type === 'exterior2' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('exterior3')} class="pill {type === 'exterior3' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('exterior4')} class="pill {type === 'exterior4' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 4 } })}
</button>
</div>
{:else}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('hall')} class="pill {type === 'hall' ? 'active' : ''}">
{$t('furnishing.hall')}
</button>
<button on:click={() => changeType('room1')} class="pill {type === 'room1' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('room2')} class="pill {type === 'room2' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('room3')} class="pill {type === 'room3' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('corridor')} class="pill {type === 'corridor' ? 'active' : ''}">
{$t('furnishing.corridor')}
</button>
<button on:click={() => changeType('room4')} class="pill {type === 'room4' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 4 } })}
</button>
<button on:click={() => changeType('room5')} class="pill {type === 'room5' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 5 } })}
</button>
</div>
{/if}
<div class="flex mt-4 wrapper">
<div class="block overflow-x-auto xl:overflow-x-visible whitespace-no-wrap">
<div class="px-4 table">
<table class="w-full block pl-4 pr-4 py-2 md:pl-8 md:py-4 bg-item rounded-xl">
<tr>
<th />
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('name')}
sort={sortBy === 'name'}
order={sortOrder}
align="left"
>
{$t('furnishing.name')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('energy')}
sort={sortBy === 'energy'}
order={sortOrder}
align="center"
>
{$t('furnishing.energy')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('load')}
sort={sortBy === 'load'}
order={sortOrder}
align="center"
>
{$t('furnishing.load')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('ratio')}
sort={sortBy === 'ratio'}
order={sortOrder}
align="center"
>
{$t('furnishing.ratio')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('using')}
sort={sortBy === 'using'}
order={sortOrder}
align="center"
>
{$t('furnishing.using')}
</TableHeader>
</tr>
{#each items as item (item.id)}
<tr>
<td class="pr-4 h-12">
<img
src="/images/furnishing/{item.id}.png"
alt=""
class="h-12 w-12 image relative"
style="min-width: 3rem;"
/>
</td>
<td class="px-4 text-gray-200">{item.name}</td>
<td class="px-4 text-gray-200 text-center">{item.energy}</td>
<td class="px-4 text-gray-200 text-center">{item.load}</td>
<td class="px-4 text-gray-200 text-center" style={calculateColor(item.ratio)}>{item.ratio.toFixed(2)}</td>
<td class="px-4">
<div
class="flex justify-between items-center border-2 border-white border-opacity-25 rounded-xl text-gray-400"
>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, 1)}>
<Icon path={mdiPlus} />
</button>
<p
class="h-full px-2 text-center {currentUsage[type][item.id] > 0
? 'text-gray-200'
: 'text-gray-700'}"
style="min-width: 40px;"
>
{currentUsage[type][item.id] || 0}
</p>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, -1)}>
<Icon path={mdiMinus} />
</button>
</div>
</td>
</tr>
{/each}
</table>
</div>
</div>
</div>
</div>
<style>
.pill {
@apply rounded-2xl;
@apply border-2;
@apply border-white;
@apply border-opacity-25;
@apply text-white;
@apply px-4;
@apply py-1;
@apply outline-none;
@apply transition;
@apply duration-100;
@apply whitespace-no-wrap;
&:hover {
@apply border-primary;
}
&.active {
@apply bg-primary;
@apply border-primary;
@apply text-background;
}
}
.image[alt]:after {
@apply block absolute top-0 left-0 w-full h-full bg-item;
content: '';
}
</style>

View File

@ -15,6 +15,7 @@
import Discord from './_index/discord.svelte'; import Discord from './_index/discord.svelte';
import Twitter from './_index/twitter.svelte'; import Twitter from './_index/twitter.svelte';
import Achievement from './_index/achievement.svelte'; import Achievement from './_index/achievement.svelte';
import Furnishing from './_index/furnishing.svelte';
let refreshLayout; let refreshLayout;
@ -59,6 +60,7 @@
<Calculator on:done={onDone} /> <Calculator on:done={onDone} />
<Achievement on:done={onDone} /> <Achievement on:done={onDone} />
<Twitter on:done={onDone} /> <Twitter on:done={onDone} />
<Furnishing on:done={onDone} />
<!-- <div class="flex flex-col space-y-4"> <!-- <div class="flex flex-col space-y-4">
</div> --> </div> -->
</Masonry> </Masonry>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Some files were not shown because too many files have changed in this diff Show More