(feat) auto-Kana with wanakana 🐊

This commit is contained in:
polv committed 2026-02-28 15:11:53 +07:00
1 parent 641f23538b
commit e3ccd517a8
3 files changed
+67 -12

No files matched your search

+9
View File
@@ -44,6 +44,9 @@ importers:
react-icons: react-icons:
specifier: ^5.5.0 specifier: ^5.5.0
version: 5.5.0(react@19.2.4) version: 5.5.0(react@19.2.4)
wanakana:
specifier: ^5.3.1
version: 5.3.1
zustand: zustand:
specifier: ^5.0.11 specifier: ^5.0.11
version: 5.0.11(@types/react@19.2.14)(react@19.2.4) version: 5.0.11(@types/react@19.2.14)(react@19.2.4)
@@ -1195,6 +1198,10 @@ packages:
yaml: yaml:
optional: true optional: true
wanakana@5.3.1:
resolution: {integrity: sha512-OSDqupzTlzl2LGyqTdhcXcl6ezMiFhcUwLBP8YKaBIbMYW1wAwDvupw2T9G9oVaKT9RmaSpyTXjxddFPUcFFIw==}
engines: {node: '>=12'}
yallist@3.1.1: yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -2618,6 +2625,8 @@ snapshots:
'@types/node': 24.10.13 '@types/node': 24.10.13
fsevents: 2.3.3 fsevents: 2.3.3
wanakana@5.3.1: {}
yallist@3.1.1: {} yallist@3.1.1: {}
yaml@1.10.2: {} yaml@1.10.2: {}
+1
View File
@@ -19,6 +19,7 @@
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"wanakana": "^5.3.1",
"zustand": "^5.0.11" "zustand": "^5.0.11"
}, },
"devDependencies": { "devDependencies": {
+57 -12
View File
@@ -1,6 +1,16 @@
import { CompositionEventHandler, useEffect, useRef, useState } from "react"; import { CompositionEventHandler, useEffect, useRef, useState } from "react";
import { toHiragana } from "wanakana";
import { Box, Button, Card, Group, Input, Stack } from "@chakra-ui/react"; import {
Box,
Card,
CloseButton,
Group,
Input,
InputGroup,
Stack,
Switch,
} from "@chakra-ui/react";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import Glossary from "../components/Glossary"; import Glossary from "../components/Glossary";
@@ -22,7 +32,10 @@ function SearchPage() {
const [entries, setEntries] = useState<Entry[]>([]); const [entries, setEntries] = useState<Entry[]>([]);
const [searchTimeout, setSearchTimeout] = useState(0); const [searchTimeout, setSearchTimeout] = useState(0);
const [furigana, setFurigana] = useState(""); const [furigana, setFurigana] = useState("");
const [isAutoKana, set_isAutoKana] = useState(true);
const nSearch = useRef(0); const nSearch = useRef(0);
const searchboxRef = useRef<HTMLInputElement | null>(null);
const lang = "ja-JP"; const lang = "ja-JP";
@@ -89,6 +102,14 @@ function SearchPage() {
); );
} }
function onSearchboxChange(q: string) {
if (isAutoKana) {
q = toHiragana(q, { useObsoleteKana: true, IMEMode: true });
}
setQ(q);
}
const FURIGANA_REGEX = /^[\p{scx=Hiragana}\p{scx=Katakana}]+$/u; const FURIGANA_REGEX = /^[\p{scx=Hiragana}\p{scx=Katakana}]+$/u;
const KANJI_REGEX = /([\p{sc=Han}\p{N}々〆ヵヶ]+)/u; const KANJI_REGEX = /([\p{sc=Han}\p{N}々〆ヵヶ]+)/u;
@@ -134,6 +155,17 @@ function SearchPage() {
: k; : k;
} }
const clearButton = q.trim() ? (
<CloseButton
size={"xs"}
me={-2}
onClick={() => {
setQ("");
searchboxRef.current ? searchboxRef.current.focus() : null;
}}
/>
) : null;
return ( return (
<Stack maxW={"1000px"} margin={"0.5em auto"}> <Stack maxW={"1000px"} margin={"0.5em auto"}>
<form <form
@@ -143,18 +175,31 @@ function SearchPage() {
}} }}
> >
<Group attached w="full"> <Group attached w="full">
<Input <InputGroup endElement={clearButton}>
id="greet-input" <Input
value={q} id="greet-input"
onChange={(e) => setQ(e.currentTarget.value)} ref={searchboxRef}
onCompositionUpdate={updateFurigana} value={q}
onCompositionEnd={addFurigana} onChange={(e) => onSearchboxChange(e.currentTarget.value)}
autoComplete="off" onCompositionUpdate={updateFurigana}
spellCheck={false} onCompositionEnd={addFurigana}
placeholder="Search..." autoComplete="off"
/> spellCheck={false}
<Button type="submit">Search</Button> placeholder="Search..."
/>
</InputGroup>
{/* <Button type="submit">Search</Button> */}
</Group> </Group>
<div style={{ marginTop: "0.5em" }}>
<Switch.Root
checked={isAutoKana}
onCheckedChange={(d) => set_isAutoKana(d.checked)}
>
<Switch.HiddenInput />
<Switch.Control />
<Switch.Label>Auto-convert Kana</Switch.Label>
</Switch.Root>
</div>
</form> </form>
<Box as={"ol"} listStyleType={"number"} style={{ margin: "1em" }}> <Box as={"ol"} listStyleType={"number"} style={{ margin: "1em" }}>
{entries.map( {entries.map(