From 38169ecef7dc02165be31660d95524c72a15dac9 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:29:47 +0800 Subject: [PATCH] [+] Input --- src/routes/song/[slug]/+page.svelte | 60 ++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/routes/song/[slug]/+page.svelte b/src/routes/song/[slug]/+page.svelte index ed73774..227196b 100644 --- a/src/routes/song/[slug]/+page.svelte +++ b/src/routes/song/[slug]/+page.svelte @@ -4,6 +4,7 @@ import { LinearProgress } from "m3-svelte"; import { onMount } from "svelte"; import type { LyricSegment } from "../../../shared/types.ts"; + import { isKana, isKanji, toHiragana } from "wanakana"; let { data }: PageProps = $props() @@ -41,6 +42,56 @@ onMount(() => { hiddenInput.focus() }) + + // Fuzzy matching rules + const fuzzyMatch = [['わ', 'は'], ['を', 'お']] + function fuzzyEquals(kana1: string, kana2: string): string { + [kana1, kana2] = [toHiragana(kana1), toHiragana(kana2)] + if (kana1 === kana2) return 'right' + if (fuzzyMatch.some(([a, b]) => (kana1 === a && kana2 === b) || (kana1 === b && kana2 === a))) return 'fuzzy' + return 'wrong' + } + + // List of characters need to be composed instead of directly typed + const composeList = [ + 'っ', 'ゃ', 'ゅ', 'ょ', 'ぁ', 'ぃ', 'ぅ', 'ぇ', 'ぉ', + 'が', 'ぎ', 'ぐ', 'げ', 'ご', + 'ざ', 'じ', 'ず', 'ぜ', 'ぞ', + 'だ', 'ぢ', 'づ', 'で', 'ど', + 'ば', 'び', 'ぶ', 'べ', 'ぼ', + 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ' + ] + + // On input changed: Convert to hiragana, compare with current position, update states + function inputChanged(input: string, isComposed: boolean) { + console.log(`input changed: ${input}`) + // Convert to hiragana + inp = toHiragana(inp, { IMEMode: true }) + + // While it has kana or kanji, pop from input + while (inp.length && (isKana(inp[0]) || isKanji(inp[0]))) { + let char = inp[0] + + // Check if it matches current character + let cLine = processedLrc[li] + let cSeg = cLine.parts.find(seg => wi >= seg.swi && wi < seg.swi + seg.kana.length)! + let exp = cSeg.kana[wi - cSeg.swi] + let res = fuzzyEquals(char, exp) + if (res === 'wrong' && !isComposed && composeList.includes(exp)) return // Need to compose, stop here + states[li][wi] = res + + // Move index + wi += 1 + if (wi >= cLine.totalLen) { + li += 1 + wi = 0 + } + inp = inp.slice(1) + } + } + + $effect(() => inputChanged(inp, false)) + a.name).join(", ") + " - " + data.brief.album} right={[ @@ -50,7 +101,11 @@ - + { + inputChanged(inp, true) + console.log("Event: input") +}} bind:value={inp} autofocus /> +
{#each processedLrc as line, l} @@ -88,6 +143,9 @@ .wrong color: #e55757 background-color: rgba(229, 87, 87, 0.1) + .fuzzy + color: #e5a657 + background-color: rgba(229, 166, 87, 0.1) .right color: #7b78c2 \ No newline at end of file