From 5fcbdc1af3d26033244990cc66669549d712f1e7 Mon Sep 17 00:00:00 2001 From: uxiun Date: Sat, 4 Nov 2023 14:57:11 +0900 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E6=9B=B8=E3=81=8D=E3=81=97=E3=81=A6id?= =?UTF-8?q?=E3=81=8C=E5=8F=96=E5=BE=97=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=8F?= =?UTF-8?q?=E3=81=AA=E3=82=89=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/TextRewriter/entries.tsx | 383 ++++++++--------- pages/TextRewriter/entryForm.tsx | 525 ++++++++++++----------- pages/TextRewriter/index.tsx | 2 + pages/TextRewriter/info.module.css | 4 + pages/TextRewriter/info.tsx | 98 +++++ pages/TextRewriter/output.module.css | 4 + pages/TextRewriter/output.tsx | 326 +++++++------- pages/TextRewriter/recycleForm.tsx | 609 ++++++++++++++------------- pages/TextRewriter/searchForm.tsx | 61 ++- pages/index.tsx | 1 + styles/Home.module.css | 0 ts/atom.ts | 444 +++++++++---------- ts/lang.ts | 243 +++++++---- ts/type.ts | 11 +- 14 files changed, 1469 insertions(+), 1242 deletions(-) create mode 100644 pages/TextRewriter/info.module.css create mode 100644 pages/TextRewriter/info.tsx create mode 100644 pages/TextRewriter/output.module.css delete mode 100644 styles/Home.module.css diff --git a/pages/TextRewriter/entries.tsx b/pages/TextRewriter/entries.tsx index 828ef9b..e70dcb5 100644 --- a/pages/TextRewriter/entries.tsx +++ b/pages/TextRewriter/entries.tsx @@ -1,28 +1,29 @@ import { - Entry, - EntryBoolKey, - FromToObj, - allentriesAtom, - dedupedEntryMapAtom, - entryIdDedupedMapAtom, - getDedupKeyById, - getLengthMap, - inputtingEntryAtom, - newEntryAtom, - ngramIndexesForEntryAtom, - searchDelayAtom, - uilanguageAtom, + Entry, + EntryBoolKey, + FromToObj, + allentriesAtom, + dedupedEntryMapAtom, + entryAttributeReadable, + entryIdDedupedMapAtom, + getDedupKeyById, + getLengthMap, + inputtingEntryAtom, + newEntryAtom, + ngramIndexesForEntryAtom, + searchDelayAtom, + uilanguageAtom, } from "@/ts/atom" import { - Box, - Button, - FormControl, - IconButton, - Input, - TextField, - Typography, - Checkbox, - FormControlLabel, + Box, + Button, + FormControl, + IconButton, + Input, + TextField, + Typography, + Checkbox, + FormControlLabel, } from "@mui/material" import EditIcon from "@mui/icons-material/Edit" import { useAtom } from "jotai" @@ -39,29 +40,29 @@ import SearchForm from "./searchForm" import { spacecss } from "@/ts/css" const Entries: FC = () => { - return ( -
- - -
- ) + return ( +
+ + +
+ ) } const RecentEntries: FC = ({ limit }) => { - const [newEntry] = useAtom(newEntryAtom) - const [allentries, setAllEntries] = useAtom(allentriesAtom) - return ( - <> -
- {allentries - .slice(-limit) - .reverse() - .map(e => ( -
{JSON.stringify(e)}
- ))} -
- - ) + const [newEntry] = useAtom(newEntryAtom) + const [allentries, setAllEntries] = useAtom(allentriesAtom) + return ( + <> +
+ {allentries + .slice(-limit) + .reverse() + .map(e => ( +
{JSON.stringify(e)}
+ ))} +
+ + ) } // const AutocompleteForm: FC = () => { @@ -72,107 +73,109 @@ const RecentEntries: FC = ({ limit }) => { // } type SimilarEntriesProp = { - maxShowNumber: number - allowDiffMax?: number + maxShowNumber: number + allowDiffMax?: number } const SimilarEntries: FC = prop => { - const [allentries, setAllEntries] = useAtom(allentriesAtom) - const [inputting] = useAtom(inputtingEntryAtom) - const [atomIndex] = useAtom(ngramIndexesForEntryAtom) - const [atomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) - const [atomDedupedMap] = useAtom(dedupedEntryMapAtom) - const [atomUIlanguage] = useAtom(uilanguageAtom) - - const [showPoint, setShowPoint] = useState(false) - - const lengthmaps = getLengthMap(atomEntryIdDedupedMap, atomDedupedMap) - - const ngramScoreMap_concernLength = (fromto: keyof FromToObj) => - concernLength( - mapUnionsToAverage( - atomIndex[fromto].map(index => { - const ngramScored = ngramScoreMap(index)(inputting[fromto]) - // console.log(`ngramScored${index.n}`, ngramScored) - return ngramScored - }) - ) - )(inputting[fromto].length, lengthmaps[fromto]) - - const similarfrom = useMemo(() => { - // console.log("atom index", atomIndex) - const scored = ngramScoreMap_concernLength("from") - // console.log("scored", scored) - return [...scored.entries()].sort((d, f) => f[1] - d[1]).slice(0, prop.maxShowNumber) - }, [allentries, inputting.from]) - const similarto = useMemo(() => { - const scored = ngramScoreMap_concernLength("to") - return [...scored.entries()].sort((d, f) => f[1] - d[1]).slice(0, prop.maxShowNumber) - }, [allentries, inputting.to, inputting.from]) - - const similarCalc = { - from: similarfrom, - to: similarto, - } - const cssflexCenter = { - display: "flex", - alignItems: "center", - } - - const similarList = (fromto: keyof FromToObj) => - function listfunc() { - return ( - - {fromto} - - {similarCalc[fromto].map(([id, score]) => { - // console.log("atomEntryIdDedupedMap", atomEntryIdDedupedMap) - const fkey = getDedupKeyById(id, atomEntryIdDedupedMap) - return fkey === undefined ? ( - cannot get id - ) : ( - - {showPoint ? `${ketaDirtyDisplay("round", 5)(score)}` : ``} - {caseUndefined(atomDedupedMap.get(fkey ?? ""))(v => { - const e: Entry = { ...JSON.parse(fkey), to: v } - return - }, stringified key not found)} - - ) - })} - - - ) - } - - return ( - - - {translationTree.headerTitle.list.similar[atomUIlanguage]} - - setShowPoint(d => !d)} - value="showPoint" - /> - } - label={translationTree.showPoint[atomUIlanguage]} - /> - - - - {[similarList("from")(), similarList("to")()]} - - ) + const [allentries, setAllEntries] = useAtom(allentriesAtom) + const [inputting] = useAtom(inputtingEntryAtom) + const [atomIndex] = useAtom(ngramIndexesForEntryAtom) + const [atomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) + const [atomDedupedMap] = useAtom(dedupedEntryMapAtom) + const [atomUIlanguage] = useAtom(uilanguageAtom) + + const [showPoint, setShowPoint] = useState(false) + + const lengthmaps = getLengthMap(atomEntryIdDedupedMap, atomDedupedMap) + + const ngramScoreMap_concernLength = (fromto: keyof FromToObj) => + concernLength( + mapUnionsToAverage( + atomIndex[fromto].map(index => { + const ngramScored = ngramScoreMap(index)(inputting[fromto]) + // console.log(`ngramScored${index.n}`, ngramScored) + return ngramScored + }) + ) + )(inputting[fromto].length, lengthmaps[fromto]) + + const similarfrom = useMemo(() => { + // console.log("atom index", atomIndex) + const scored = ngramScoreMap_concernLength("from") + // console.log("scored", scored) + return [...scored.entries()].sort((d, f) => f[1] - d[1]).slice(0, prop.maxShowNumber) + }, [allentries, inputting.from]) + const similarto = useMemo(() => { + const scored = ngramScoreMap_concernLength("to") + return [...scored.entries()].sort((d, f) => f[1] - d[1]).slice(0, prop.maxShowNumber) + }, [allentries, inputting.to, inputting.from]) + + const similarCalc = { + from: similarfrom, + to: similarto, + } + const cssflexCenter = { + display: "flex", + alignItems: "center", + } + + const similarList = (fromto: keyof FromToObj) => + function listfunc() { + return ( + + {entryAttributeReadable(atomUIlanguage)(fromto)} + + {similarCalc[fromto].map(([id, score]) => { + // console.log("atomEntryIdDedupedMap", atomEntryIdDedupedMap) + const fkey = getDedupKeyById(id, atomEntryIdDedupedMap) + return fkey === undefined ? ( + cannot get id + ) : ( + + {showPoint ? `${ketaDirtyDisplay("round", 5)(score)}` : ``} + {caseUndefined(atomDedupedMap.get(fkey ?? ""))(v => { + const e: Entry = { ...JSON.parse(fkey), to: v } + return + }, stringified key not found)} + + ) + })} + + + ) + } + + return ( + + + + {translationTree.headerTitle.list.similar[atomUIlanguage]} + + + setShowPoint(d => !d)} + value="showPoint" + /> + } + label={translationTree.showPoint[atomUIlanguage]} + /> + + + + {[similarList("from")(), similarList("to")()]} + + ) } // type DisplayEntryByIdProp = { @@ -184,59 +187,59 @@ const SimilarEntries: FC = prop => { type ToggledEntryView = { entry: Entry; hideMe: () => void } const EntryView: FC = ({ entry }) => { - const checkboxAlterTypo = function TypoWrap(label: EntryBoolKey) { - return ( - - {label} - - ) - } - return ( - - {entry.from} - - → - - {entry.to} - - {(["ic", "sc", "mw"] as EntryBoolKey[]).map(k => checkboxAlterTypo(k))} - - - ) + const checkboxAlterTypo = function TypoWrap(label: EntryBoolKey) { + return ( + + {label} + + ) + } + return ( + + {entry.from} + + → + + {entry.to} + + {(["ic", "sc", "mw"] as EntryBoolKey[]).map(k => checkboxAlterTypo(k))} + + + ) } type PropEntry = { - entry: Entry + entry: Entry } const EachEntry: FC = ({ entry }) => { - const [formVisible, setFormVisible] = useState(false) - - return ( - - { - setFormVisible(s => !s) - }}> - - - {formVisible ? ( - setFormVisible(false)} defaultValues={entry} /> - ) : ( - - )} - - ) + const [formVisible, setFormVisible] = useState(false) + + return ( + + { + setFormVisible(s => !s) + }}> + + + {formVisible ? ( + setFormVisible(false)} defaultValues={entry} /> + ) : ( + + )} + + ) } export default Entries diff --git a/pages/TextRewriter/entryForm.tsx b/pages/TextRewriter/entryForm.tsx index 774bb11..6c84dfb 100644 --- a/pages/TextRewriter/entryForm.tsx +++ b/pages/TextRewriter/entryForm.tsx @@ -1,267 +1,298 @@ -import validator from "validator"; -import { Entry, TooltipOperationBools, allentriesAtom, dedupEntries, dedupedEntryMapAtom, defaultEntry, defaultTooltipOperationBools, entryAttributeReadable, entryIdDedupedMapAtom, get_by_entry, inputtingEntryAtom, keyvalueToEntry, matchingEntryAtom, newEntryAtom, nextEntryIdAtom, ngramIndexesForEntryAtom, ngramIndexingEntry, searchDelayAtom, uilanguageAtom } from "@/ts/atom"; -import { translationTree } from "@/ts/lang"; -import { Box, Button, Checkbox, FormControlLabel, TextField, Tooltip } from "@mui/material"; -import { useAtom } from "jotai"; -import { FC, MouseEventHandler, useEffect, useState } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; -import Entries from "./entries"; -import JSONoutput from "./output"; -import { ngramIndexing, ngramSegmentify } from "@/ts/ts/text"; -import { useDebounce } from "@/ts/hook"; +import validator from "validator" +import { + Entry, + TooltipOperationBools, + allentriesAtom, + dedupEntries, + dedupedEntryMapAtom, + defaultEntry, + defaultTooltipOperationBools, + entryAttributeReadable, + entryIdDedupedMapAtom, + get_by_entry, + inputtingEntryAtom, + keyvalueToEntry, + matchingEntryAtom, + newEntryAtom, + nextEntryIdAtom, + ngramIndexesForEntryAtom, + ngramIndexingEntry, + searchDelayAtom, + uilanguageAtom, +} from "@/ts/atom" +import { translationTree } from "@/ts/lang" +import { Box, Button, Checkbox, FormControlLabel, TextField, Tooltip } from "@mui/material" +import { useAtom } from "jotai" +import { FC, MouseEventHandler, useEffect, useState } from "react" +import { Controller, useForm, useWatch } from "react-hook-form" +import Entries from "./entries" +import JSONoutput from "./output" +import { ngramIndexing, ngramSegmentify } from "@/ts/ts/text" +import { useDebounce } from "@/ts/hook" const EntryForm: FC = () => { - const [message, setMessage] = useState({ - alreadyExist: "", - operation: "", - }) - const [lang] = useAtom(uilanguageAtom) - const [newForm, setNewForm] = useAtom(newEntryAtom) - const readableAttr = (k: keyof Entry) => entryAttributeReadable(lang)(k) - const [allentries, setAllEntries] = useAtom(allentriesAtom) - const [atomMatchingEntry, setatomMatchingEntry] = useAtom(matchingEntryAtom) - const [atomInputtingEntry, setatomInputtingEntry] = useAtom(inputtingEntryAtom) - const [atomIndex, setatomIndex] = useAtom(ngramIndexesForEntryAtom) - const [atomNextEntryId, setatomNextEntryId] = useAtom(nextEntryIdAtom) - const [atomEntryIdDedupedMap, setatomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) - const [atomSearchDelay] = useAtom(searchDelayAtom) + const [message, setMessage] = useState({ + alreadyExist: "", + operation: "", + }) + const [lang] = useAtom(uilanguageAtom) + const [newForm, setNewForm] = useAtom(newEntryAtom) + const readableAttr = (k: keyof Entry) => entryAttributeReadable(lang)(k) + const [allentries, setAllEntries] = useAtom(allentriesAtom) + const [atomMatchingEntry, setatomMatchingEntry] = useAtom(matchingEntryAtom) + const [atomInputtingEntry, setatomInputtingEntry] = useAtom(inputtingEntryAtom) + const [atomIndex, setatomIndex] = useAtom(ngramIndexesForEntryAtom) + const [atomNextEntryId, setatomNextEntryId] = useAtom(nextEntryIdAtom) + const [atomEntryIdDedupedMap, setatomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) + const [atomSearchDelay] = useAtom(searchDelayAtom) + const [entriesForJSON, setEntriesForJSON] = useState(allentries) + type Form = Entry + const { control, handleSubmit, formState } = useForm
({ + defaultValues: defaultEntry, + }) + const realtimeUseWatchValue = useWatch({ control }) + const useWatchValue = useDebounce(atomSearchDelay, realtimeUseWatchValue) + const [dedupedEntryMap, setDedupedEntryMap] = useAtom(dedupedEntryMapAtom) + const [isCheckboxEnable, setIsCheckboxEnable] = useState(false) + type AddButtonContent = "add" | "update" + const [addButtonContent, setAddButtonContent] = useState("add") + const [openTip, setOpenTip] = useState(defaultTooltipOperationBools) - const [entriesForJSON, setEntriesForJSON] = useState(allentries) - type Form = Entry - const {control, handleSubmit, formState} = useForm({ - defaultValues: defaultEntry - }) - const realtimeUseWatchValue = useWatch({control}) - const useWatchValue = useDebounce(atomSearchDelay, realtimeUseWatchValue) - const [dedupedEntryMap, setDedupedEntryMap] = useAtom(dedupedEntryMapAtom) - const [isCheckboxEnable, setIsCheckboxEnable] = useState(false); - type AddButtonContent = "add"|"update" - const [addButtonContent, setAddButtonContent] = useState("add"); - const [openTip, setOpenTip] = useState(defaultTooltipOperationBools) + useEffect(() => { + // const debounce = setTimeout(() => { + const newe: Entry = { + from: useWatchValue.from ?? newForm.from, + to: useWatchValue.to ?? newForm.to, + ic: useWatchValue.ic ?? newForm.ic, + mw: useWatchValue.mw ?? newForm.mw, + sc: useWatchValue.sc ?? newForm.sc, + } + setatomInputtingEntry(newe) + setIsCheckboxEnable(!!newe.from.match(/.*[a-zA-Z].*/)) + const to = get_by_entry(dedupedEntryMap)(newe) + if (typeof to == "string") { + setMessage(s => ({ ...s, alreadyExist: translationTree.confirm.alreadyExist[lang] })) + setAddButtonContent("update") + setatomMatchingEntry({ ...newe, to }) + } else { + setMessage(s => ({ ...s, alreadyExist: "" })) + setAddButtonContent("add") + setatomMatchingEntry(newe) + } + // }, 100) + // return () => clearTimeout(debounce) + }, [useWatchValue, allentries]) + const addEntry = (f: Form) => { + const { to, ...fkey } = f + const key = JSON.stringify(fkey) + const matchedEntry = atomMatchingEntry + const hasDeleted = dedupedEntryMap.delete(key) //削除しておくことで更新した項目が上に来る + dedupedEntryMap.set(key, to) + if (hasDeleted) { + console.log("updated ", matchedEntry) + console.log("to", `"${to}"`) + setMessage(s => ({ + ...s, + operation: translationTree.tooltip.operation.update(f.from, matchedEntry.to)(f.to)[lang], + })) + setatomIndex(indexes => ({ + from: indexes.from, + to: indexes.to.map(index => { + ngramSegmentify(index.n, matchedEntry.to).forEach(ngram => index.index.delete(ngram)) + return ngramIndexing(index)(atomNextEntryId, to) + }), + })) + } else { + console.log("added:", f) + setMessage(s => ({ + ...s, + operation: translationTree.tooltip.operation.add(f.from, f.to)[lang], + })) + setatomIndex(indexes => ngramIndexingEntry(atomNextEntryId, indexes)(f)) + } + setatomEntryIdDedupedMap(m => { + m.set(key, atomNextEntryId) + return new Map([...m.entries()]) + }) + setatomNextEntryId(i => i + 1) + const array = Array.from(dedupedEntryMap) + const map = new Map(array) + setDedupedEntryMap(map) + const newEntries = array.map(k => keyvalueToEntry(k)) + setAllEntries(newEntries) + setEntriesForJSON(newEntries) + } + const deleteEntry = (f: Form) => { + const { to, ...fkey } = f + const key = JSON.stringify(fkey) + const hasDeleted = dedupedEntryMap.delete(key) + if (hasDeleted) { + console.log("deleted:", matchingEntryAtom) + setMessage(s => ({ + ...s, + operation: translationTree.tooltip.operation.delete(f.from, f.to)[lang], + })) + setatomEntryIdDedupedMap(m => { + m.delete(key) + return new Map([...m.entries()]) + }) + } else { + console.log("failed to delete") + setMessage(s => ({ ...s, operation: translationTree.tooltip.fail("delete")[lang] })) + } - useEffect(()=>{ - const debounce = setTimeout(()=>{ + const array = Array.from(dedupedEntryMap) + const map = new Map(array) + setDedupedEntryMap(map) + const newEntries = array.map(k => keyvalueToEntry(k)) + setAllEntries(newEntries) + setEntriesForJSON(newEntries) + } + const checkbox = function CheckboxWrapper(name: keyof Form) { + return ( + ( + + )} + /> + } + label={entryAttributeReadable(lang)(name)} + /> + ) + } - const newe: Entry = { - from: useWatchValue.from?? newForm.from, - to: useWatchValue.to?? newForm.to, - ic: useWatchValue.ic?? newForm.ic, - mw: useWatchValue.mw?? newForm.mw, - sc: useWatchValue.sc?? newForm.sc - } - setatomInputtingEntry(newe); - setIsCheckboxEnable(!!newe.from.match(/.*[a-zA-Z].*/)) - const to = get_by_entry(dedupedEntryMap)(newe) - if (typeof to=="string") { - setMessage(s => ({...s, alreadyExist: translationTree.confirm.alreadyExist[lang]})) - setAddButtonContent("update") - setatomMatchingEntry({...newe, to}) - } else { - setMessage(s => ({...s, alreadyExist: ""})) - setAddButtonContent("add") - setatomMatchingEntry(newe) - } - }, 100); - return ()=> clearTimeout(debounce) - }, [useWatchValue, allentries]) - const addEntry = (f: Form) => { - const {to, ...fkey} = f - const key = JSON.stringify(fkey) - const matchedEntry = atomMatchingEntry - const hasDeleted = dedupedEntryMap.delete(key) //削除しておくことで更新した項目が上に来る - dedupedEntryMap.set(key, to) - if (hasDeleted) { - console.log("updated ", matchedEntry) - console.log("to", `"${to}"`) - setMessage(s => ({...s, operation: translationTree.tooltip.operation.update(f.from, matchedEntry.to)(f.to)[lang] }) ) - setatomIndex(indexes => ({ - from: indexes.from, - to: indexes.to.map(index => { - ngramSegmentify(index.n, matchedEntry.to).forEach(ngram => index.index.delete(ngram)) - return ngramIndexing(index)(atomNextEntryId, to) - }) - })) - } else { - console.log("added:", f); - setMessage(s => ({...s, operation: translationTree.tooltip.operation.add(f.from, f.to)[lang] }) ) - setatomIndex(indexes => ngramIndexingEntry(atomNextEntryId, indexes)(f)) - } - setatomEntryIdDedupedMap(m => { - m.set(key, atomNextEntryId) - return new Map([...m.entries()]) - }) - setatomNextEntryId(i => i+1) - const array = Array.from(dedupedEntryMap) - const map = new Map(array) - setDedupedEntryMap(map) - const newEntries = array.map(k => keyvalueToEntry(k)) - setAllEntries(newEntries) - setEntriesForJSON(newEntries) - } - const deleteEntry = (f: Form) => { - const {to, ...fkey} = f - const key = JSON.stringify(fkey) - const hasDeleted = dedupedEntryMap.delete(key) - if (hasDeleted){ - console.log("deleted:", matchingEntryAtom) - setMessage(s => ({...s, operation: translationTree.tooltip.operation.delete(f.from, f.to)[lang] })) - setatomEntryIdDedupedMap(m => { - m.delete(key) - return new Map([...m.entries()]) - }) - } else { - console.log("failed to delete") - setMessage(s => ({...s, operation: translationTree.tooltip.fail("delete")[lang]})) - } + const handleClick = + // : MouseEventHandler = e => + (name: keyof TooltipOperationBools) => { + const entry: Entry = { + from: useWatchValue.from ?? newForm.from, + to: useWatchValue.to ?? newForm.to, + ic: useWatchValue.ic ?? newForm.ic, + mw: useWatchValue.mw ?? newForm.mw, + sc: useWatchValue.sc ?? newForm.sc, + } + setOpenTip(s => ({ ...defaultTooltipOperationBools, [name]: true })) + if (name == "add") { + addEntry(entry) + } else if (name == "delete") { + deleteEntry(entry) + } + } - const array = Array.from(dedupedEntryMap) - const map = new Map(array) - setDedupedEntryMap(map) - const newEntries = array.map(k => keyvalueToEntry(k)) - setAllEntries(newEntries) - setEntriesForJSON(newEntries) + const tooltip = function ToolTipWrap(name: keyof TooltipOperationBools) { + return ( + setOpenTip(s => ({ ...s, [name]: false }))} + disableHoverListener + placement="top" + title={ + name == "add" + ? addButtonContent == "update" + ? translationTree.tooltip.operation.update( + atomMatchingEntry.from, + atomMatchingEntry.to + )(atomInputtingEntry.to)[lang] + : translationTree.tooltip.operation.add(atomMatchingEntry.from, atomMatchingEntry.to)[ + lang + ] + : translationTree.tooltip.operation.delete( + atomMatchingEntry.from, + atomMatchingEntry.to + )[lang] + }> + + + ) + } - } - const checkbox = function CheckboxWrapper (name: keyof Form){return ( - ( - - )} - /> - } - label={entryAttributeReadable("en")(name)} - /> - )} + return ( + <> + + + {[checkbox("ic"), checkbox("sc"), checkbox("mw")]} + - const handleClick = - // : MouseEventHandler = e => - (name: keyof TooltipOperationBools) => { - const entry: Entry = { - from: useWatchValue.from?? newForm.from, - to: useWatchValue.to?? newForm.to, - ic: useWatchValue.ic?? newForm.ic, - mw: useWatchValue.mw?? newForm.mw, - sc: useWatchValue.sc?? newForm.sc - } - setOpenTip(s => ({...defaultTooltipOperationBools, [name]: true})) - if (name=="add"){ - addEntry(entry) - } else if (name=="delete"){ - deleteEntry(entry) - } - } - - const tooltip = function ToolTipWrap(name: keyof TooltipOperationBools) { - return( - setOpenTip(s => ({...s, [name]: false}) )} - disableHoverListener - placement="top" - title={ - name=="add" - ? (addButtonContent=="update" - ? translationTree.tooltip.operation.update(atomMatchingEntry.from, atomMatchingEntry.to)(atomInputtingEntry.to)[lang] - : translationTree.tooltip.operation.add(atomMatchingEntry.from, atomMatchingEntry.to)[lang]) - : (translationTree.tooltip.operation.delete(atomMatchingEntry.from, atomMatchingEntry.to)[lang]) - } - > - - - ) - } - - return( - <> - - - {[ - checkbox("ic"), - checkbox("sc"), - checkbox("mw"), - ]} - - - ( - - )}/> - ( - - )}/> - - - {[ - tooltip("add"), - tooltip("delete"), - ]} - - - - ) + ( + + )} + /> + ( + + )} + /> + + {[tooltip("add"), tooltip("delete")]} + + + + ) } const Output: FC = () => { - const [allentries] = useAtom(allentriesAtom) - return( - <> - ) + const [allentries] = useAtom(allentriesAtom) + return <> } type MessageBoxProps = { - alreadyExist: string - operation: string + alreadyExist: string + operation: string } const MessageBox: FC = ms => ( - - {Object.entries(ms).map(([k,m])=>( -
- {m} -
- ) )} -
+ + {Object.entries(ms).map(([k, m]) => ( +
{m}
+ ))} +
) -export default EntryForm \ No newline at end of file +export default EntryForm diff --git a/pages/TextRewriter/index.tsx b/pages/TextRewriter/index.tsx index 2023b86..3df30c4 100644 --- a/pages/TextRewriter/index.tsx +++ b/pages/TextRewriter/index.tsx @@ -3,6 +3,7 @@ import EntryForm from "./entryForm" import ParseForm from "../form" import JSONoutput from "./output" import Link from "next/link" +import InfoComponent from "./info" export default function Home() { return <>

@@ -12,6 +13,7 @@ export default function Home() { Text Rewriter Extension

+ diff --git a/pages/TextRewriter/info.module.css b/pages/TextRewriter/info.module.css new file mode 100644 index 0000000..76cbc5b --- /dev/null +++ b/pages/TextRewriter/info.module.css @@ -0,0 +1,4 @@ +.flex { + display: flex; + flex-wrap: wrap; +} diff --git a/pages/TextRewriter/info.tsx b/pages/TextRewriter/info.tsx new file mode 100644 index 0000000..19bf44d --- /dev/null +++ b/pages/TextRewriter/info.tsx @@ -0,0 +1,98 @@ +import { Lang, defaultLanguage, uilanguageAtom } from "@/ts/atom" +import { translationTree } from "@/ts/lang" +import { Autocomplete, Box, Button, TextField, ToggleButton } from "@mui/material" +import { useAtom } from "jotai" +import { FC, useState } from "react" +import ExpandMoreOutlinedIcon from "@mui/icons-material/ExpandMoreOutlined" +import KeyboardArrowRightOutlinedIcon from "@mui/icons-material/KeyboardArrowRightOutlined" +import { Controller, useForm, useWatch } from "react-hook-form" +import styles from "./info.module.css" + +type LangForm = { + lang: Lang +} + +const InfoComponent: FC = () => { + const [show, setShow] = useState(true) + const [lang, setLang] = useAtom(uilanguageAtom) + console.info("InfoComponent(), lang=", lang) + const { control, handleSubmit, formState, getValues } = useForm({ + defaultValues: { lang }, + }) + const useWatchValue = useWatch({ control }) + const [formValue, setFormValue] = useState(lang) + + const options = [...Object.entries(translationTree.form.languages)].map(([value, label]) => value) + + const onSubmit = (f: LangForm) => { + // const v: Lang = langs.find(o => o.label == f.lang)?.value ?? defaultLanguage + // setLang(v) + // setLang(f.lang) + } + return ( +
+ + { + // e.preventDefault() + // const v = getValues("lang") + setFormValue((va as Lang) ?? defaultLanguage) + setLang(va as Lang) + }} + renderOption={(ps, op) => ( + {`${op} ${ + translationTree.form.languages[op as Lang] + }`} + )} + renderInput={params => } + /> + {/* ( + { + e.preventDefault() + const v = getValues("lang") + setLang(v as Lang) + }} + renderOption={(ps, op) => ( + {`${op} ${ + translationTree.form.languages[op as Lang] + }`} + )} + renderInput={params => ( + + )} + /> + )}> */} + +
+ +
+
+ {show ? [...Object.entries(translationTree.info)].map(o =>

{o[1][lang]}

) : ""}{" "} +
+
+ ) +} + +export default InfoComponent diff --git a/pages/TextRewriter/output.module.css b/pages/TextRewriter/output.module.css new file mode 100644 index 0000000..ad061bd --- /dev/null +++ b/pages/TextRewriter/output.module.css @@ -0,0 +1,4 @@ + +.spacedButton { + margin-right: 5px; +} \ No newline at end of file diff --git a/pages/TextRewriter/output.tsx b/pages/TextRewriter/output.tsx index ec924b2..6700756 100644 --- a/pages/TextRewriter/output.tsx +++ b/pages/TextRewriter/output.tsx @@ -1,134 +1,150 @@ +import LaunchIcon from "@mui/icons-material/Launch" import { - Entry, - NgramIndexesForEntry, - addEntriesDeduping, - allentriesAtom, - dedupedEntryMapAtom, - defaultNgramIndexesForEntry, - deleteEntriesFromIndex, - entryIdDedupedMapAtom, - getDedupKey, - justDedupEntries, - nextEntryIdAtom, - ngramIndexesForEntryAtom, - ngramIndexingSequenceEntry, - sortAndStringify, - uilanguageAtom, + Entry, + NgramIndexesForEntry, + addEntriesDeduping, + allentriesAtom, + dedupedEntryMapAtom, + defaultNgramIndexesForEntry, + deleteEntriesFromIndex, + entryIdDedupedMapAtom, + getDedupKey, + justDedupEntries, + nextEntryIdAtom, + ngramIndexesForEntryAtom, + ngramIndexingSequenceEntry, + sortAndStringify, + uilanguageAtom, } from "@/ts/atom" import { translationTree } from "@/ts/lang" import { StringNumberTuple } from "@/ts/ts/text" import { copyToClipboard } from "@/ts/ts/util.list" -import { Button, InputAdornment, TextField, Tooltip } from "@mui/material" +import { Box, Button, InputAdornment, TextField, Tooltip, Typography } from "@mui/material" import { useAtom } from "jotai" import { FC, useEffect, useState } from "react" +import outputmodule from "./output.module.css" type Props = { - allentries: Entry[] + allentries: Entry[] } const JSONoutput: FC = () => { - const [lang] = useAtom(uilanguageAtom) - const [allentries, setAllEntries] = useAtom(allentriesAtom) - const [dedupedEntryMap, setDedupedEntryMap] = useAtom(dedupedEntryMapAtom) - const [atomNextEntryId, setatomNextEntryId] = useAtom(nextEntryIdAtom) - const [atomIndex, setatomIndex] = useAtom(ngramIndexesForEntryAtom) - const [atomEntryIdDedupedMap, setatomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) + const [lang] = useAtom(uilanguageAtom) + const [allentries, setAllEntries] = useAtom(allentriesAtom) + const [dedupedEntryMap, setDedupedEntryMap] = useAtom(dedupedEntryMapAtom) + const [atomNextEntryId, setatomNextEntryId] = useAtom(nextEntryIdAtom) + const [atomIndex, setatomIndex] = useAtom(ngramIndexesForEntryAtom) + const [atomEntryIdDedupedMap, setatomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) - const [messages, setMessages] = useState({ - parseError: "", - }) - const [output, setOutput] = useState(sortAndStringify(allentries)) - type Buttons = { - copy: boolean - append: boolean - initialize: boolean - } - const [openTip, setOpenTip] = useState({ - copy: false, - append: false, - initialize: false, - }) + const [messages, setMessages] = useState({ + parseError: "", + }) + const [output, setOutput] = useState(sortAndStringify(allentries)) + type Buttons = { + copy: boolean + append: boolean + initialize: boolean + } + const [openTip, setOpenTip] = useState({ + copy: false, + append: false, + initialize: false, + }) - useEffect(() => { - const t0 = performance.now() - setOutput(sortAndStringify(allentries)) - const t1 = performance.now() - console.log(`sort and output of ${allentries.length} entries took ${t1 - t0}ms`) - setMessages(s => ({ ...s, parseError: "" })) - }, [allentries]) - const handleChange = (e: React.ChangeEvent) => { - setOutput(e.target.value) - } - const handleClick = (attr: keyof Buttons) => () => { - if (attr == "copy") { - copyToClipboard(output) - } else { - importClick(attr)() - } - setOpenTip(s => ({ ...s, [attr]: true })) - } - type ImportType = "append" | "initialize" - const importClick = (importType: ImportType) => () => { - try { - const res: Entry[] = JSON.parse(output) - console.log(res) - if (importType == "initialize") { - const { entries, dedupedMap } = justDedupEntries(res) - console.log("imported entries:", entries) - setAllEntries(entries) - setDedupedEntryMap(dedupedMap) + useEffect(() => { + const t0 = performance.now() + setOutput(sortAndStringify(allentries)) + const t1 = performance.now() + console.log(`sort and output of ${allentries.length} entries took ${t1 - t0}ms`) + setMessages(s => ({ ...s, parseError: "" })) + }, [allentries]) + const handleChange = (e: React.ChangeEvent) => { + setOutput(e.target.value) + } + const handleClick = (attr: keyof Buttons) => () => { + if (attr == "copy") { + copyToClipboard(output) + } else { + importClick(attr)() + } + setOpenTip(s => ({ ...s, [attr]: true })) + } + type ImportType = "append" | "initialize" + const importClick = (importType: ImportType) => () => { + try { + const res: Entry[] = JSON.parse(output) + console.log(res) + if (importType == "initialize") { + const { entries, dedupedMap } = justDedupEntries(res) + console.log("imported entries:", entries) + setAllEntries(entries) + setDedupedEntryMap(dedupedMap) - setatomEntryIdDedupedMap(m => new Map(entries.map((e, i) => [getDedupKey(e), i]))) - setatomIndex(indexes => ngramIndexingSequenceEntry(0, defaultNgramIndexesForEntry)(entries)) - setatomNextEntryId(entries.length) - } else { - const { entries, dedupedMap } = addEntriesDeduping(dedupedEntryMap)(res) - console.log("imported entries:", entries) - setDedupedEntryMap(dedupedMap) - setAllEntries(entries) + setatomEntryIdDedupedMap(m => new Map(entries.map((e, i) => [getDedupKey(e), i]))) + setatomIndex(indexes => ngramIndexingSequenceEntry(0, defaultNgramIndexesForEntry)(entries)) + setatomNextEntryId(entries.length) + } else { + const { entries, dedupedMap } = addEntriesDeduping(dedupedEntryMap)(res) + console.log("imported entries:", entries) + setDedupedEntryMap(dedupedMap) + setAllEntries(entries) - const newEntryIdMapSrc = [...atomEntryIdDedupedMap.entries()] - const newEntryMapSpreaded = entries.map( - (e, i) => [getDedupKey(e), atomNextEntryId + i] as StringNumberTuple - ) - const newEntryMap = new Map(newEntryMapSpreaded) - const spreaded = [...atomEntryIdDedupedMap.entries()] - const duplicateEntryIds = spreaded - .filter(([key, id]) => newEntryMap.get(key) !== undefined) - .map(([key, id]) => id) - setatomEntryIdDedupedMap(m => new Map([...spreaded, ...newEntryMapSpreaded])) - setatomIndex(indexes => ({ - from: indexes.from.map(index => deleteEntriesFromIndex(duplicateEntryIds)(index)), - to: indexes.to.map(index => deleteEntriesFromIndex(duplicateEntryIds)(index)), - })) - setatomNextEntryId(id => id + entries.length) - } - } catch { - setMessages(s => ({ ...s, parseError: translationTree.error.parse[lang] })) - } - } - const tooltip = function ToolTipWrap(name: keyof Buttons) { - return ( - setOpenTip(s => ({ ...s, [name]: false }))} - disableHoverListener - placement="top" - title={`${name}!`}> - - - ) - } - return ( -
-

JSON({allentries.length})

-
{allentries.length >= 0 ? "" : ""}
-
- {(["copy", "append", "initialize"] as (keyof Buttons)[]).map(name => tooltip(name))} -
- {/* [getDedupKey(e), atomNextEntryId + i] as StringNumberTuple + ) + const newEntryMap = new Map(newEntryMapSpreaded) + const spreaded = [...atomEntryIdDedupedMap.entries()] + const duplicateEntryIds = spreaded + .filter(([key, id]) => newEntryMap.get(key) !== undefined) + .map(([key, id]) => id) + setatomEntryIdDedupedMap(m => new Map([...spreaded, ...newEntryMapSpreaded])) + setatomIndex(indexes => ({ + from: indexes.from.map(index => deleteEntriesFromIndex(duplicateEntryIds)(index)), + to: indexes.to.map(index => deleteEntriesFromIndex(duplicateEntryIds)(index)), + })) + setatomNextEntryId(id => id + entries.length) + } + } catch { + setMessages(s => ({ ...s, parseError: translationTree.error.parse[lang] })) + } + } + const tooltip = function ToolTipWrap(name: keyof Buttons) { + return ( + setOpenTip(s => ({ ...s, [name]: false }))} + disableHoverListener + placement="top" + title={`${name}!`}> + + + ) + } + return ( +
+ JSON({allentries.length}) +
+ {(["copy", "append", "initialize"] as (keyof Buttons)[]).map(name => tooltip(name))} + {/* + example🡥 + */} + + + +
+
+ {/* setOpenTip(s => ({...s, copy: false}))} @@ -141,7 +157,7 @@ const JSONoutput: FC = () => { > copy - + { import(initialize) */} -
- {Object.entries(messages).map(([k, v]) => ( -
{v}
- ))} -
- - // setOpenTip(false)} - // disableHoverListener - // placement="left" - // title="copied!" - // > - // +
+ {Object.entries(messages).map(([k, v]) => ( +
{v}
+ ))} +
+ + // setOpenTip(false)} + // disableHoverListener + // placement="left" + // title="copied!" + // > + // - // - // - // }} - // disabled - onChange={handleChange} - /> -
- ) + //
+ // + // }} + // disabled + onChange={handleChange} + /> +
+ ) } export default JSONoutput diff --git a/pages/TextRewriter/recycleForm.tsx b/pages/TextRewriter/recycleForm.tsx index 2c611f0..da5ba68 100644 --- a/pages/TextRewriter/recycleForm.tsx +++ b/pages/TextRewriter/recycleForm.tsx @@ -1,24 +1,24 @@ import { - Entry, - TooltipOperationBools, - allentriesAtom, - dedupedEntryMapAtom, - defaultTooltipOperationBools, - deleteEntriesFromIndex, - entryAttributeReadable, - entryIdDedupedMapAtom, - get_by_entry, - inputtingEntryAtom, - keyvalueToEntry, - matchingEntryAtom, - modifyNgramData, - newEntryAtom, - nextEntryIdAtom, - ngramIndexesForEntryAtom, - ngramIndexingEntry, - searchDelayAtom, - uilanguageAtom, - updateIndexList, + Entry, + TooltipOperationBools, + allentriesAtom, + dedupedEntryMapAtom, + defaultTooltipOperationBools, + deleteEntriesFromIndex, + entryAttributeReadable, + entryIdDedupedMapAtom, + get_by_entry, + inputtingEntryAtom, + keyvalueToEntry, + matchingEntryAtom, + modifyNgramData, + newEntryAtom, + nextEntryIdAtom, + ngramIndexesForEntryAtom, + ngramIndexingEntry, + searchDelayAtom, + uilanguageAtom, + updateIndexList, } from "@/ts/atom" import { useDebounce } from "@/ts/hook" import { translationTree } from "@/ts/lang" @@ -31,311 +31,314 @@ import { FC, useEffect, useState } from "react" import { Controller, useForm, useWatch } from "react-hook-form" type RecycleFormProp = { - defaultValues: Entry - hideMe?: () => void + defaultValues: Entry + hideMe?: () => void } const RecycleForm: FC = prop => { - const [message, setMessage] = useState({ - alreadyExist: "", - operation: "", - }) - const [lang] = useAtom(uilanguageAtom) - const [newForm, setNewForm] = useAtom(newEntryAtom) - const readableAttr = (k: keyof Entry) => entryAttributeReadable(lang)(k) - const [allentries, setAllEntries] = useAtom(allentriesAtom) - const [atomMatchingEntry, setatomMatchingEntry] = useAtom(matchingEntryAtom) - const [atomInputtingEntry, setatomInputtingEntry] = useAtom(inputtingEntryAtom) - const [atomIndex, setatomIndex] = useAtom(ngramIndexesForEntryAtom) - const [atomNextEntryId, setatomNextEntryId] = useAtom(nextEntryIdAtom) - const [atomEntryIdDedupedMap, setatomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) - const [atomSearchDelay] = useAtom(searchDelayAtom) + const [message, setMessage] = useState({ + alreadyExist: "", + operation: "", + }) + const [lang] = useAtom(uilanguageAtom) + const [newForm, setNewForm] = useAtom(newEntryAtom) + const readableAttr = (k: keyof Entry) => entryAttributeReadable(lang)(k) + const [allentries, setAllEntries] = useAtom(allentriesAtom) + const [atomMatchingEntry, setatomMatchingEntry] = useAtom(matchingEntryAtom) + const [atomInputtingEntry, setatomInputtingEntry] = useAtom(inputtingEntryAtom) + const [atomIndex, setatomIndex] = useAtom(ngramIndexesForEntryAtom) + const [atomNextEntryId, setatomNextEntryId] = useAtom(nextEntryIdAtom) + const [atomEntryIdDedupedMap, setatomEntryIdDedupedMap] = useAtom(entryIdDedupedMapAtom) + const [atomSearchDelay] = useAtom(searchDelayAtom) - const [entriesForJSON, setEntriesForJSON] = useState(allentries) - type Form = Entry - const { control, handleSubmit, formState } = useForm
({ - defaultValues: prop.defaultValues, - }) - const realtimeUseWatchValue = useWatch({ control }) - const [dedupedEntryMap, setDedupedEntryMap] = useAtom(dedupedEntryMapAtom) - const [isCheckboxEnable, setIsCheckboxEnable] = useState(false) - type AddButtonContent = "add" | "update" - const [addButtonContent, setAddButtonContent] = useState("add") - const [openTip, setOpenTip] = useState(defaultTooltipOperationBools) - const useWatchValue = useDebounce(atomSearchDelay, realtimeUseWatchValue) + const [entriesForJSON, setEntriesForJSON] = useState(allentries) + type Form = Entry + const { control, handleSubmit, formState } = useForm({ + defaultValues: prop.defaultValues, + }) + const realtimeUseWatchValue = useWatch({ control }) + const [dedupedEntryMap, setDedupedEntryMap] = useAtom(dedupedEntryMapAtom) + const [isCheckboxEnable, setIsCheckboxEnable] = useState(false) + type AddButtonContent = "add" | "update" + const [addButtonContent, setAddButtonContent] = useState("add") + const [openTip, setOpenTip] = useState(defaultTooltipOperationBools) + const useWatchValue = useDebounce(atomSearchDelay, realtimeUseWatchValue) - useEffect(() => { - const newe: Entry = { - from: useWatchValue.from ?? newForm.from, - to: useWatchValue.to ?? newForm.to, - ic: useWatchValue.ic ?? newForm.ic, - mw: useWatchValue.mw ?? newForm.mw, - sc: useWatchValue.sc ?? newForm.sc, - } - setatomInputtingEntry(newe) - setIsCheckboxEnable(!!newe.from.match(/.*[a-zA-Z].*/)) - const to = get_by_entry(dedupedEntryMap)(newe) - if (typeof to == "string") { - setMessage(s => ({ ...s, alreadyExist: translationTree.confirm.alreadyExist[lang] })) - setAddButtonContent("update") - setatomMatchingEntry({ ...newe, to }) - } else { - setMessage(s => ({ ...s, alreadyExist: "" })) - setAddButtonContent("add") - setatomMatchingEntry(newe) - } - }, [useWatchValue, allentries]) + useEffect(() => { + const newe: Entry = { + from: useWatchValue.from ?? newForm.from, + to: useWatchValue.to ?? newForm.to, + ic: useWatchValue.ic ?? newForm.ic, + mw: useWatchValue.mw ?? newForm.mw, + sc: useWatchValue.sc ?? newForm.sc, + } + setatomInputtingEntry(newe) + setIsCheckboxEnable(!!newe.from.match(/.*[a-zA-Z].*/)) + const to = get_by_entry(dedupedEntryMap)(newe) + if (typeof to == "string") { + setMessage(s => ({ ...s, alreadyExist: translationTree.confirm.alreadyExist[lang] })) + setAddButtonContent("update") + setatomMatchingEntry({ ...newe, to }) + } else { + setMessage(s => ({ ...s, alreadyExist: "" })) + setAddButtonContent("add") + setatomMatchingEntry(newe) + } + }, [useWatchValue, allentries]) - const addEntry = (f: Form) => { - // console.log("addEntry called") - // console.log("atomNextEntryId", atomNextEntryId) - const { to, ...fkey } = f - const key = JSON.stringify(fkey) - const matchedEntry = atomMatchingEntry - const hasDeleted = dedupedEntryMap.delete(key) //削除しておくことで更新した項目が上に来る - dedupedEntryMap.set(key, to) - if (hasDeleted) { - console.log("updated ", matchedEntry) - console.log("to", `"${to}"`) - setMessage(s => ({ - ...s, - operation: translationTree.tooltip.operation.update(f.from, matchedEntry.to)(f.to)[lang], - })) - console.log("atomEntryIdDedupedMap:", atomEntryIdDedupedMap) + const addEntry = (f: Form) => { + // console.log("addEntry called") + // console.log("atomNextEntryId", atomNextEntryId) + const { to, ...fkey } = f + const key = JSON.stringify(fkey) + const matchedEntry = atomMatchingEntry + const hasDeleted = dedupedEntryMap.delete(key) //削除しておくことで更新した項目が上に来る + dedupedEntryMap.set(key, to) + if (hasDeleted) { + console.log("updated ", matchedEntry) + console.log("to", `"${to}"`) + setMessage(s => ({ + ...s, + operation: translationTree.tooltip.operation.update(f.from, matchedEntry.to)(f.to)[lang], + })) + console.log("atomEntryIdDedupedMap:", atomEntryIdDedupedMap) - setatomIndex( - indexes => - caseUndefined(atomEntryIdDedupedMap.get(key))( - targetid => ({ - from: indexes.from.map(index => - modifyNgramData( - data => - new Map( - list_modify([...data.entries()])( - ([id, posi]) => id == targetid, - ([id, positions]) => [targetid, positions] - ) - ) - )(index) - ), - to: indexes.to.map(index => - ngramIndexing(deleteEntriesFromIndex([targetid])(index))(atomNextEntryId, to) - ), - }), - indexes - ) - // const updater = updateIndexList(key)(indexes, matchedEntry, atomEntryIdDedupedMap) - // return { - // from: updater("from"), - // to: updater("to", { - // id: atomNextEntryId, - // text: to, - // }), - // } - ) - } else { - console.log("added:", f) - setMessage(s => ({ - ...s, - operation: translationTree.tooltip.operation.add(f.from, f.to)[lang], - })) - setatomIndex(indexes => ngramIndexingEntry(atomNextEntryId, indexes)(f)) - } - setatomEntryIdDedupedMap(m => { - m.set(key, atomNextEntryId) - return new Map([...m.entries()]) - }) - setatomNextEntryId(i => i + 1) - const array = Array.from(dedupedEntryMap) - const map = new Map(array) - setDedupedEntryMap(map) - const newEntries = array.map(k => keyvalueToEntry(k)) - setAllEntries(newEntries) - setEntriesForJSON(newEntries) - } + setatomIndex( + indexes => + caseUndefined(atomEntryIdDedupedMap.get(key))( + targetid => ({ + from: indexes.from.map(index => + modifyNgramData( + data => { + data.delete(targetid) + return data + } + // new Map( + // list_modify([...data.entries()])( + // ([id, posi]) => id == targetid, + // ([id, positions]) => [targetid, positions] + // ) + // ) + )(index) + ), + to: indexes.to.map(index => + ngramIndexing(deleteEntriesFromIndex([targetid])(index))(atomNextEntryId, to) + ), + }), + indexes + ) + // const updater = updateIndexList(key)(indexes, matchedEntry, atomEntryIdDedupedMap) + // return { + // from: updater("from"), + // to: updater("to", { + // id: atomNextEntryId, + // text: to, + // }), + // } + ) + } else { + console.log("added:", f) + setMessage(s => ({ + ...s, + operation: translationTree.tooltip.operation.add(f.from, f.to)[lang], + })) + } + setatomIndex(indexes => ngramIndexingEntry(atomNextEntryId, indexes)(f)) + setatomEntryIdDedupedMap(m => { + m.set(key, atomNextEntryId) + return new Map([...m.entries()]) + }) + setatomNextEntryId(i => i + 1) + const array = Array.from(dedupedEntryMap) + const map = new Map(array) + setDedupedEntryMap(map) + const newEntries = array.map(k => keyvalueToEntry(k)) + setAllEntries(newEntries) + setEntriesForJSON(newEntries) + } - const deleteEntry = (f: Form) => { - const { to, ...fkey } = f - const key = JSON.stringify(fkey) - const hasDeleted = dedupedEntryMap.delete(key) - if (hasDeleted) { - console.log("deleted:", matchingEntryAtom) - setMessage(s => ({ - ...s, - operation: translationTree.tooltip.operation.delete(f.from, f.to)[lang], - })) - setatomEntryIdDedupedMap(m => { - m.delete(key) - return new Map([...m.entries()]) - }) - setatomIndex(indexes => { - const upd = updateIndexList(key)(indexes, atomMatchingEntry, atomEntryIdDedupedMap) - return { - from: upd("from"), - to: upd("to"), - } - }) - } else { - console.log("failed to delete") - setMessage(s => ({ ...s, operation: translationTree.tooltip.fail("delete")[lang] })) - } + const deleteEntry = (f: Form) => { + const { to, ...fkey } = f + const key = JSON.stringify(fkey) + const hasDeleted = dedupedEntryMap.delete(key) + if (hasDeleted) { + console.log("deleted:", matchingEntryAtom) + setMessage(s => ({ + ...s, + operation: translationTree.tooltip.operation.delete(f.from, f.to)[lang], + })) + setatomEntryIdDedupedMap(m => { + m.delete(key) + return new Map([...m.entries()]) + }) + setatomIndex(indexes => { + const upd = updateIndexList(key)(indexes, atomMatchingEntry, atomEntryIdDedupedMap) + return { + from: upd("from"), + to: upd("to"), + } + }) + } else { + console.log("failed to delete") + setMessage(s => ({ ...s, operation: translationTree.tooltip.fail("delete")[lang] })) + } - const array = Array.from(dedupedEntryMap) - const map = new Map(array) - setDedupedEntryMap(map) - const newEntries = array.map(k => keyvalueToEntry(k)) - setAllEntries(newEntries) - setEntriesForJSON(newEntries) - } + const array = Array.from(dedupedEntryMap) + const map = new Map(array) + setDedupedEntryMap(map) + const newEntries = array.map(k => keyvalueToEntry(k)) + setAllEntries(newEntries) + setEntriesForJSON(newEntries) + } - const checkbox = function CheckboxWrapper(name: keyof Form) { - return ( - ( - - )} - /> - } - label={entryAttributeReadable("en")(name)} - /> - ) - } + const checkbox = function CheckboxWrapper(name: keyof Form) { + return ( + ( + + )} + /> + } + label={entryAttributeReadable(lang)(name)} + /> + ) + } - const handleClick = - // : MouseEventHandler = e => - (name: keyof TooltipOperationBools) => { - const entry: Entry = { - from: useWatchValue.from ?? newForm.from, - to: useWatchValue.to ?? newForm.to, - ic: useWatchValue.ic ?? newForm.ic, - mw: useWatchValue.mw ?? newForm.mw, - sc: useWatchValue.sc ?? newForm.sc, - } - setOpenTip(s => ({ ...defaultTooltipOperationBools, [name]: true })) - if (name == "add") { - addEntry(entry) - } else if (name == "delete") { - deleteEntry(entry) - } - } + const handleClick = + // : MouseEventHandler = e => + (name: keyof TooltipOperationBools) => { + const entry: Entry = { + from: useWatchValue.from ?? newForm.from, + to: useWatchValue.to ?? newForm.to, + ic: useWatchValue.ic ?? newForm.ic, + mw: useWatchValue.mw ?? newForm.mw, + sc: useWatchValue.sc ?? newForm.sc, + } + setOpenTip(s => ({ ...defaultTooltipOperationBools, [name]: true })) + if (name == "add") { + addEntry(entry) + } else if (name == "delete") { + deleteEntry(entry) + } + } - const tooltip = function ToolTipWrap(name: keyof TooltipOperationBools) { - return ( - setOpenTip(s => ({ ...s, [name]: false }))} - disableHoverListener - placement="top" - title={ - name == "add" - ? addButtonContent == "update" - ? translationTree.tooltip.operation.update( - atomMatchingEntry.from, - atomMatchingEntry.to - )(atomInputtingEntry.to)[lang] - : translationTree.tooltip.operation.add(atomMatchingEntry.from, atomMatchingEntry.to)[ - lang - ] - : translationTree.tooltip.operation.delete( - atomMatchingEntry.from, - atomMatchingEntry.to - )[lang] - }> - - - ) - } + const tooltip = function ToolTipWrap(name: keyof TooltipOperationBools) { + return ( + setOpenTip(s => ({ ...s, [name]: false }))} + disableHoverListener + placement="top" + title={ + name == "add" + ? addButtonContent == "update" + ? translationTree.tooltip.operation.update( + atomMatchingEntry.from, + atomMatchingEntry.to + )(atomInputtingEntry.to)[lang] + : translationTree.tooltip.operation.add(atomMatchingEntry.from, atomMatchingEntry.to)[ + lang + ] + : translationTree.tooltip.operation.delete( + atomMatchingEntry.from, + atomMatchingEntry.to + )[lang] + }> + + + ) + } - return ( - <> - - - {[checkbox("ic"), checkbox("sc"), checkbox("mw")]} - + return ( + <> + + + {[checkbox("ic"), checkbox("sc"), checkbox("mw")]} + - ( - - )} - /> - ( - - )} - /> + ( + + )} + /> + ( + + )} + /> - - {[tooltip("add"), tooltip("delete")]} - - - - ) + + {[tooltip("add"), tooltip("delete")]} + + + + ) } const Output: FC = () => { - const [allentries] = useAtom(allentriesAtom) - return <> + const [allentries] = useAtom(allentriesAtom) + return <> } type MessageBoxProps = { - alreadyExist: string - operation: string + alreadyExist: string + operation: string } const MessageBox: FC = ms => ( - - {Object.entries(ms).map(([k, m]) => ( -
{m}
- ))} -
+ + {Object.entries(ms).map(([k, m]) => ( +
{m}
+ ))} +
) export default RecycleForm diff --git a/pages/TextRewriter/searchForm.tsx b/pages/TextRewriter/searchForm.tsx index 9474ce8..3851694 100644 --- a/pages/TextRewriter/searchForm.tsx +++ b/pages/TextRewriter/searchForm.tsx @@ -8,40 +8,39 @@ import { FC, useEffect, useState } from "react" import { Controller, useForm, useWatch } from "react-hook-form" const SearchForm: FC = () => { - const [atomSearchDelay, setAtomSearchDelay] = useAtom(searchDelayAtom) - const [atomUIlanguage] = useAtom(uilanguageAtom) + const [atomSearchDelay, setAtomSearchDelay] = useAtom(searchDelayAtom) + const [atomUIlanguage] = useAtom(uilanguageAtom) - const { control, register } = useForm({ - defaultValues: defaultSimilarForm, - }) - const useWatchValue = useWatch({ control }) - const useWatchValueCopy = useDebounce(300, useWatchValue) - const [focusing, setFocusing] = useState(false) + const { control, register } = useForm({ + defaultValues: defaultSimilarForm, + }) + const useWatchValue = useWatch({ control }) + const [focusing, setFocusing] = useState(false) - useEffect(() => { - setAtomSearchDelay(useWatchValue.delay ?? defaultSimilarForm.delay) - }, [useWatchValueCopy]) + useEffect(() => { + setAtomSearchDelay(useWatchValue.delay ?? defaultSimilarForm.delay) + }, [useWatchValue]) - return ( - - ( - setFocusing(f => !f)} - onBlur={() => setFocusing(f => !f)} - helperText={focusing && translationTree.searchDelay.help[atomUIlanguage]} - /> - )} - /> - - ) + return ( + + ( + setFocusing(f => !f)} + onBlur={() => setFocusing(f => !f)} + helperText={focusing && translationTree.searchDelay.help[atomUIlanguage]} + /> + )} + /> + + ) } export default SearchForm diff --git a/pages/index.tsx b/pages/index.tsx index b59d556..a17a34d 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -8,6 +8,7 @@ export default function Home() { > <>

transformat

+

translate between formats.

<>

JSON formmater for Text Rewriter Extension

diff --git a/styles/Home.module.css b/styles/Home.module.css deleted file mode 100644 index e69de29..0000000 diff --git a/ts/atom.ts b/ts/atom.ts index 24fb45b..d7c7275 100644 --- a/ts/atom.ts +++ b/ts/atom.ts @@ -1,67 +1,69 @@ import { atom, useAtom } from "jotai" import { - consoler, - groupByForList, - groupByForSet, - objectSortByASCIIsort, - sortFnAsFarFromZero, - sortManyTimes, - sortManyTimesByGrouping, + consoler, + groupByForList, + groupByForSet, + objectSortByASCIIsort, + sortFnAsFarFromZero, + sortManyTimes, + sortManyTimesByGrouping, } from "./ts/util.list" import { listtools, objdo, objectToJsonRelaxed, strlisttools } from "./ts/object" import { - NgramIndex, - NgramIndexData, - emptyNgramIndex, - ngramIndexing, - ngramIndexingSequence, - ngramSegmentify, + NgramIndex, + NgramIndexData, + emptyNgramIndex, + ngramIndexing, + ngramIndexingSequence, + ngramSegmentify, } from "./ts/text" import { caseUndefined, fallback } from "./ts/map" export type Entry = { - from: string - to: string - ic: boolean - mw: boolean - sc: boolean + from: string + to: string + ic: boolean + mw: boolean + sc: boolean } -const langs = ["ja", "en"] as const -type Lang = (typeof langs)[number] -export const searchDelayAtom = atom(300) +export const langs = ["ja", "en"] as const +export type Lang = (typeof langs)[number] +export const defaultLanguage = "ja" +export const defaultSearchDelay = 700 +export const searchDelayAtom = atom(defaultSearchDelay) const entryAttributeTranslations: Record> = { - from: { - en: "from", - ja: "置換対象", - }, - ic: { - en: "ignore case", - ja: "大文字小文字を無視する", - }, - mw: { - en: "match word", - ja: "単語のみ", - }, - sc: { - en: "smart case", - ja: "賢い大小文字", - }, - to: { - en: "to", - ja: "置換結果", - }, + from: { + en: "from", + ja: "置換対象", + }, + ic: { + en: "ignore case", + ja: "大文字小文字を無視する", + }, + mw: { + en: "match word", + ja: "単語のみ", + }, + sc: { + en: "smart case", + ja: "賢い大小文字", + }, + to: { + en: "to", + ja: "置換結果", + }, } export const entryAttributeReadable = - (lang: Lang) => - (attr: keyof Entry): string => - entryAttributeTranslations[attr][lang] + (lang: Lang) => + (attr: keyof Entry): string => + entryAttributeTranslations[attr][lang] export const isEqualEntry = (ej: Entry, ek: Entry) => - Object.keys(ej).every(k => ej[k as keyof Entry] == ek[k as keyof Entry]) + Object.keys(ej).every(k => ej[k as keyof Entry] == ek[k as keyof Entry]) export const uilanguageAtom = atom("ja") export const allentriesAtom = atom([]) export const nextEntryIdAtom = atom(0) export type TextLengthMaps = { - from: Map - to: Map + from: Map + to: Map } export type IdEntryMap = Map @@ -69,22 +71,22 @@ export type IdEntryMapUpdate = (v: V) => V export const IdEntryMapAtom = atom(new Map()) export type NgramIndexesForEntry = { - from: NgramIndex[] - to: NgramIndex[] + from: NgramIndex[] + to: NgramIndex[] } export const defaultNgramIndexesForEntry: NgramIndexesForEntry = { - from: [emptyNgramIndex(1), emptyNgramIndex(2)], - to: [emptyNgramIndex(1), emptyNgramIndex(2)], + from: [emptyNgramIndex(1), emptyNgramIndex(2)], + to: [emptyNgramIndex(1), emptyNgramIndex(2)], } export const ngramIndexesForEntryAtom = atom(defaultNgramIndexesForEntry) export const defaultEntry: Entry = { - from: "", - ic: false, - mw: false, - sc: false, - to: "", + from: "", + ic: false, + mw: false, + sc: false, + to: "", } export type EntryBoolKey = "ic" | "mw" | "sc" export const inputtingEntryAtom = atom(defaultEntry) @@ -93,218 +95,218 @@ export const newEntryAtom = atom(defaultEntry) export const dedupedEntryMapAtom = atom>(new Map()) export const get_by_entry = (dedupedMap: Map) => (e: Entry) => { - let { to, ...key } = e - const stringified = JSON.stringify(key) - const value = dedupedMap.get(stringified) - return value + let { to, ...key } = e + const stringified = JSON.stringify(key) + const value = dedupedMap.get(stringified) + return value } export const keyvalueToEntry = ([k, to]: [string, string]): Entry => - ({ ...JSON.parse(k), to } as Entry) + ({ ...JSON.parse(k), to } as Entry) export const dedupEntries = (es: Entry[]) => - Array.from(new Set(es.map(e => JSON.stringify(e)))).map(str => JSON.parse(str) as Entry) + Array.from(new Set(es.map(e => JSON.stringify(e)))).map(str => JSON.parse(str) as Entry) export const addEntriesDeduping = (dedupedEntryMap: Map) => (entries: Entry[]) => { - const deduped = new Map([ - ...dedupedEntryMap, - ...entries.map(f => { - const { to, ...fkey } = f - const key = JSON.stringify(fkey) - return [key, to] as [string, string] - }), - ]) - const array = Array.from(deduped) - const newEntries = array.map(k => keyvalueToEntry(k)) - return { - entries: newEntries, - dedupedMap: deduped, - } + const deduped = new Map([ + ...dedupedEntryMap, + ...entries.map(f => { + const { to, ...fkey } = f + const key = JSON.stringify(fkey) + return [key, to] as [string, string] + }), + ]) + const array = Array.from(deduped) + const newEntries = array.map(k => keyvalueToEntry(k)) + return { + entries: newEntries, + dedupedMap: deduped, + } } export const justDedupEntries = (entries: Entry[]) => { - const deduped = new Map( - entries.map(f => { - const { to, ...fkey } = f - const key = JSON.stringify(fkey) - return [key, to] as [string, string] - }) - ) - const array = Array.from(deduped) - const e = array.map(k => keyvalueToEntry(k)) - return { - entries: e, - dedupedMap: deduped, - } + const deduped = new Map( + entries.map(f => { + const { to, ...fkey } = f + const key = JSON.stringify(fkey) + return [key, to] as [string, string] + }) + ) + const array = Array.from(deduped) + const e = array.map(k => keyvalueToEntry(k)) + return { + entries: e, + dedupedMap: deduped, + } } const entryBoolApplyRangePoint = (e: Entry): number => - (e.mw ? 0 : 4) + (e.ic ? 2 : 0) + (e.sc ? 0 : 1) + 0 + (e.mw ? 0 : 4) + (e.ic ? 2 : 0) + (e.sc ? 0 : 1) + 0 export const sortAndStringify = (entries: Entry[]) => { - const sorted = [...groupByForList(entries)(e => e.from.length).entries()] - .map(([n, es]): [number, Entry[]] => [ - n, - [...groupByForList(es)(e => e.from).entries()] - .map(([fromtext, es]): [string, Entry[]] => [ - fromtext, - es.sort( - sortFnAsFarFromZero( - 0, - n - )((a, b, i) => entryBoolApplyRangePoint(a) - entryBoolApplyRangePoint(b)) - ), - ]) - .sort( - sortFnAsFarFromZero( - 0, - n - )(([a, as], [b, bs], i) => (a.codePointAt(i) ?? 0) - (b.codePointAt(i) ?? 0)) - ) - .flatMap(([text, es]) => es), - // es.sort(sortFnAsFarFromZero(0, n)( - // (a, b, i)=> ((a.from.codePointAt(i)??0) - (b.from.codePointAt(i)??0)) - // )) - ]) - .sort(([n, _], [m, __]) => m - n) - .flatMap(([n, es]) => es) - const output: string = strlisttools.intersperse_reduce(",\n")( - sorted.map(entry => - objectToJsonRelaxed(entry)({ - propColon: { after: " " }, - entryComma: { after: " " }, - }) - ) - ) - // return JSON.stringify(sorted) - return "[" + output + "]" + const sorted = [...groupByForList(entries)(e => e.from.length).entries()] + .map(([n, es]): [number, Entry[]] => [ + n, + [...groupByForList(es)(e => e.from).entries()] + .map(([fromtext, es]): [string, Entry[]] => [ + fromtext, + es.sort( + sortFnAsFarFromZero( + 0, + n + )((a, b, i) => entryBoolApplyRangePoint(a) - entryBoolApplyRangePoint(b)) + ), + ]) + .sort( + sortFnAsFarFromZero( + 0, + n + )(([a, as], [b, bs], i) => (a.codePointAt(i) ?? 0) - (b.codePointAt(i) ?? 0)) + ) + .flatMap(([text, es]) => es), + // es.sort(sortFnAsFarFromZero(0, n)( + // (a, b, i)=> ((a.from.codePointAt(i)??0) - (b.from.codePointAt(i)??0)) + // )) + ]) + .sort(([n, _], [m, __]) => m - n) + .flatMap(([n, es]) => es) + const output: string = strlisttools.intersperse_reduce(",\n")( + sorted.map(entry => + objectToJsonRelaxed(entry)({ + propColon: { after: " " }, + entryComma: { after: " " }, + }) + ) + ) + // return JSON.stringify(sorted) + return "[" + output + "]" } export type TooltipOperationBools = { - add: boolean - delete: boolean + add: boolean + delete: boolean } export const defaultTooltipOperationBools: TooltipOperationBools = { - add: false, - delete: false, + add: false, + delete: false, } export type FromToObj = { - from: string - to: string + from: string + to: string } export type EntryIdDedupedMap = Map export const entryIdDedupedMapAtom = atom(new Map()) export const ngramIndexingSequenceEntry = - (startId: number, indexes: NgramIndexesForEntry) => - (entries: T[]): NgramIndexesForEntry => { - // console.log("ngramIndexingSequenceEntry, entries:", entries) - const calc = (key: keyof FromToObj) => - indexes[key].map(index => ngramIndexingSequence(startId, index)(entries.map(e => e[key]))) - return { - from: calc("from"), - to: calc("to"), - } as NgramIndexesForEntry - } + (startId: number, indexes: NgramIndexesForEntry) => + (entries: T[]): NgramIndexesForEntry => { + // console.log("ngramIndexingSequenceEntry, entries:", entries) + const calc = (key: keyof FromToObj) => + indexes[key].map(index => ngramIndexingSequence(startId, index)(entries.map(e => e[key]))) + return { + from: calc("from"), + to: calc("to"), + } as NgramIndexesForEntry + } export const ngramIndexingEntryFromTo = - (entryId: number, indexes: NgramIndexesForEntry) => - (fromto: keyof FromToObj, text: string): NgramIndex[] => - indexes[fromto].map(index => ngramIndexing(index)(entryId, text)) + (entryId: number, indexes: NgramIndexesForEntry) => + (fromto: keyof FromToObj, text: string): NgramIndex[] => + indexes[fromto].map(index => ngramIndexing(index)(entryId, text)) export const ngramIndexingEntry = - (entryId: number, indexes: NgramIndexesForEntry) => - (entry: T): NgramIndexesForEntry => { - // console.log("ngramIndexingEntry called") - const calc = ngramIndexingEntryFromTo(entryId, indexes) - return { - from: calc("from", entry.from), - to: calc("to", entry.to), - } - } + (entryId: number, indexes: NgramIndexesForEntry) => + (entry: T): NgramIndexesForEntry => { + // console.log("ngramIndexingEntry called") + const calc = ngramIndexingEntryFromTo(entryId, indexes) + return { + from: calc("from", entry.from), + to: calc("to", entry.to), + } + } // export const ngramDeleteIndexingEntry = (entryId: number) export const getDedupKeyObj = (e: Entry) => { - const { to, ...keys } = e - return keys + const { to, ...keys } = e + return keys } export const getDedupKey = (e: Entry) => JSON.stringify(getDedupKeyObj(e)) export const getDedupKeyById = (id: number, eimap: EntryIdDedupedMap) => { - const matched = [...eimap.entries()].filter(([key, idd]) => id == idd) - return matched.length > 0 ? matched[0][0] : undefined + const matched = [...eimap.entries()].filter(([key, idd]) => id == idd) + return matched.length > 0 ? matched[0][0] : undefined } export type UniqueInfoAsKeyOfEntry = { - from: string - ic: boolean - mw: boolean - sc: boolean + from: string + ic: boolean + mw: boolean + sc: boolean } export type UniqueInfoTo_dedupedMap = Map export const getLengthMap = ( - getidMap: EntryIdDedupedMap, - dedupedMap: UniqueInfoTo_dedupedMap + getidMap: EntryIdDedupedMap, + dedupedMap: UniqueInfoTo_dedupedMap ): TextLengthMaps => { - return [...dedupedMap.entries()].reduce( - (lenmap: TextLengthMaps, [key, to]) => - ["from", "to"].reduce((m, fromto) => { - return { - ...m, - [fromto]: new Map( - [ - ...m[fromto as keyof TextLengthMaps].entries(), - [ - getidMap.get(key) ?? -1, - fromto === "to" - ? to.length - : (JSON.parse(key) as UniqueInfoAsKeyOfEntry).from.length, - ] as [number, number], - ].filter(([id, _]) => id >= 0) - ), - } - }, lenmap), - { - from: new Map(), - to: new Map(), - } - ) + return [...dedupedMap.entries()].reduce( + (lenmap: TextLengthMaps, [key, to]) => + ["from", "to"].reduce((m, fromto) => { + return { + ...m, + [fromto]: new Map( + [ + ...m[fromto as keyof TextLengthMaps].entries(), + [ + getidMap.get(key) ?? -1, + fromto === "to" + ? to.length + : (JSON.parse(key) as UniqueInfoAsKeyOfEntry).from.length, + ] as [number, number], + ].filter(([id, _]) => id >= 0) + ), + } + }, lenmap), + { + from: new Map(), + to: new Map(), + } + ) } export type TextAndId = { - text: string - id: number + text: string + id: number } export const updateIndexList = - (key: string) => - (indexes: NgramIndexesForEntry, matchedEntry: Entry, atomEntryIdDedupedMap: EntryIdDedupedMap) => - (fromTo: keyof FromToObj, toIndex?: TextAndId) => { - return indexes[fromTo].map(index => { - ngramSegmentify(index.n, matchedEntry[fromTo]).forEach(ngram => { - caseUndefined(index.index.get(ngram))( - indexData => - caseUndefined(atomEntryIdDedupedMap.get(key))(id => { - indexData.delete(id) - return indexData - }, indexData), - new Map() - ) - }) - return caseUndefined(toIndex)(({ id, text }) => ngramIndexing(index)(id, text), index) - }) - } + (key: string) => + (indexes: NgramIndexesForEntry, matchedEntry: Entry, atomEntryIdDedupedMap: EntryIdDedupedMap) => + (fromTo: keyof FromToObj, toIndex?: TextAndId) => { + return indexes[fromTo].map(index => { + ngramSegmentify(index.n, matchedEntry[fromTo]).forEach(ngram => { + caseUndefined(index.index.get(ngram))( + indexData => + caseUndefined(atomEntryIdDedupedMap.get(key))(id => { + indexData.delete(id) + return indexData + }, indexData), + new Map() + ) + }) + return caseUndefined(toIndex)(({ id, text }) => ngramIndexing(index)(id, text), index) + }) + } export const modifyNgramData = - (modifier: (data: NgramIndexData) => NgramIndexData) => - (index: NgramIndex): NgramIndex => ({ - n: index.n, - index: new Map([...index.index.entries()].map(([ngram, data]) => [ngram, modifier(data)])), - }) + (modifier: (data: NgramIndexData) => NgramIndexData) => + (index: NgramIndex): NgramIndex => ({ + n: index.n, + index: new Map([...index.index.entries()].map(([ngram, data]) => [ngram, modifier(data)])), + }) export const deleteEntriesFromIndex = - (deleteIds: number[]) => - (index: NgramIndex): NgramIndex => ({ - n: index.n, - index: new Map( - [...index.index.entries()].map(([ngram, data]) => [ - ngram, - new Map([...data.entries()].filter(([id, _]) => !deleteIds.includes(id))), - ]) - ), - }) + (deleteIds: number[]) => + (index: NgramIndex): NgramIndex => ({ + n: index.n, + index: new Map( + [...index.index.entries()].map(([ngram, data]) => [ + ngram, + new Map([...data.entries()].filter(([id, _]) => !deleteIds.includes(id))), + ]) + ), + }) diff --git a/ts/lang.ts b/ts/lang.ts index e3798ae..8485206 100644 --- a/ts/lang.ts +++ b/ts/lang.ts @@ -1,93 +1,154 @@ -export type OperationEffect = "add"|"update"|"delete" +export type OperationEffect = "add" | "update" | "delete" export const translationTree = { - add: { - en: "add", - ja: "追加", - }, - update: { - en: "udpate", - ja: "更新" - }, - delete: { - en: "delete", - ja: "削除" - }, - confirm: { - alreadyExist: { - en: "same entry already exists, which will be overloaded.", - ja: "同じ条件の組がもうあるので、上書きされます。" - } - }, - error: { - parse: { - en: "couldn't parsed as JSON", - ja: "解析不能", - } - }, - headerTitle: { - list: { - similar: {en: "similar", ja: "類似"} - } - }, - tooltip: { - operation: { - add: (from: string, to: string)=> ({ - en: `added: ${from} → ${to}` - ,ja: `追加: ${from} → ${to}` - }), - delete: (from: string, to: string)=> ({ - en: `deleted: ${from} → ${to}` - ,ja: `削除: ${from} → ${to}` - }), - update: (from: string, to: string)=>(to1: string)=> ({ - en: `updated: ${from} → ${to}` - ,ja: `更新: ${from} → (${to} > ${to1})` - }), - }, - fail: (opef: OperationEffect) => ({ - en: `failed to ${translationTree[opef].en}`, - ja: `${translationTree[opef].ja}に失敗しました` - }), - - }, - form: { - rules: { - required: { - en: "input something", - ja: "何か入力してください" - }, - minlength: (min: number)=> ({ - en: `input at least ${min} characters` - ,ja: `最低${min}文字入力してください` - }) - }, - helperText: { - invalid: { - en: "invalid", - ja: "無効" - } - } - }, - unit: { - point: { - en: "point" - ,ja: "点" - }, - }, - showPoint: { - en: "show match point", - ja: "一致度表示" - }, - searchDelay: { - label: - { - en: "search delay", - ja: "検索遅延" - }, - help: { - en: "increase this if input lagged", - ja: "入力がもたつくなら数値を大きく" - } - } + infobutton: { + en: "attention", + ja: "注意", + }, + info: { + appendNotWork: { + en: "import(append) does not work well", + ja: "追加引導は上手く動作しません", + }, + refreshAlert: { + en: "page refreshing will reset all!", + ja: "頁を更新すると全てまっさらな状態に戻ります!", + }, + }, + example: { + en: "example", + ja: "例", + }, + add: { + en: "add", + ja: "追加", + }, + update: { + en: "udpate", + ja: "更新", + }, + delete: { + en: "delete", + ja: "削除", + }, + confirm: { + alreadyExist: { + en: "same entry already exists, which will be overloaded.", + ja: "同じ条件の組がもうあるので、上書きされます。", + }, + }, + error: { + parse: { + en: "couldn't parsed as JSON", + ja: "解析不能", + }, + }, + headerTitle: { + list: { + similar: { en: "similar", ja: "類似" }, + }, + }, + tooltip: { + operation: { + add: (from: string, to: string) => ({ + en: `added: ${from} → ${to}`, + ja: `追加: ${from} → ${to}`, + }), + delete: (from: string, to: string) => ({ + en: `deleted: ${from} → ${to}`, + ja: `削除: ${from} → ${to}`, + }), + update: (from: string, to: string) => (to1: string) => ({ + en: `updated: ${from} → ${to}`, + ja: `更新: ${from} → (${to} > ${to1})`, + }), + }, + fail: (opef: OperationEffect) => ({ + en: `failed to ${translationTree[opef].en}`, + ja: `${translationTree[opef].ja}に失敗しました`, + }), + }, + form: { + rules: { + required: { + en: "input something", + ja: "何か入力してください", + }, + minlength: (min: number) => ({ + en: `input at least ${min} characters`, + ja: `最低${min}文字入力してください`, + }), + }, + helperText: { + invalid: { + en: "invalid", + ja: "無効", + }, + }, + label: { + from: { + en: "from", + ja: "置換対象", + }, + ic: { + en: "ignore case", + ja: "大文字小文字を無視する", + }, + mw: { + en: "match word", + ja: "単語のみ", + }, + sc: { + en: "smart case", + ja: "賢い大小文字", + }, + to: { + en: "to", + ja: "置換結果", + }, + }, + languages: { + en: "English", + ja: "日本語", + }, + }, + language: { + en: "language", + ja: "表示言語", + }, + unit: { + point: { + en: "point", + ja: "点", + }, + }, + showPoint: { + en: "show match point", + ja: "一致度表示", + }, + searchDelay: { + label: { + en: "search delay", + ja: "検索遅延", + }, + help: { + en: "increase this if input lagged", + ja: "入力がもたつくなら数値を大きく", + }, + }, + output: { + buttons: { + copy: { + en: "copy", + ja: "複製", + }, + append: { + en: "import (append)", + ja: "追加引導", + }, + initialize: { + en: "import (initialize)", + ja: "初期化引導", + }, + }, + }, } - diff --git a/ts/type.ts b/ts/type.ts index 93c0cf9..d3ae191 100644 --- a/ts/type.ts +++ b/ts/type.ts @@ -1,10 +1,13 @@ +import { defaultSearchDelay } from "./atom" + export type LimitN = { - limit: number + limit: number } export type SimilarForm = { - delay: number + delay: number } + export const defaultSimilarForm: SimilarForm = { - delay: 300 -} \ No newline at end of file + delay: defaultSearchDelay, +}