From 185e1bcf730079f64adc4253aaa99fb979d99cae Mon Sep 17 00:00:00 2001 From: mii443 Date: Tue, 11 Mar 2025 19:43:37 +0900 Subject: [PATCH] update to 1.11.0 --- package-lock.json | 13 +- package.json | 4 +- release.json | 10 +- src-tauri/2.0.0-rc | 9 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/capabilities/desktop.json | 1 + src-tauri/src/config.rs | 10 +- src-tauri/tauri.conf.json | 7 +- src/AboutComponent.tsx | 4 +- src/App.tsx | 6 +- src/DictionaryComponent.tsx | 318 ++++++++++++++++++---------- src/SettingsComponent.tsx | 2 +- src/TitleBar.tsx | 2 +- src/TsfSettingsModal.tsx | 5 +- 15 files changed, 256 insertions(+), 139 deletions(-) diff --git a/package-lock.json b/package-lock.json index b56c2b2..de89143 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "vrclipboard-ime-gui", - "version": "1.9.0", + "version": "1.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vrclipboard-ime-gui", - "version": "1.9.0", + "version": "1.11.0", "dependencies": { "@tauri-apps/api": "^2.0.0-rc.0", "@tauri-apps/plugin-shell": "^2.0.0", - "@tauri-apps/plugin-updater": "^2.0.0", + "@tauri-apps/plugin-updater": "^2.6.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -1276,9 +1276,10 @@ } }, "node_modules/@tauri-apps/plugin-updater": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.0.0.tgz", - "integrity": "sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.6.0.tgz", + "integrity": "sha512-j74RUolLIhQDQwrff6R28xIewYVXME1gFU+d+4LYN1dLRzLD+ySa7VHqzyWYxWEvm+TPZ7lkUxa5a9uH9Ist3A==", + "license": "MIT OR Apache-2.0", "dependencies": { "@tauri-apps/api": "^2.0.0" } diff --git a/package.json b/package.json index ea1553b..26fa903 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vrclipboard-ime-gui", "private": true, - "version": "1.10.0", + "version": "1.11.0", "type": "module", "scripts": { "dev": "vite", @@ -12,7 +12,7 @@ "dependencies": { "@tauri-apps/api": "^2.0.0-rc.0", "@tauri-apps/plugin-shell": "^2.0.0", - "@tauri-apps/plugin-updater": "^2.0.0", + "@tauri-apps/plugin-updater": "^2.6.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/release.json b/release.json index 023c03c..c2da0cf 100644 --- a/release.json +++ b/release.json @@ -1,7 +1,7 @@ { - "url": "https://r2-vrime.mii.dev/releases/vrclipboard-ime-gui_1.9.0_x64_ja-JP.msi.zip", - "version": "1.9.0", - "notes": "VRChat外でコピーした文字列の変換をスキップする機能の実装", - "pub_date": "2024-09-27T12:18:27+00:00", - "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTTStkVlpUR0NpcVdBalVrbUpDcDdac3NhSFhMZFZGbW90STNOYnJNVVhzZkdFQ0t3TzVTMkRoUkhQQVI1RnVHR2xSdUNYL0ttS0NTWW1OcFMyS2NtaGoybmZmdjU5TUFJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzI3NDM5Mjk5CWZpbGU6dnJjbGlwYm9hcmQtaW1lLWd1aV8xLjkuMF94NjRfamEtSlAubXNpLnppcAp6QTNXc2hGbXNaRU1PdVA3L1FHOVBrU2wvOE1HZGFpL3h6K2lSa3Buby9Jbnc0VTVnaGMrR2tQOHVOUHVHa1A3b1FmZmVUQlJZYk9WY3hmL3dteVZEZz09Cg==" + "url": "https://r2-vrime.mii.dev/releases/vrclipboard-ime-gui_1.11.0_x64_ja-JP.msi.zip", + "version": "1.11.0", + "notes": "UIの変更, TSF再変換機能の正式リリース", + "pub_date": "2025-03-11T19:14:02+09:00", + "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTTStkVlpUR0NpcVcvYitzcmhYakFkOTBWZVo5RHFwVk9xbjdzNlg3VHMrZ3NBaHpxS2VacEg4ckE3V3ZZSGZETzFyZE1Qc0JoRTltUGhvREdsUzF2Ry91c2RtWnlqOXdrPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzQxNjg4MzA3CWZpbGU6dnJjbGlwYm9hcmQtaW1lLWd1aV8xLjExLjBfeDY0X2phLUpQLm1zaS56aXAKVWt0b0t5YkR5MlczM1FRYXV2THRHYjQwWVFxRGR2OW1KTExJNGFpeGcyN2k2Q1hrU3Yrb3dxU3NvY2FLNTk4bDd1Q1lFanpaQktIM0UxdTBzNmd3Q3c9PQo=" } diff --git a/src-tauri/2.0.0-rc b/src-tauri/2.0.0-rc index e3187fb..ee72c95 100644 --- a/src-tauri/2.0.0-rc +++ b/src-tauri/2.0.0-rc @@ -1,7 +1,12 @@ -added 1 package, and audited 194 packages in 4s +changed 1 package, and audited 194 packages in 3s 38 packages are looking for funding run `npm fund` for details -found 0 vulnerabilities +4 vulnerabilities (3 moderate, 1 high) + +To address all issues, run: + npm audit fix + +Run `npm audit` for details. diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 19e13ad..0c4a785 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4399,7 +4399,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vrclipboard-ime-gui" -version = "1.10.0" +version = "1.11.0" dependencies = [ "anyhow", "calc", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d0358d9..1878793 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vrclipboard-ime-gui" -version = "1.10.0" +version = "1.11.0" description = "VRClipboard IME" authors = ["mii"] edition = "2021" diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json index 00ff1a6..9874144 100644 --- a/src-tauri/capabilities/desktop.json +++ b/src-tauri/capabilities/desktop.json @@ -6,6 +6,7 @@ "linux" ], "permissions": [ + "updater:default", "updater:default" ] } \ No newline at end of file diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 669a5fd..845d976 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -27,7 +27,7 @@ pub struct Config { pub on_copy_mode: OnCopyMode, #[serde(default = "bool_true")] pub skip_url: bool, - #[serde(default = "bool_false")] + #[serde(default = "bool_true")] pub use_tsf_reconvert: bool, #[serde(default = "bool_true")] pub skip_on_out_of_vrc: bool, @@ -44,7 +44,7 @@ impl Default for Config { ignore_prefix: true, on_copy_mode: OnCopyMode::ReturnToChatbox, skip_url: true, - use_tsf_reconvert: false, + use_tsf_reconvert: true, skip_on_out_of_vrc: true, tsf_announce: false } @@ -95,7 +95,11 @@ impl Config { let mut contents = String::new(); file.read_to_string(&mut contents)?; trace!("Raw config contents: {}", contents); - let config: Config = serde_yaml::from_str(&contents)?; + let mut config: Config = serde_yaml::from_str(&contents)?; + if !config.tsf_announce { + config.use_tsf_reconvert = true; + config.tsf_announce = true; + } debug!("Config loaded successfully"); Ok(config) } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 668f9b5..fceb903 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -24,14 +24,17 @@ }, "productName": "vrclipboard-ime-gui", "mainBinaryName": "vrclipboard-ime-gui", - "version": "1.10.0", + "version": "1.11.0", "identifier": "dev.mii.vrclipboard-ime", "plugins": { "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEE5QTI2MDRDNTlENUY5OEMKUldTTStkVlpUR0NpcVIrMXZqOHFpNzNXMFVKT0d3aHJIWFlOUVJubGN5VTAzUkVwYW95bVlMYUQK", "endpoints": [ "https://r2-vrime.mii.dev/release.json" - ] + ], + "windows": { + "installMode": "passive" + } } }, "app": { diff --git a/src/AboutComponent.tsx b/src/AboutComponent.tsx index 972b3a7..652c622 100644 --- a/src/AboutComponent.tsx +++ b/src/AboutComponent.tsx @@ -30,7 +30,7 @@ const AboutComponent: React.FC = () => {
バージョン: - 1.10.0 + 1.11.0
ライセンス: @@ -38,7 +38,7 @@ const AboutComponent: React.FC = () => {
最終更新: - 2025年2月 + 2025年3月11日
技術: diff --git a/src/App.tsx b/src/App.tsx index 9d7d36c..9329d6f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { List, Settings, Terminal, Bug, Info, Check, Book } from 'lucide-react'; +import { List, Settings, Terminal, Bug, Info, Check } from 'lucide-react'; import { listen } from "@tauri-apps/api/event"; import { invoke } from "@tauri-apps/api/core"; import "./App.css"; @@ -24,7 +24,7 @@ const AppContent = () => { const [activeMenuItem, setActiveMenuItem] = useState('home'); const [showTsfModal, setShowTsfModal] = useState(false); const [currentSettings, setCurrentSettings] = useState(null); - const [isTsfAvailable, setIsTsfAvailable] = useState(null); + const [_isTsfAvailable, setIsTsfAvailable] = useState(null); const [showTsfSuccessMessage, setShowTsfSuccessMessage] = useState(false); useEffect(() => { @@ -146,7 +146,7 @@ const AppContent = () => {
} label="ログ" id="home" /> } label="設定" id="settings" /> - } label="辞書" id="dictionary" /> + {/*} label="辞書" id="dictionary" />*/}
{/* 下側にデバッグタブを配置 */}
diff --git a/src/DictionaryComponent.tsx b/src/DictionaryComponent.tsx index e935acb..c47e0c2 100644 --- a/src/DictionaryComponent.tsx +++ b/src/DictionaryComponent.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; import { invoke } from '@tauri-apps/api/core'; -import { Book, Save, Plus, Trash, ChevronUp, ChevronDown, AlertCircle, Check, Edit, AlignLeft, Info, X } from 'lucide-react'; +import { Book, Save, Plus, Trash, ChevronUp, ChevronDown, AlertCircle, Check, Edit, AlignLeft, X } from 'lucide-react'; import { Dictionary, DictionaryEntry, @@ -12,16 +12,136 @@ import { convertFromRustEntry } from './types/dictionary'; +// 入力フィールド用の内部コンポーネント(フォーカスを保持するため) +const InputField: React.FC<{ + label: string; + value: string; + placeholder?: string; + onChange: (value: string) => void; + inputRef?: React.RefObject; +}> = ({ label, value, placeholder, onChange, inputRef }) => { + // ローカルステートで値を管理 + const [localValue, setLocalValue] = useState(value); + + // 親から渡された値が変わったらローカルステートを更新 + useEffect(() => { + setLocalValue(value); + }, [value]); + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; + setLocalValue(newValue); + onChange(newValue); + }; + + return ( +
+ + +
+ ); +}; + +// 数値入力フィールド用のコンポーネント +const NumberField: React.FC<{ + label: string; + value: number; + onChange: (value: number) => void; + description?: string; +}> = ({ label, value, onChange, description }) => { + // ローカルステートで値を管理 + const [localValue, setLocalValue] = useState(value.toString()); + + useEffect(() => { + setLocalValue(value.toString()); + }, [value]); + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; + setLocalValue(newValue); + onChange(parseInt(newValue) || 0); + }; + + return ( +
+ + + {description && ( +

+ {description} +

+ )} +
+ ); +}; + +// チェックボックス用のコンポーネント +const CheckboxField: React.FC<{ + id: string; + label: string; + checked: boolean; + onChange: (checked: boolean) => void; +}> = ({ id, label, checked, onChange }) => { + // ローカルステートで値を管理 + const [isChecked, setIsChecked] = useState(checked); + + useEffect(() => { + setIsChecked(checked); + }, [checked]); + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.checked; + setIsChecked(newValue); + onChange(newValue); + }; + + return ( +
+ + +
+ ); +}; + const DictionaryComponent: React.FC = () => { const [dictionary, setDictionary] = useState({ entries: [] }); - const [selectedEntry, setSelectedEntry] = useState(null); const [editIndex, setEditIndex] = useState(null); - const [isEditing, setIsEditing] = useState(false); const [showDialog, setShowDialog] = useState(false); const [isMethodDropdownOpen, setIsMethodDropdownOpen] = useState(false); const [isConverterDropdownOpen, setIsConverterDropdownOpen] = useState(false); const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle'); - + + // 編集中のエントリを更新するためのフォースレンダリング用state + const [, forceUpdate] = useState({}); + + // Refを使って値を保持 + const entryRef = useRef(null); + const methodDropdownRef = useRef(null); const converterDropdownRef = useRef(null); @@ -86,17 +206,19 @@ const DictionaryComponent: React.FC = () => { }; const handleAddEntry = () => { - setSelectedEntry(getDefaultDictionaryEntry()); + entryRef.current = getDefaultDictionaryEntry(); setEditIndex(null); - setIsEditing(true); setShowDialog(true); + // 強制的に再レンダリング + forceUpdate({}); }; const handleEditEntry = (entry: DictionaryEntry, index: number) => { - setSelectedEntry({...entry}); + entryRef.current = {...entry}; setEditIndex(index); - setIsEditing(true); setShowDialog(true); + // 強制的に再レンダリング + forceUpdate({}); }; const handleDeleteEntry = (index: number) => { @@ -108,34 +230,41 @@ const DictionaryComponent: React.FC = () => { }; const handleSaveEntry = () => { - if (!selectedEntry) return; + if (!entryRef.current) return; const newEntries = [...dictionary.entries]; // 新規追加の場合 if (editIndex === null) { - newEntries.push(selectedEntry); + newEntries.push(entryRef.current); } else { // 編集の場合 - newEntries[editIndex] = selectedEntry; + newEntries[editIndex] = entryRef.current; } const newDict = { ...dictionary, entries: newEntries }; setDictionary(newDict); saveDictionary(newDict); + + // ダイアログを閉じて状態をリセット + closeDialog(); + }; + + const closeDialog = () => { setShowDialog(false); - setIsEditing(false); - setSelectedEntry(null); + setIsMethodDropdownOpen(false); + setIsConverterDropdownOpen(false); + entryRef.current = null; }; const handleChangePriority = (index: number, direction: 'up' | 'down') => { if (dictionary.entries.length <= 1) return; const newEntries = [...dictionary.entries]; - const entry = newEntries[index]; + const entry = {...newEntries[index]}; if (direction === 'up' && index > 0) { - const prevEntry = newEntries[index - 1]; + const prevEntry = {...newEntries[index - 1]}; const tempPriority = prevEntry.priority; prevEntry.priority = entry.priority; entry.priority = tempPriority; @@ -144,7 +273,7 @@ const DictionaryComponent: React.FC = () => { newEntries[index] = prevEntry; newEntries[index - 1] = entry; } else if (direction === 'down' && index < newEntries.length - 1) { - const nextEntry = newEntries[index + 1]; + const nextEntry = {...newEntries[index + 1]}; const tempPriority = nextEntry.priority; nextEntry.priority = entry.priority; entry.priority = tempPriority; @@ -159,10 +288,15 @@ const DictionaryComponent: React.FC = () => { saveDictionary(newDict); }; - const handleChangeEntryField = (field: keyof DictionaryEntry, value: any) => { - if (!selectedEntry) return; + // 入力フィールドの値を更新(フォーカスを失わないようRef経由で更新) + const handleChangeEntryField = (field: K, value: DictionaryEntry[K]) => { + if (!entryRef.current) return; - const updatedEntry = { ...selectedEntry, [field]: value }; + // RefのDictionaryEntryをディープコピー + const updatedEntry = { ...entryRef.current }; + + // フィールドの値を更新 + updatedEntry[field] = value as DictionaryEntry[K]; // 変換方法がReplace以外の場合はoutputをnullに if (field === 'method' && value !== ConversionMethod.Replace) { @@ -174,7 +308,13 @@ const DictionaryComponent: React.FC = () => { updatedEntry.converter_char = 'r'; } - setSelectedEntry(updatedEntry); + // 更新したEntryをRefに保存 + entryRef.current = updatedEntry; + + // UIを強制的に更新(ドロップダウンメニュー表示/非表示の対応のため) + if (field === 'method' || field === 'converter_char') { + forceUpdate({}); + } }; const handleSelectMethod = (method: ConversionMethod) => { @@ -219,7 +359,9 @@ const DictionaryComponent: React.FC = () => { // 辞書エントリダイアログ const EntryDialog = () => { - if (!showDialog || !selectedEntry) return null; + if (!showDialog || !entryRef.current) return null; + + const entry = entryRef.current; return (
@@ -229,13 +371,7 @@ const DictionaryComponent: React.FC = () => { {editIndex !== null ? '辞書エントリの編集' : '新しい辞書エントリ'}
-
- - handleChangeEntryField('input', e.target.value)} - className="w-full p-1.5 text-sm border rounded bg-white dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200 focus:border-indigo-400 outline-none" - placeholder="例: こんにちは" - /> -
- handleChangeEntryField('use_regex', e.target.checked)} - className="h-3.5 w-3.5 text-indigo-500 border-gray-300 dark:border-gray-600 rounded dark:bg-gray-700" - /> - -
-
+ handleChangeEntryField('input', value)} + /> -
+ handleChangeEntryField('use_regex', checked)} + /> + +
setIsMethodDropdownOpen(!isMethodDropdownOpen)} > - {getMethodLabel(selectedEntry.method as ConversionMethod, selectedEntry.converter_char)} + {getMethodLabel(entry.method as ConversionMethod, entry.converter_char)}
{isMethodDropdownOpen && ( @@ -286,9 +410,9 @@ const DictionaryComponent: React.FC = () => { className="p-1.5 hover:bg-indigo-50 dark:hover:bg-indigo-900/50 cursor-pointer" onClick={() => handleSelectMethod(ConversionMethod.Replace)} > -
- {selectedEntry.method === ConversionMethod.Replace && } - +
+ {entry.method === ConversionMethod.Replace && } + 置き換え
@@ -297,9 +421,9 @@ const DictionaryComponent: React.FC = () => { className="p-1.5 hover:bg-indigo-50 dark:hover:bg-indigo-900/50 cursor-pointer" onClick={() => handleSelectMethod(ConversionMethod.None)} > -
- {selectedEntry.method === ConversionMethod.None && } - +
+ {entry.method === ConversionMethod.None && } + 無変換
@@ -308,9 +432,9 @@ const DictionaryComponent: React.FC = () => { className="p-1.5 hover:bg-indigo-50 dark:hover:bg-indigo-900/50 cursor-pointer" onClick={() => handleSelectMethod(ConversionMethod.Converter)} > -
- {selectedEntry.method === ConversionMethod.Converter && } - +
+ {entry.method === ConversionMethod.Converter && } + 変換
@@ -319,19 +443,18 @@ const DictionaryComponent: React.FC = () => { )}
- {selectedEntry.method === ConversionMethod.Converter && ( -
+ {entry.method === ConversionMethod.Converter && ( +
setIsConverterDropdownOpen(!isConverterDropdownOpen)} > - {selectedEntry.converter_char - ? getConverterInfo(selectedEntry.converter_char)?.name || 'ローマ字→漢字' + {entry.converter_char + ? getConverterInfo(entry.converter_char)?.name || 'ローマ字→漢字' : 'ローマ字→漢字'} @@ -344,9 +467,9 @@ const DictionaryComponent: React.FC = () => { className="p-1.5 hover:bg-indigo-50 dark:hover:bg-indigo-900/50 cursor-pointer" onClick={() => handleSelectConverter(converter.id)} > -
- {selectedEntry.converter_char === converter.id && } - +
+ {entry.converter_char === converter.id && } + {converter.name} - {converter.description}
@@ -357,47 +480,26 @@ const DictionaryComponent: React.FC = () => {
)} - {selectedEntry.method === ConversionMethod.Replace && ( -
- - handleChangeEntryField('output', e.target.value)} - className="w-full p-1.5 text-sm border rounded bg-white dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200 focus:border-indigo-400 outline-none" - placeholder="例: Hello" - /> -
+ {entry.method === ConversionMethod.Replace && ( + handleChangeEntryField('output', value)} + /> )} -
- - handleChangeEntryField('priority', parseInt(e.target.value) || 0)} - className="w-full p-1.5 text-sm border rounded bg-white dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200 focus:border-indigo-400 outline-none" - min="0" - /> -

- 数値が大きいほど優先度が高くなります。 -

-
+ handleChangeEntryField('priority', value)} + description="数値が大きいほど優先度が高くなります。" + />