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
آخر نشاط في المصدر
٢٦ مايو ٢٠٢٦ في ٠٠:٥٥
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٤
التفرعات
٠

خيارات التثبيت

يُحدَّد 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