mirror of
https://github.com/mii443/vrclipboard-ime-gui.git
synced 2025-08-22 16:15:32 +00:00
update Terminal
This commit is contained in:
@ -31,6 +31,8 @@ pub struct Config {
|
||||
pub use_tsf_reconvert: bool,
|
||||
#[serde(default = "bool_true")]
|
||||
pub skip_on_out_of_vrc: bool,
|
||||
#[serde(default = "bool_false")]
|
||||
pub tsf_announce: bool
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -44,6 +46,7 @@ impl Default for Config {
|
||||
skip_url: true,
|
||||
use_tsf_reconvert: false,
|
||||
skip_on_out_of_vrc: true,
|
||||
tsf_announce: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use clipboard_master::{CallbackResult, ClipboardHandler};
|
||||
use regex::Regex;
|
||||
use rosc::{encoder, OscMessage, OscPacket, OscType};
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri::{AppHandle, Emitter};
|
||||
use tracing::{error, info, warn};
|
||||
use windows::Win32::System::DataExchange::GetClipboardOwner;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
//#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod com;
|
||||
mod config;
|
||||
@ -23,7 +23,6 @@ use com::Com;
|
||||
use config::Config;
|
||||
use handler::ConversionHandler;
|
||||
use tauri_emit_subscriber::TauriEmitSubscriber;
|
||||
use tracing::{instrument::WithSubscriber, level_filters::LevelFilter, Level};
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
@ -58,8 +57,6 @@ fn save_settings(config: Config, state: State<AppState>) -> Result<(), String> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("VRClipboard-IME Logs\nバグがあった場合はこのログを送ってください。");
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
|
@ -4,7 +4,8 @@ import { listen } from "@tauri-apps/api/event";
|
||||
import "./App.css";
|
||||
import TitleBar from "./TitleBar";
|
||||
import SettingsComponent from "./SettingsComponent";
|
||||
import { ThemeProvider, useTheme } from "./ThemeContext";
|
||||
import { ThemeProvider } from "./ThemeContext";
|
||||
import { LogProvider } from "./LogContext";
|
||||
import TerminalComponent from "./TerminalComponent";
|
||||
import AboutComponent from "./AboutComponent";
|
||||
|
||||
@ -15,7 +16,6 @@ interface Log {
|
||||
}
|
||||
|
||||
const AppContent = () => {
|
||||
const { theme } = useTheme();
|
||||
const [logs, setLogs] = useState<Log[]>([]);
|
||||
const [activeMenuItem, setActiveMenuItem] = useState('home');
|
||||
|
||||
@ -118,7 +118,9 @@ const AppContent = () => {
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<LogProvider>
|
||||
<AppContent />
|
||||
</LogProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
69
src/LogContext.tsx
Normal file
69
src/LogContext.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { createContext, useState, useContext, useEffect } from 'react';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
|
||||
export interface LogMessage {
|
||||
level: 'info' | 'warn' | 'error' | 'debug' | 'trace';
|
||||
message: string;
|
||||
module_path: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface LogContextType {
|
||||
logs: LogMessage[];
|
||||
clearLogs: () => void;
|
||||
}
|
||||
|
||||
export const LogContext = createContext<LogContextType | undefined>(undefined);
|
||||
|
||||
export const LogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [logs, setLogs] = useState<LogMessage[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// グローバルにログイベントを監視
|
||||
const setupLogListener = async () => {
|
||||
try {
|
||||
// log-eventイベントのリスナーを設定
|
||||
const unlisten = await listen<LogMessage>('log-event', (event) => {
|
||||
setLogs(prev => [...prev, event.payload]);
|
||||
});
|
||||
|
||||
// クリーンアップ関数を返す
|
||||
return () => {
|
||||
unlisten();
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to set up log listener:', error);
|
||||
return () => {};
|
||||
}
|
||||
};
|
||||
|
||||
// リスナーをセットアップし、その結果をPromiseとして保持
|
||||
const unlistenPromise = setupLogListener();
|
||||
|
||||
// コンポーネントのアンマウント時にリスナーを解除
|
||||
return () => {
|
||||
unlistenPromise.then(cleanupFn => cleanupFn()).catch(err => {
|
||||
console.error('Error cleaning up event listener:', err);
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const clearLogs = () => {
|
||||
setLogs([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<LogContext.Provider value={{ logs, clearLogs }}>
|
||||
{children}
|
||||
</LogContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// カスタムフック
|
||||
export const useLogs = (): LogContextType => {
|
||||
const context = useContext(LogContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useLogs must be used within a LogProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
@ -1,52 +1,30 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { Check, Copy, Trash, Download } from 'lucide-react';
|
||||
|
||||
interface LogMessage {
|
||||
level: 'info' | 'warn' | 'error' | 'debug' | 'trace';
|
||||
message: string;
|
||||
module_path: string;
|
||||
timestamp: string;
|
||||
}
|
||||
import { useLogs } from './LogContext';
|
||||
|
||||
const TerminalComponent: React.FC = () => {
|
||||
const [logs, setLogs] = useState<LogMessage[]>([]);
|
||||
const { logs, clearLogs } = useLogs();
|
||||
const [filter, setFilter] = useState<string>('');
|
||||
const [autoScroll, setAutoScroll] = useState<boolean>(true);
|
||||
const [copied, setCopied] = useState<boolean>(false);
|
||||
const terminalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 自動スクロール効果
|
||||
useEffect(() => {
|
||||
const setupLogListener = async () => {
|
||||
try {
|
||||
const unlisten = await listen<LogMessage>('log-event', (event) => {
|
||||
setLogs(prev => [...prev, event.payload]);
|
||||
if (autoScroll && terminalRef.current) {
|
||||
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unlisten();
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to set up log listener:', error);
|
||||
}
|
||||
};
|
||||
|
||||
setupLogListener();
|
||||
|
||||
if (terminalRef.current) {
|
||||
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// コンポーネントがマウントされたとき、または新しいログが追加されたときに自動スクロール
|
||||
if (autoScroll && terminalRef.current) {
|
||||
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
|
||||
}
|
||||
}, [logs, filter, autoScroll]);
|
||||
|
||||
// コンポーネントがマウントされた時に一度だけ実行
|
||||
useEffect(() => {
|
||||
// 初期スクロール位置を設定
|
||||
if (terminalRef.current) {
|
||||
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getLogColor = (level: string): string => {
|
||||
switch (level) {
|
||||
case 'ERROR': return 'text-red-500 dark:text-red-400';
|
||||
@ -59,8 +37,7 @@ const TerminalComponent: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleCopyLogs = async () => {
|
||||
const logText = logs
|
||||
.filter(log => filter === '' || log.message.toLowerCase().includes(filter.toLowerCase()) || log.level.includes(filter.toLowerCase()))
|
||||
const logText = filteredLogs
|
||||
.map(log => `[${log.timestamp} ${log.module_path}] [${log.level.toUpperCase()}] ${log.message}`)
|
||||
.join('\n');
|
||||
|
||||
@ -73,13 +50,8 @@ const TerminalComponent: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleClearLogs = () => {
|
||||
setLogs([]);
|
||||
};
|
||||
|
||||
const handleSaveLogs = () => {
|
||||
const logText = logs
|
||||
.filter(log => filter === '' || log.message.toLowerCase().includes(filter.toLowerCase()) || log.level.includes(filter.toLowerCase()))
|
||||
const logText = filteredLogs
|
||||
.map(log => `[${log.timestamp} ${log.module_path}] [${log.level.toUpperCase()}] ${log.message}`)
|
||||
.join('\n');
|
||||
|
||||
@ -97,7 +69,7 @@ const TerminalComponent: React.FC = () => {
|
||||
const filteredLogs = logs.filter(
|
||||
log => filter === '' ||
|
||||
log.message.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
log.level.includes(filter.toLowerCase())
|
||||
log.level.toLowerCase().includes(filter.toLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
@ -114,7 +86,7 @@ const TerminalComponent: React.FC = () => {
|
||||
{copied ? 'コピー完了' : 'コピー'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClearLogs}
|
||||
onClick={clearLogs}
|
||||
className="flex items-center text-xs text-gray-600 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded"
|
||||
title="ログをクリア"
|
||||
>
|
||||
|
Reference in New Issue
Block a user