#!/usr/bin/env bash # save_onlyoffice_file — fuerza el guardado (Ctrl+S) de un documento abierto en una # instancia de ONLYOFFICE Desktop Editors en Linux/X11 y confirma que el archivo se # escribio a disco observando el cambio de mtime. # # Para que existe: OnlyOffice mantiene los cambios en memoria hasta que el usuario # guarda. Cualquier proceso que regenere el .xlsx leyendo del disco (por ejemplo un # build que refresca hojas) perderia el trabajo manual no guardado. Esta funcion # vuelca ese trabajo a disco ANTES de tocar el archivo, de modo que el paso de # actualizacion pueda preservarlo. Es el primer paso del flujo seguro: # save_onlyoffice_file -> (actualizar el archivo) -> reload_onlyoffice_file # # La ventana se localiza por el basename del archivo (OnlyOffice titula la ventana # " — ONLYOFFICE"), igual que open_onlyoffice_file. Si no hay ventana # abierta para ese basename no hay nada que guardar: se devuelve status "no_window" # con exit 0 (el disco ya es la unica fuente de verdad). # # Funcion impura: envia eventos de teclado a X11 (xdotool) y lee el estado del # sistema de archivos. Imprime una linea JSON con el resultado a stdout. # # No usamos `set -e`: los pipelines de busqueda de ventanas (xdotool|head) pueden no # matchear y no deben abortar el script. Mantenemos -u y pipefail con guardas. set -uo pipefail save_onlyoffice_file() { local file_path="${1:-}" local instance="${2:-demo}" # --- 1. Validacion de dependencias del sistema --- local dep for dep in xdotool stat; do if ! command -v "$dep" >/dev/null 2>&1; then echo "error: dependencia ausente: '$dep' (instala xdotool, coreutils)" >&2 return 1 fi done # --- 2. Validacion de argumentos --- if [ -z "$file_path" ]; then echo "error: uso: save_onlyoffice_file [instance]" >&2 return 1 fi if [ ! -f "$file_path" ]; then echo "error: el archivo no existe: '$file_path'" >&2 return 1 fi local abs_path abs_path="$(cd "$(dirname "$file_path")" && pwd)/$(basename "$file_path")" local base base="$(basename "$abs_path")" # --- 3. Localizar la ventana de OnlyOffice por basename --- local wid="" wid="$(xdotool search --name "$base" 2>/dev/null | head -1 || true)" if [ -z "$wid" ]; then printf '{"instance":"%s","file":"%s","wid":null,"status":"no_window"}\n' \ "$instance" "$abs_path" return 0 fi local hex hex="$(printf '0x%08x' "$wid" 2>/dev/null || echo "$wid")" # --- 4. mtime antes de guardar --- local mtime_before mtime_before="$(stat -c %Y "$abs_path" 2>/dev/null || echo 0)" # --- 5. Enfocar la ventana y enviar Ctrl+S --- xdotool windowactivate --sync "$wid" >/dev/null 2>&1 || true xdotool key --clearmodifiers --window "$wid" ctrl+s >/dev/null 2>&1 || true # --- 6. Esperar el guardado; auto-confirmar el dialogo de formato si aparece --- # OnlyOffice puede mostrar un dialogo modal ("mantener formato") al guardar. Si el # mtime no cambia en ~1.2s asumimos que hay un modal esperando y le enviamos Return: # acepta la opcion por defecto, que es mantener el formato actual del archivo. Si no # habia dialogo, el Return va al editor y solo mueve de celda (inofensivo: no altera # datos). El intento se repite mientras el guardado no se confirme. local mtime_after="$mtime_before" i=0 confirmed=0 local max=27 # ~8s a 0.3s por iteracion until [ "$mtime_after" -gt "$mtime_before" ] || [ "$i" -ge "$max" ]; do read -r -t 0.3 _ /dev/null || true mtime_after="$(stat -c %Y "$abs_path" 2>/dev/null || echo "$mtime_before")" i=$((i + 1)) # A partir de ~1.2s sin guardar, confirmar el dialogo modal con Return. if [ "$i" -ge 4 ] && [ "$mtime_after" -le "$mtime_before" ]; then local dlg dlg="$(xdotool getactivewindow 2>/dev/null || true)" if [ -n "$dlg" ]; then xdotool key --clearmodifiers --window "$dlg" Return >/dev/null 2>&1 || true confirmed=1 fi fi done local status="saved" if [ "$mtime_after" -le "$mtime_before" ]; then # Sin cambio de mtime: no habia nada pendiente que guardar. status="no_change" fi printf '{"instance":"%s","file":"%s","wid":"%s","status":"%s","dialog_confirmed":%s,"mtime_before":%s,"mtime_after":%s}\n' \ "$instance" "$abs_path" "$hex" "$status" "$confirmed" "$mtime_before" "$mtime_after" return 0 } # Ejecutable directo: `bash save_onlyoffice_file.sh [instance]`. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then save_onlyoffice_file "$@" fi