Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"dependencies": {
"cheerio": "^1.0.0",
"hangul-js": "^0.2.6",
"hiroba-js": "^2.3.2",
"hiroba-js": "2.4.0",
"lzutf8": "^0.6.3",
"node-hiroba": "^1.7.7",
"svelte-lazy": "^1.2.7",
Expand Down
43 changes: 43 additions & 0 deletions src/components/Closet/Closet-btn.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
function openPopup (): void {
const url = new URL(location.href)
url.pathname = '/'
url.hash = '/closet'
Array.from(url.searchParams).forEach(([k, _]) => {
console.log(k)
url.searchParams.delete(k)
})

location.href = url.href
}
</script>

<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->

<div
class="button"
on:click={openPopup}
>
<div class="text">
Closet++
</div>
</div>

<style>
.button{
display:flex;
justify-content: center;
align-items: center;
}

.text{
background-image: url('image/sp/640/btn_p_04_640.png');
background-size: 100% 100%;
width: 100px;
height: 40px;
display:flex;
justify-content: center;
align-items: center;
}
</style>
102 changes: 102 additions & 0 deletions src/components/Closet/Closet.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { Closet } from '../../lib/closet'
import Button from '../Common/Button.svelte'
import CostumePreset from './CostumePreset.svelte'
import type { KisekaeReqData } from 'hiroba-js'
import type { Writable } from 'svelte/store'

const title: Writable<string> = getContext('title')
let parts: number[][] = []
let presets: KisekaeReqData[] = []
let closet: Closet | null = null
$: updateCloset(presets)?.catch(() => {})

onMount(async () => {
title.set('Closet++')
parts = await fetchCostumeParts()
closet = await Closet.getInstance()
presets = [...closet.presets]
window.scrollTo(0, 0)
})

async function fetchCostumeParts (): Promise<number[][]> {
const response = await fetch('https://donderhiroba.jp/mypage_kisekae.php')
const html = await response.text()

const dom = new DOMParser().parseFromString(html, 'text/html')
const ids = [
'tab-cos-kigu',
'tab-cos-head',
'tab-cos-body',
'tab-cos-make',
'tab-cos-acce'
]

const parts: number[][] = [[], [], [], [], []]
ids.forEach((id, i) => {
dom
.getElementById(id)
?.querySelectorAll('ul > li > a')
?.forEach((e) => {
const num = Number(e.getAttribute('name'))
if (!Number.isNaN(num)) {
parts[i].push(num)
}
})
})

return parts
}

async function addPreset (): Promise<void> {
presets = [...presets, {
color: {
body: 1,
face: 0,
limb: 3
},
costume: {
kigurumi: 0,
head: 0,
body: 0,
face: 0,
petitCharacter: 0
}
}]
}

async function updateCloset (presets: KisekaeReqData[]): Promise<void> {
if (closet === null) return
closet.presets = [...presets]
await closet.save()
}
</script>

<div class="container">
{#if closet}
{#each presets as preset, index}
<CostumePreset
bind:preset
{parts}
remove={() => {
presets = presets.filter((_, i) => i !== index)
}}
/>
{/each}
<Button on:click={addPreset}>Add preset</Button>
{:else}
Loading...
{/if}
</div>

<style>
.container {
display: flex;
flex-direction: column;
width: 100%;
max-width: 300px;
row-gap: 10px;
margin-top: 5px;
}
</style>
103 changes: 103 additions & 0 deletions src/components/Closet/CostumePreset.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script lang="ts">
import { DonderHiroba, type KisekaeReqData } from 'hiroba-js'
import Button from '../Common/Button.svelte'
import PresetEditor from './PresetEditor.svelte'

export let preset: KisekaeReqData
export let parts: number[][]
export let remove: () => any

let editorOpened = false
$: costumeUrl = getCostumeURL(preset)

function getCostumeURL (preset: KisekaeReqData): string {
const url = new URL('https://donderhiroba.jp/imgsrc_mydon.php')
url.searchParams.set('face', preset.color.face.toString())
url.searchParams.set('body', preset.color.body.toString())
url.searchParams.set('limb', preset.color.limb.toString())
url.searchParams.set('cos1', preset.costume.kigurumi.toString())
url.searchParams.set('cos2', preset.costume.head.toString())
url.searchParams.set('cos3', preset.costume.body.toString())
url.searchParams.set('cos4', preset.costume.face.toString())
url.searchParams.set('cos5', preset.costume.petitCharacter.toString())

return url.href
}
</script>

<div class="container">
<div class="preview">
<div class="left">
<img src={costumeUrl} alt="costume" />
</div>
<div class="right">
<Button
on:click={async () => {
await DonderHiroba.func.changeKisekae({ kisekae: preset }).then(() => { alert('Applied successfully.') })
}}
>
<div style="width:80px;">Apply</div>
</Button>
<Button
on:click={() => {
editorOpened = !editorOpened
}}
>
<div style="width:80px;">Edit</div>
</Button>
<Button
on:click={() => {
preset.costume.kigurumi = 0
preset.costume.head = 0
preset.costume.body = 0
preset.costume.face = 0
preset.costume.petitCharacter = 0
}}
>
<div style="width:80px;">Reset</div>
</Button>
<Button
on:click={() => {
if (confirm('Are you sure?')) {
remove()
}
}}
>
<div style="width:80px;">Remove</div>
</Button>
</div>
</div>
<PresetEditor bind:preset {parts} show={editorOpened} />
</div>

<style>
.container {
width: 100%;
background-color: white;
border-radius: 10px;

box-sizing: border-box;
padding: 5px;
}

.preview {
display: flex;
flex-direction: row;
}

.left,
.right {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.right {
flex: 1 1 auto;
}

img {
width: 150px;
height: 150px;
}
</style>
Loading
Loading