cuando termines y verifica que esté todo subido
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: save_onlyoffice_file
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: shell
|
||||
purity: impure
|
||||
version: 1.1.0
|
||||
description: "Fuerza el guardado (Ctrl+S) de un documento abierto en una instancia de OnlyOffice Desktop en Linux/X11 y confirma que llego a disco por cambio de mtime. Primer paso del flujo seguro guardar -> actualizar -> recargar; evita perder cambios no guardados cuando un build regenera el archivo leyendo del disco."
|
||||
signature: "save_onlyoffice_file(file_path: string, [instance: string]) -> json"
|
||||
error_type: error_go_core
|
||||
tags: [onlyoffice, desktop, x11, gui, save, persist]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
file_path: bash/functions/shell/save_onlyoffice_file.sh
|
||||
params:
|
||||
- name: file_path
|
||||
desc: "ruta al documento abierto en OnlyOffice cuyo guardado se quiere forzar. Debe existir. Se normaliza a ruta absoluta y se usa su basename para localizar la ventana."
|
||||
- name: instance
|
||||
desc: "nombre del slot/instancia para etiquetar la salida JSON (default: 'demo'). Usar el MISMO valor que en open/reload/close del mismo documento por coherencia."
|
||||
output: "linea JSON a stdout: {\"instance\":\"<i>\",\"file\":\"<abs>\",\"wid\":\"<hex>|null\",\"status\":\"saved\"|\"no_change\"|\"no_window\",\"dialog_confirmed\":0|1[,\"mtime_before\":N,\"mtime_after\":N]}. dialog_confirmed=1 si se envio Return para cerrar el dialogo modal de formato. Exit 0 salvo error de dependencia o archivo inexistente (exit 1)."
|
||||
---
|
||||
|
||||
Fuerza el guardado (Ctrl+S) de un documento abierto en una instancia de ONLYOFFICE
|
||||
Desktop Editors en Linux/X11 y confirma que el guardado llegó a disco observando el
|
||||
cambio de `mtime` del archivo.
|
||||
|
||||
Existe para cerrar una ventana de pérdida de datos: OnlyOffice mantiene los cambios
|
||||
en memoria hasta que el usuario guarda. Cualquier proceso que regenere el archivo
|
||||
leyendo del disco (un build que refresca hojas, un script de sincronización)
|
||||
perdería el trabajo manual no guardado. Esta función vuelca ese trabajo a disco
|
||||
ANTES de tocar el archivo, de modo que el paso de actualización pueda preservarlo.
|
||||
|
||||
Es el primer paso del flujo seguro de refresco:
|
||||
|
||||
```
|
||||
save_onlyoffice_file -> (actualizar el archivo en disco) -> reload_onlyoffice_file
|
||||
```
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Forzar el guardado de un xlsx abierto en la instancia "afiliados"
|
||||
bash bash/functions/shell/save_onlyoffice_file.sh \
|
||||
/home/enmanuel/afiliados/programas_afiliados.xlsx afiliados
|
||||
# {"instance":"afiliados","file":"/home/enmanuel/afiliados/programas_afiliados.xlsx","wid":"0x0a20002a","status":"saved","mtime_before":1718380000,"mtime_after":1718380042}
|
||||
|
||||
# Via fn run (tras fn index)
|
||||
./fn run save_onlyoffice_file /home/enmanuel/afiliados/programas_afiliados.xlsx afiliados
|
||||
|
||||
# Encadenado con la actualización y la recarga (flujo seguro completo)
|
||||
bash bash/functions/shell/save_onlyoffice_file.sh "$XLSX" afiliados
|
||||
python build_xlsx.py # regenera solo las hojas gestionadas
|
||||
./fn run reload_onlyoffice_file "$XLSX" afiliados
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Llámala SIEMPRE justo antes de regenerar o modificar en disco un archivo que el
|
||||
usuario pueda tener abierto en OnlyOffice, para no pisar sus cambios sin guardar.
|
||||
Es el primer eslabón del flujo guardar -> actualizar -> recargar. Si no hay ventana
|
||||
abierta para ese archivo, es un no-op seguro (status `no_window`).
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Orden crítico**: guarda ANTES de actualizar el archivo. Si actualizas primero y
|
||||
guardas OnlyOffice después, OnlyOffice sobrescribe tu actualización con su copia
|
||||
en memoria (vieja). El flujo correcto es save -> update -> reload.
|
||||
- **status `no_change`**: el `mtime` no cambió. Normalmente significa que no había
|
||||
cambios pendientes (no es un error).
|
||||
- **Auto-confirmación del diálogo de formato (v1.1.0)**: si tras Ctrl+S el guardado no
|
||||
se completa en ~1.2s, la función asume que OnlyOffice mostró un diálogo modal
|
||||
("mantener formato") y le envía Return, que acepta la opción por defecto (mantener el
|
||||
formato actual). El campo `dialog_confirmed` indica si se envió. Si no había diálogo,
|
||||
el Return va al editor y solo mueve de celda (no altera datos). Para suprimir el
|
||||
diálogo de forma permanente, desmárcalo en OnlyOffice: Configuración avanzada →
|
||||
desactivar el aviso de formato al guardar.
|
||||
- **status `no_window`**: no hay ninguna ventana cuyo título contenga el basename del
|
||||
archivo. No hay nada que guardar; el disco ya es la única fuente de verdad.
|
||||
- **Detección por basename**: dos archivos con el mismo nombre en rutas distintas
|
||||
colisionan al localizar la ventana (igual que open/reload).
|
||||
- **X11 obligatorio**: depende de `xdotool` (y `stat` de coreutils). No funciona en
|
||||
Wayland puro sin XWayland.
|
||||
- **Foco**: la función activa la ventana (`windowactivate --sync`) para que Ctrl+S
|
||||
llegue al editor. Roba el foco un instante; es esperable.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v1.1.0 (2026-06-15) — auto-confirma el diálogo modal "mantener formato" enviando
|
||||
Return a la ventana activa cuando el guardado no se completa en ~1.2s; añade el campo
|
||||
`dialog_confirmed` a la salida JSON.
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/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
|
||||
# "<basename> — 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 <file_path> [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 2>/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 <file> [instance]`.
|
||||
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
||||
save_onlyoffice_file "$@"
|
||||
fi
|
||||
Reference in New Issue
Block a user