furigana format is now <>{} and add input message for glob

This commit is contained in:
polv committed 2026-03-15 14:27:34 +07:00
1 parent b1ef987f9d
commit 96e36e1d4c
3 files changed
+46 -30

No files matched your search

+4 -4
View File
@@ -38,7 +38,7 @@ interface Progress {
} }
function LoadingDialog() { function LoadingDialog() {
const [messages, setMessages] = useState<Progress[]>([]); const [messages, set_messages] = useState<Progress[]>([]);
const isInit = useRef(false); const isInit = useRef(false);
@@ -106,11 +106,11 @@ function LoadingDialog() {
} }
await invoke("init_yomitan", { dicts, lang }); await invoke("init_yomitan", { dicts, lang });
setMessages([]); set_messages([]);
} }
listen<LoadYomitanZipDirResult>("load-yomitan-dir", ({ payload }) => { listen<LoadYomitanZipDirResult>("load-yomitan-dir", ({ payload }) => {
setMessages((messages) => { set_messages((messages) => {
if (payload.new_dicts.length > 0) { if (payload.new_dicts.length > 0) {
messages = [ messages = [
...messages, ...messages,
@@ -149,7 +149,7 @@ function LoadingDialog() {
}, []); }, []);
function updateProgress(payload: Progress) { function updateProgress(payload: Progress) {
setMessages((messages) => { set_messages((messages) => {
const prev = messages[messages.length - 1]; const prev = messages[messages.length - 1];
if (prev?.message === payload.message) { if (prev?.message === payload.message) {
messages = messages.slice(0, messages.length - 1); messages = messages.slice(0, messages.length - 1);
+39 -26
View File
@@ -6,13 +6,13 @@ import {
useState, useState,
} from "react"; } from "react";
import { useSearchParams } from "react-router"; import { useSearchParams } from "react-router";
import { toKana } from "wanakana"; import { toHiragana, toKana } from "wanakana";
import { import {
Box, Box,
Card, Card,
CloseButton, CloseButton,
Group, Field,
Input, Input,
InputGroup, InputGroup,
Stack, Stack,
@@ -116,9 +116,10 @@ function SearchPage() {
} }
if (!norm_q) return; if (!norm_q) return;
const ender = /\p{Z}$/u.test(q) ? "" : "*"; const ender =
/\p{Z}$/u.test(q) || [..."*?[]"].some((c) => q.includes(c)) ? "" : "*";
const re = /\<(.+?)\>\[(.+?)\]/g; const re = /\<(.+?)\>{(.+?)}/g;
let qTerm = ""; let qTerm = "";
let qReading = ""; let qReading = "";
@@ -199,9 +200,7 @@ function SearchPage() {
q = toKana(q, { q = toKana(q, {
useObsoleteKana: true, useObsoleteKana: true,
IMEMode: true, IMEMode: true,
customKanaMapping: Object.fromEntries( customKanaMapping: Object.fromEntries([..."<>{}"].map((c) => [c, c])),
"<>[]".split("").map((c) => [c, c]),
),
}); });
} }
@@ -224,14 +223,14 @@ function SearchPage() {
target, target,
}) => { }) => {
const cleanedFuri = [...furigana.replace(/n/g, "ん")] const cleanedFuri = [...furigana.replace(/n/g, "ん")]
.map((c) => katakanaToHiragana(c)) .map((c) => toHiragana(c, { passRomaji: true }))
.join(""); .join("");
let parts = compositionData.split(KANJI_REGEX); let parts = compositionData.split(KANJI_REGEX);
if (parts.length === 1) return; if (parts.length === 1) return;
const hiraganaParts = parts.map((p) => const hiraganaParts = parts.map((p) =>
[...p].map((c) => katakanaToHiragana(c)).join(""), [...p].map((c) => toHiragana(c, { passRomaji: true })).join(""),
); );
const regex = new RegExp( const regex = new RegExp(
`^${hiraganaParts.map((p, idx) => `(${idx & 1 ? ".+" : p})`).join("")}$`, `^${hiraganaParts.map((p, idx) => `(${idx & 1 ? ".+" : p})`).join("")}$`,
@@ -244,7 +243,7 @@ function SearchPage() {
rt.shift(); rt.shift();
const markup = parts const markup = parts
.map((p, idx) => (idx & 1 ? `<${p}>[${rt[idx]}]` : p)) .map((p, idx) => (idx & 1 ? `<${p}>{${rt[idx]}}` : p))
.join(""); .join("");
set_q((q) => { set_q((q) => {
@@ -262,13 +261,6 @@ function SearchPage() {
}); });
}; };
function katakanaToHiragana(k: string) {
let c = k.charCodeAt(0);
return c >= 12449 && c <= 12531
? String.fromCharCode(k.charCodeAt(0) - 96)
: k;
}
const clearButton = q.trim() ? ( const clearButton = q.trim() ? (
<CloseButton <CloseButton
size={"xs"} size={"xs"}
@@ -309,7 +301,7 @@ function SearchPage() {
e.preventDefault(); e.preventDefault();
}} }}
> >
<Group attached w="full"> <Field.Root w="full">
<InputGroup endElement={clearButton}> <InputGroup endElement={clearButton}>
<Input <Input
id="greet-input" id="greet-input"
@@ -324,8 +316,24 @@ function SearchPage() {
placeholder="Search..." placeholder="Search..."
/> />
</InputGroup> </InputGroup>
{/* <Button type="submit">Search</Button> */} {q.includes(" ") ? (
</Group> <Field.HelperText>
Space (" ") to mark word boundary for search
</Field.HelperText>
) : null}
{[..."*?[]"].some((c) => q.includes(c)) ? (
<Field.HelperText>
<code>{"*?[]"}</code> can be used as
<a
href="https://en.wikipedia.org/wiki/Glob_(programming)#Syntax"
target="_blank"
rel="noopener noreferrer"
>
glob
</a>
</Field.HelperText>
) : null}
</Field.Root>
<div style={{ marginTop: "0.5em", display: "flex", gap: "1em" }}> <div style={{ marginTop: "0.5em", display: "flex", gap: "1em" }}>
<Switch.Root <Switch.Root
checked={isAutoKana} checked={isAutoKana}
@@ -357,18 +365,23 @@ function SearchPage() {
<li key={i} lang={lang} style={{ marginLeft: "1.5em" }}> <li key={i} lang={lang} style={{ marginLeft: "1.5em" }}>
<div> <div>
<Card.Root> <Card.Root>
<Card.Header flexDirection={"row"} gap={0}> <Card.Header display={"block"}>
<a onClick={() => onTermClicked(term + " ")}>{term}</a> <a
onClick={() =>
onTermClicked(reading ? `<${term}>{${reading}}` : term)
}
>
{term}
</a>
{reading && reading !== term ? ( {reading && reading !== term ? (
<> <>
{"("} {"("}
<a onClick={() => onTermClicked(reading + " ")}> <a onClick={() => onTermClicked(reading)}>{reading}</a>
{reading}
</a>
{")"} {")"}
</> </>
) : null} ) : null}
【{dict_title}】{JSONdumpClean(it)} <span>【{dict_title}】</span>
<span>{JSONdumpClean(it)}</span>
</Card.Header> </Card.Header>
<Card.Body> <Card.Body>
<div <div
+3
View File
@@ -29,4 +29,7 @@ export default defineConfig(async () => ({
ignored: ["**/src-tauri/**"], ignored: ["**/src-tauri/**"],
}, },
}, },
build: {
chunkSizeWarningLimit: 1024, // default = 500 kB
},
})); }));