update Terminal

This commit is contained in:
mii
2025-03-04 18:15:33 +09:00
parent e6e29da52c
commit 723445786e
6 changed files with 95 additions and 52 deletions

View File

@ -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
}
}
}

View File

@ -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;

View File

@ -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())

View File

@ -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
View 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;
};

View File

@ -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="ログをクリア"
>