Sincronizador de portapapeles para Windows que mantiene el portapapeles sincronizado entre máquinas usando Firebase Firestore como backend en tiempo real.
Construido con Electron + TypeScript. Se distribuye como un único ejecutable portable (ClipboardSync.exe), sin instalación.
Al arrancar, la aplicación:
- Se minimiza en la bandeja del sistema (system tray)
- Abre la ventana de configuración automáticamente
- Si no hay usuario configurado, muestra el diálogo para introducir el
userId(email) - Una vez configurado, inicia la sincronización con Firebase Firestore en tiempo real
Cualquier texto copiado al portapapeles se sube a Firestore. Si otra máquina con el mismo userId copia algo, se descarga y se pega en el portapapeles local.
La ventana de configuración muestra:
- Barra de estado — indicador visual (verde = activo, gris = detenido, rojo = error) y el
userIdactual - Log en tiempo real — registro de operaciones del sync (subidas, bajadas, errores)
- Botones de acción:
Cerrar aplicación— termina el proceso completamenteMinimizar a la barra de tareas— oculta la ventana; la app sigue corriendo en el trayDetener— pausa la sincronización sin cerrar la appReiniciar— reinicia la sincronización (útil tras cambiar el userId)
- Botón Cambiar — permite cambiar el
userIden cualquier momento
Para volver a abrir la ventana desde el tray: clic o doble clic sobre el icono, o clic derecho → Abrir configuración.
clipboard-sync-repo/
├── src/
│ ├── main/
│ │ ├── main.ts # Punto de entrada Electron (single-instance lock, tray, IPC)
│ │ ├── config.ts # Lectura/escritura de config.json en %LOCALAPPDATA%
│ │ ├── sync.ts # Motor de sincronización Firebase + clipboard
│ │ ├── tray.ts # Icono y menú de la bandeja del sistema
│ │ ├── window.ts # BrowserWindow + handlers IPC
│ │ └── updater.ts # Auto-actualización con electron-updater
│ ├── preload/
│ │ └── preload.ts # Bridge seguro entre main y renderer (contextBridge)
│ └── renderer/
│ ├── settings.html # Interfaz de configuración
│ ├── settings.css # Estilos dark mode
│ └── renderer.ts # Lógica del renderer
├── assets/
│ ├── logo.svg # Fuente del icono
│ └── logo.ico # Icono generado (usado por Electron y el .exe)
├── dist/ # Salida de compilación TypeScript (ignorada en git)
├── dist-release/ # Salida de empaquetado electron-builder (ignorada en git)
├── tsconfig.json # Base TypeScript
├── tsconfig.main.json # Compilación main + preload (CommonJS)
├── tsconfig.renderer.json # Compilación renderer (ESNext)
├── electron-builder.yml # Configuración de empaquetado
└── package.json
- Node.js 24+
npm install
| Script | Descripción |
|---|---|
npm run dev |
Compila TypeScript y lanza la app con Electron |
npm run build |
Solo compila TypeScript → dist/ |
npm run package |
Compila + empaqueta → dist-release/ClipboardSync.exe (portable) |
npm run generate-icon |
Convierte assets/logo.svg → assets/logo.ico (ejecutar al cambiar el SVG) |
npm run devSi %LOCALAPPDATA%\clipboard-sync\config.json no existe, la app abre el diálogo de userId automáticamente al arrancar.
Para simular una primera ejecución limpia:
Remove-Item "$env:LOCALAPPDATA\clipboard-sync" -Recurse -Force
npm run devnpm run packageGenera dist-release/ClipboardSync.exe. Requiere Modo de desarrollador de Windows activado (Configuración → Sistema → Para desarrolladores).
El workflow .github/workflows/release.yml se dispara manualmente (workflow_dispatch) con un único parámetro:
version: número de versión (ej.1.2.3, sinv)
El workflow:
- Hace
npm version $versiony crea el tag en git - Compila TypeScript
- Empaqueta con
electron-builder --win portable --publish always - Publica
ClipboardSync.exeen GitHub Releases automáticamente
El archivo de configuración se guarda en:
%LOCALAPPDATA%\clipboard-sync\config.json
Contenido:
{"userId": "email@dominio.com"}El userId debe coincidir entre todas las máquinas que quieran compartir portapapeles. Solo se usa como clave de documento en Firestore — no hay autenticación Firebase.
Cuando la app está empaquetada (app.isPackaged), comprueba automáticamente si hay una nueva versión disponible en GitHub Releases al arrancar. Si la hay, descarga e instala la actualización en segundo plano y notifica al usuario.
Al arrancar, la aplicación:
- Se minimiza en la bandeja del sistema (system tray)
- Abre la ventana de configuración automáticamente
- Si no hay usuario configurado, muestra el diálogo para introducir el
userId(email) - Una vez configurado, inicia la sincronización con Firebase Firestore en tiempo real
Cualquier texto copiado al portapapeles se sube a Firestore. Si otra máquina con el mismo userId copia algo, se descarga y se pega en el portapapeles local.
La ventana de configuración muestra:
- Barra de estado — indicador visual (verde = activo, gris = detenido, rojo = error) y el
userIdactual - Log en tiempo real — registro de operaciones del sync (subidas, bajadas, errores)
- Botones de acción:
Cerrar aplicación— termina el proceso completamenteMinimizar a la barra de tareas— oculta la ventana; la app sigue corriendo en el trayDetener— pausa la sincronización sin cerrar la appReiniciar— reinicia la sincronización (útil tras cambiar el userId)
- Botón Cambiar — permite cambiar el
userIden cualquier momento
Para volver a abrir la ventana desde el tray: clic o doble clic sobre el icono, o clic derecho → Abrir configuración.
clipboard-sync-repo/
├── src/
│ ├── main/
│ │ ├── main.ts # Punto de entrada Electron (single-instance lock, tray, IPC)
│ │ ├── config.ts # Lectura/escritura de config.json en %LOCALAPPDATA%
│ │ ├── sync.ts # Motor de sincronización Firebase + clipboard
│ │ ├── tray.ts # Icono y menú de la bandeja del sistema
│ │ ├── window.ts # BrowserWindow + handlers IPC
│ │ └── updater.ts # Auto-actualización con electron-updater
│ ├── preload/
│ │ └── preload.ts # Bridge seguro entre main y renderer (contextBridge)
│ └── renderer/
│ ├── settings.html # Interfaz de configuración
│ ├── settings.css # Estilos dark mode
│ └── renderer.ts # Lógica del renderer
├── assets/
│ ├── logo.svg # Fuente del icono
│ └── logo.ico # Icono generado (usado por Electron y el .exe)
├── dist/ # Salida de compilación TypeScript (ignorada en git)
├── dist-release/ # Salida de empaquetado electron-builder (ignorada en git)
├── tsconfig.json # Base TypeScript
├── tsconfig.main.json # Compilación main + preload (CommonJS)
├── tsconfig.renderer.json # Compilación renderer (ESNext)
├── electron-builder.yml # Configuración de empaquetado
└── package.json
- Node.js 24+
npm install
| Script | Descripción |
|---|---|
npm run dev |
Compila TypeScript y lanza la app con Electron |
npm run build |
Solo compila TypeScript → dist/ |
npm run package |
Compila + empaqueta → dist-release/ClipboardSync.exe (portable) |
npm run generate-icon |
Convierte assets/logo.svg → assets/logo.ico (ejecutar al cambiar el SVG) |
npm run devSi %LOCALAPPDATA%\clipboard-sync\config.json no existe, la app abre el diálogo de userId automáticamente al arrancar.
Para simular una primera ejecución limpia:
Remove-Item "$env:LOCALAPPDATA\clipboard-sync" -Recurse -Force
npm run devnpm run packageGenera dist-release/ClipboardSync.exe. Requiere Modo de desarrollador de Windows activado (Configuración → Sistema → Para desarrolladores).
El workflow .github/workflows/release.yml se dispara manualmente (workflow_dispatch) con un único parámetro:
version: número de versión (ej.1.2.3, sinv)
El workflow:
- Hace
npm version $versiony crea el tag en git - Compila TypeScript
- Empaqueta con
electron-builder --win portable --publish always - Publica
ClipboardSync.exeen GitHub Releases automáticamente
El archivo de configuración se guarda en:
%LOCALAPPDATA%\clipboard-sync\config.json
Contenido:
{"userId": "email@dominio.com"}El userId debe coincidir entre todas las máquinas que quieran compartir portapapeles. Solo se usa como clave de documento en Firestore — no hay autenticación Firebase.
Cuando la app está empaquetada (app.isPackaged), comprueba automáticamente si hay una nueva versión disponible en GitHub Releases al arrancar. Si la hay, descarga e instala la actualización en segundo plano y notifica al usuario.