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.

Quellinformationen

Repository
reason-machines/devtools-skills
Letzte Quellaktivität
26. Mai 2026 um 00:55
Erkannte Sprache von SKILL.md
Englisch
Sterne
4
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen