Skip to main content

tiez-clipboard-manager

TieZ is a Tauri-based cross-platform clipboard manager with history, tags, sync (WebDAV/MQTT), privacy protection, and fast workflows for managing clipboard data across devices.

설치로 이동

소스 정보

저장소
reason-machines/devtools-skills
최근 소스 활동
2026년 5월 26일 00:55
감지된 SKILL.md 언어
영어
스타
4
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
tiez-clipboard-manager
description
TieZ is a Tauri-based cross-platform clipboard manager with history, tags, sync (WebDAV/MQTT), privacy protection, and fast workflows for managing clipboard data across devices.
triggers
["how do I set up TieZ clipboard manager","configure TieZ clipboard sync with WebDAV","build TieZ clipboard from source","develop features for TieZ clipboard manager","customize TieZ clipboard themes","integrate MQTT sync in TieZ","troubleshoot TieZ clipboard build errors","add tauri commands to TieZ clipboard"]
# TieZ Clipboard Manager Skill > Skill by [ara.so](https://ara.so) — Devtools Skills collection. ## Overview TieZ is a cross-platform clipboard manager built with Tauri 2, React, and Rust. It provides clipboard history tracking, tag organization, privacy masking, cloud sync (WebDAV/MQTT), and multiple UI themes. The application uses a native Rust backend for performance and a TypeScript/React frontend for the UI. **Key Technologies:** - **Tauri 2**: Desktop framework (Rust backend + web frontend) - **React**: Frontend UI framework - **TypeScript**: Frontend language - **Rust**: Backend logic, clipboard monitoring, file operations - **WebDAV/MQTT**: Sync protocols for cross-device data ## Installation & Setup ### Prerequisites ```bash # Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install Node.js (v18+) # macOS brew install node # Windows (use installer from nodejs.org) # Install pnpm npm install -g pnpm ``` ### Clone and Build ```bash # Clone repository git clone https://github.com/jimuzhe/tiez-clipboard.git cd tiez-clipboard # Install frontend dependencies pnpm install # Run in development mode pnpm tauri dev # Build for production pnpm tauri build ``` ### Development Structure ``` tiez-clipboard/ ├── src/ # React frontend (TypeScript) ├── src-tauri/ # Rust backend │ ├── src/ │ │ ├── main.rs │ │ ├── clipboard/ │ │ ├── sync/ │ │ └── commands/ │ ├── Cargo.toml │ └── tauri.conf.json ├── package.json └── vite.config.ts ``` ## Key Concepts ### Tauri Commands TieZ uses Tauri commands to bridge Rust backend and React frontend: **Backend (Rust):** ```rust // src-tauri/src/commands/clipboard.rs use tauri::State; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct ClipboardItem { pub id: String, pub content: String, pub content_type: String, pub timestamp: i64, pub tags: Vec<String>, } #[tauri::command] pub async fn get_clipboard_history( limit: Option<usize>, ) -> Result<Vec<ClipboardItem>, String> { // Query database for clipboard history let items = query_history(limit.unwrap_or(100)) .await .map_err(|e| e.to_string())?; Ok(items) } #[tauri::command] pub async fn add_clipboard_item( content: String, content_type: String, tags: Vec<String>, ) -> Result<ClipboardItem, String> { // Store clipboard item let item = store_clipboard_item(content, content_type, tags) .await .map_err(|e| e.to_string())?; Ok(item) } #[tauri::command] pub async fn delete_clipboard_item(id: String) -> Result<(), String> { remove_item(&id).await.map_err(|e| e.to_string())?; Ok(()) } ``` **Frontend (TypeScript/React):** ```typescript // src/api/clipboard.ts import { invoke } from '@tauri-apps/api/core'; export interface ClipboardItem { id: string; content: string; content_type: string; timestamp: number; tags: string[]; } export async function getClipboardHistory( limit?: number ): Promise<ClipboardItem[]> { return await invoke<ClipboardItem[]>('get_clipboard_history', { limit }); } export async function addClipboardItem( content: string, contentType: string, tags: string[] ): Promise<ClipboardItem> { return await invoke<ClipboardItem>('add_clipboard_item', { content, contentType, tags, }); } export async function deleteClipboardItem(id: string): Promise<void> { await invoke('delete_clipboard_item', { id }); } ``` ### Clipboard Monitoring ```rust // src-tauri/src/clipboard/monitor.rs use clipboard_master::{CallbackResult, ClipboardHandler, Master}; use std::sync::{Arc, Mutex}; use tauri::{AppHandle, Manager}; pub struct ClipboardMonitor { app_handle: AppHandle, } impl ClipboardHandler for ClipboardMonitor { fn on_clipboard_change(&mut self) -> CallbackResult { // Get clipboard content if let Ok(content) = get_clipboard_text() { // Emit event to frontend self.app_handle .emit_all("clipboard-changed", content) .unwrap_or_default(); } CallbackResult::Next } } pub fn start_monitoring(app_handle: AppHandle) { std::thread::spawn(move || { let monitor = ClipboardMonitor { app_handle }; Master::new(monitor).run().unwrap(); }); } ``` ### WebDAV Sync Integration ```rust // src-tauri/src/sync/webdav.rs use reqwest::Client; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct WebDAVConfig { pub url: String, pub username: String, pub password: String, pub path: String, } pub struct WebDAVSync { client: Client, config: WebDAVConfig, } impl WebDAVSync { pub fn new(config: WebDAVConfig) -> Self { Self { client: Client::new(), config, } } pub async fn upload_data(&self, data: &str) -> Result<(), String> { let url = format!("{}/{}", self.config.url, self.config.path); self.client .put(&url) .basic_auth(&self.config.username, Some(&self.config.password)) .body(data.to_string()) .send() .await .map_err(|e| e.to_string())?; Ok(()) } pub async fn download_data(&self) -> Result<String, String> { let url = format!("{}/{}", self.config.url, self.config.path); let response = self.client .get(&url) .basic_auth(&self.config.username, Some(&self.config.password)) .send() .await .map_err(|e| e.to_string())?; response.text().await.map_err(|e| e.to_string()) } } #[tauri::command] pub async fn sync_to_webdav( config: WebDAVConfig, data: String, ) -> Result<(), String> { let sync = WebDAVSync::new(config); sync.upload_data(&data).await } ``` ### MQTT Real-time Sync ```rust // src-tauri/src/sync/mqtt.rs use rumqttc::{AsyncClient, MqttOptions, QoS}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct MQTTConfig { pub broker: String, pub port: u16, pub client_id: String, pub username: Option<String>, pub password: Option<String>, pub topic: String, } pub async fn publish_clipboard_item( config: &MQTTConfig, item: &ClipboardItem, ) -> Result<(), String> { let mut mqttoptions = MqttOptions::new( &config.client_id, &config.broker, config.port, ); if let (Some(username), Some(password)) = (&config.username, &config.password) { mqttoptions.set_credentials(username, password); } let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10); let payload = serde_json::to_string(item) .map_err(|e| e.to_string())?; client .publish(&config.topic, QoS::AtLeastOnce, false, payload) .await .map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub async fn mqtt_sync_item( config: MQTTConfig, item: ClipboardItem, ) -> Result<(), String> { publish_clipboard_item(&config, &item).await } ``` ## Frontend Development ### React Component Example ```typescript // src/components/ClipboardHistory.tsx import React, { useEffect, useState } from 'react'; import { listen } from '@tauri-apps/api/event'; import { getClipboardHistory, ClipboardItem } from '../api/clipboard'; export const ClipboardHistory: React.FC = () => { const [items, setItems] = useState<ClipboardItem[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { // Load initial history loadHistory(); // Listen for clipboard changes const unlisten = listen<string>('clipboard-changed', (event) => { console.log('Clipboard changed:', event.payload); loadHistory(); }); return () => { unlisten.then((fn) => fn()); }; }, []); const loadHistory = async () => { setLoading(true); try { const history = await getClipboardHistory(50); setItems(history); } catch (error) { console.error('Failed to load history:', error); } finally { setLoading(false); } }; return ( <div className="clipboard-history"> {loading ? ( <div>Loading...</div> ) : ( <ul> {items.map((item) => ( <li key={item.id}> <div className="content">{item.content}</div> <div className="tags"> {item.tags.map((tag) => ( <span key={tag} className="tag"> {tag} </span> ))} </div> </li> ))} </ul> )} </div> ); }; ``` ### Settings Management ```typescript // src/api/settings.ts import { invoke } from '@tauri-apps/api/core'; export interface AppSettings { theme: 'frosted' | 'notebook' | 'sticky' | '3d'; darkMode: boolean; autoStart: boolean; maxHistoryItems: number; syncEnabled: boolean; webdavConfig?: { url: string; username: string; path: string; }; mqttConfig?: { broker: string; port: number; clientId: string; topic: string; }; } export async function getSettings(): Promise<AppSettings> { return await invoke<AppSettings>('get_settings'); } export async function updateSettings( settings: Partial<AppSettings> ): Promise<void> { await invoke('update_settings', { settings }); } ``` ```rust // src-tauri/src/commands/settings.rs use tauri::State; use serde::{Deserialize, Serialize}; use std::sync::Mutex; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppSettings { pub theme: String, pub dark_mode: bool, pub auto_start: bool, pub max_history_items: usize, pub sync_enabled: bool, } pub struct SettingsState(pub Mutex<AppSettings>); #[tauri::command] pub fn get_settings(state: State<SettingsState>) -> Result<AppSettings, String> { let settings = state.0.lock().map_err(|e| e.to_string())?; Ok(settings.clone()) } #[tauri::command] pub fn update_settings( settings: AppSettings, state: State<SettingsState>, ) -> Result<(), String> { let mut current = state.0.lock().map_err(|e| e.to_string())?; *current = settings; // Persist to disk
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기