Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

21 mayo 2025Por Dea

En un mundo donde la velocidad y la colaboración son esenciales, las herramientas de productividad deben estar a la altura. Los plugins de DocSpace ofrecen una forma moderna de mejorar la manera en que los usuarios gestionan e interactúan con los documentos. En esta guía, te llevaremos a través de cómo crear tu propio plugin para DocSpace utilizando ejemplos reales de nuestros ejemplos oficiales de plugins.

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Acerca del SDK de plugins de ONLYOFFICE DocSpace

El SDK de plugins de ONLYOFFICE DocSpace es un paquete npm basado en motores TypeScript que proporciona interfaces que permiten crear tus propios plugins e integrarlos en el portal de DocSpace. El ciclo de vida del desarrollo de plugins incluye planificación, desarrollo, pruebas y uso.

Para ilustrar cómo usar nuestro SDK de plugins, hemos creado un repositorio con ejemplos de plugins. Vamos a sumergirnos en todo el proceso de desarrollo de plugins basándonos en nuestros ejemplos de plugins.

Planificación

Define el propósito de tu plugin, piensa en dónde lo vas a acceder y qué servicios de terceros necesitas conectar.

Paso 1. Instala todos los paquetes y programas necesarios

Para instalar el paquete npm @onlyoffice/docspace-plugin-sdk globalmente, usa el siguiente comando:

 npm i -g @onlyoffice/docspace-plugin-sdk

Paso 2. Diseña cómo funcionará tu plugin

Elige el servicio que te permita agregar la funcionalidad necesaria a tu DocSpace.
Por ejemplo, en nuestros ejemplos de plugins, usamos:

  • AssemblyAI para convertir el habla de archivos de audio y vídeo en texto;
  • ConvertAPI para convertir documentos, hojas de cálculo, presentaciones y formularios en PDF;
  • Draw.io  para crear, editar e insertar diagramas profesionales.

Nota: Asegúrate de que la documentación del servicio esté disponible, revisa su licencia, disponibilidad de métodos de la API, etc. Para algunos servicios, el usuario debe obtener una clave de la API para comenzar a usar el plugin.

Piensa en dónde implementarás el plugin, cuál será la estructura de tu plugin, cómo el usuario interactuará con los componentes del plugin, etc. Haz una lista de los tipos de plugins y elementos necesarios según esta información. Para más información, lee las secciones Tipos de plugins y Elementos de plugins de la documentación del SDK de plugins.

Por ejemplo, para el plugin de conversión de habla a texto, usamos las siguientes interfaces de plugins:

  • IPlugin. Requerido para cada plugin. Contiene la variable PluginStatus, utilizada para insertar el plugin en DocSpace.
  • ApiPlugin. Requerido, ya que implementamos un servicio de terceros.
  • ISettingsPluginISettings. Se usan para agregar el bloque de configuración del plugin. Los usuarios accederán a este bloque desde Ajustes -> Integración -> Plugins para ajustar los parámetros del plugin.
  • IContextMenuPluginIContextMenuItem. Se usan para implementar una acción en el menú contextual. En el plugin de conversión de habla a texto, estará disponible para archivos de vídeo y audio, permitiendo a los usuarios convertir el contenido en texto.

La lista de interfaces puede ser más larga. Por ejemplo, en el plugin de draw.io:

  • IMainButtonPluginIMainButtonItem. Se usan para implementar la acción del botón principal. En el plugin de draw.io, usamos los elementos del botón de acción -> menú Más en la sección Mis documentos o en la sala seleccionada para crear diagramas de draw.io.
  • IFilePluginIFileItem. Se usan para interactuar con los tipos de archivo especificados. En este caso, con los archivos .drawio.

Piensa en la estructura de tu plugin. Todos los archivos requeridos están descritos en la documentación. Todo lo demás puede organizarse como prefieras. Por ejemplo, en el plugin de draw.io, colocamos el código para cada tipo de plugin en carpetas separadas, mientras que en el plugin de conversión de habla a texto, este código se coloca en la carpeta src.

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Elige un nombre para tu plugin y escribe una descripción para él.

 Desarrollo del plugin

Ahora que todo el trabajo de preparación está hecho, puedes comenzar a desarrollar tu plugin.

Paso 1. Crear una plantilla de plugin

Crea una plantilla de plugin y configura sus ajustes que se mostrarán en la configuración de plugins de DocSpace:

Para crear una plantilla de plugin, abre la terminal y ejecuta el siguiente comando npx:

 npx create-docspace-plugin

Si el comando npx no está disponible, instala el paquete npm @onlyoffice/docspace-plugin-sdk globalmente utilizando el siguiente comando:

npm i -g @onlyoffice/docspace-plugin-sdk

Configura el plugin en la terminal especificando los ajustes en el diálogo. Todos los ajustes están descritos aquí.

Para el plugin de conversión de habla a texto, puedes usar los siguientes valores:

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Puedes cambiar todos los parámetros especificados más tarde en el archivo package.json.

También puedes crear un plugin en cualquier proyecto agregando el paquete npm @onlyoffice/docspace-plugin-sdk como dependencia y especificando todos los campos necesarios en el archivo package.json.

Paso 2. Configurar el punto de entrada del plugin

El punto de entrada del plugin, index.ts, se creará automáticamente en la carpeta src durante el paso de creación de la plantilla. Este archivo es requerido.

Este archivo contiene todos los métodos básicos de los tipos de plugin que seleccionaste en el paso anterior. Puedes cambiar este archivo en cualquier momento.

Si creas un plugin por ti mismo, sin plantilla, para el punto de entrada del plugin, puedes usar el código de nuestros ejemplos de plugins listos. Funcionará perfectamente.

Paso 3. Agregar iconos del plugin

Crea la carpeta assets en la carpeta raíz del plugin y agrega allí todos los iconos del plugin. El número de iconos y sus tamaños depende de los tipos de plugins que implementes. Para el plugin de conversión de habla a texto, necesitamos los siguientes iconos:

  • El tipo de plugin predeterminado requiere una imagen de logo. Esta es igual al parámetro logo del archivo package.json. El logo se mostrará en la configuración de plugins de DocSpace. El tamaño requerido del icono es 48×48 px. De lo contrario, se comprimirá a este tamaño.(no title)
  • El plugin de menú contextual usa un icono en el botón Convertir a texto. El tamaño requerido del icono es 16×16 px. De lo contrario, se comprimirá a este tamaño.

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Este icono también puede ser usado para el icono del botón principal. Por ejemplo, en el plugin de draw.io, el mismo icono se usa tanto para el menú contextual como para el menú del botón principal.

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

El plugin de draw.io también utiliza un icono específico para los archivos .drawio, que se crean con el tipo de plugin de archivo. El tamaño preferido del icono para el formato de tabla es 32×32 px.

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Paso 4. Configurar los elementos de la interfaz del plugin

Por ejemplo, el plugin de draw.io contiene dos elementos principales de UI: la ventana modal y el editor de diagramas. Crea los archivos para configurar cada elemento. Para tu conveniencia, puedes poner estos archivos en una carpeta separada llamada DrawIO.

En el archivo Dialog.ts, configura los ajustes de la ventana modal. Especifica el componente IFrame que se utiliza para incrustar el sitio web de draw.io en una ventana modal:

  export const frameProps: IFrame = {
    width: "100%",
    height: "100%",
    name: "test-drawio",
    src: "",
  }

Crea el contenedor IBox para agregarle el iframe:

 const body: IBox = {
    widthProp: "100%",
    heightProp: "100%",


    children: [
      {
        component: Components.iFrame,
        props: frameProps,
      },
    ],
  }

Configurar las propiedades de la ventan modal:

 export const drawIoModalDialogProps: IModalDialog = {
    dialogHeader: "",
    dialogBody: body,
    displayType: ModalDisplayType.modal,
    fullScreen: true,
    onClose: () => {
      const message: IMessage = {
        actions: [Actions.closeModal],
      }
      return message
    },
    onLoad: async () => {
      return {
        newDialogHeader: drawIoModalDialogProps.dialogHeader || "",
        newDialogBody: drawIoModalDialogProps.dialogBody,
      }
    },
    autoMaxHeight: true,
    autoMaxWidth: true,

En el archivo Editor.ts, configura el editor de diagramas. Crea la función DiagramEditor con los siguientes parámetros:

Parámetro Tipo Ejemplo Descripción
ui string “default” Define el tema de la interfaz del editor.
dark string “auto” Define el tema oscuro del editor.
off boolean false Especifica si el modo offline está activo.
lib boolean false Especifica si las bibliotecas están habilitadas.
lang string “auto” Define el idioma del editor.
url string `https://embed.diagrams.net` Define la URL al editor.
showSaveButton boolean true Especifica si se mostrará el botón Guardar en el editor.

Luego, especifica los métodos para trabajar con los diagramas:

Método Descripción
startEditing Inicia el editor con los datos proporcionados.
getData Devuelve los datos del diagrama.
getTitle Devuelve el título del diagrama.
getFormat Devuelve el formato del diagrama.
getFrameId Devuelve el ID del frame del editor.
getFrameUrl Devuelve la URL del iframe.
handleMessage Maneja el mensaje proporcionado.
initializeEditor Envía el mensaje load al editor.
save Guarda los datos proporcionados.

El código completo para el DiagramEditor se puede encontrar aquí.

Paso 5. Crear tipos de plugin

Ahora que el plugin predeterminado está listo, puedes comenzar a programar otros tipos de plugin.

Cada tipo de plugin tiene elementos específicos. Define el elemento del menú contextual que se mostrará cuando hagas clic derecho sobre archivos de audio o video:

export const contextMenuItem: IContextMenuItem = {
  key: "speech-to-text-context-menu-item",
  label: "Convert to text",
  icon: "speech-to-text-16.png",
  onClick: assemblyAI.speechToText,
  fileType: [FilesType.video],
  withActiveItem: true,
}

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Agregar más tipos de plugin. Por ejemplo, el plugin draw.io puede accederse desde el menú del botón principal, por lo que necesitamos especificar el elemento del botón principal:

const mainButtonItem: IMainButtonItem = {
  key: "draw-io-main-button-item",
  label: "Draw.io",
  icon: "drawio.png",
  onClick: (id: number) => {
    drawIo.setCurrentFolderId(id)


    const message: IMessage = {
      actions: [Actions.showCreateDialogModal],
      createDialogProps: {
        title: "Create diagram",
        startValue: "New diagram",
        visible: true,
        isCreateDialog: true,
        extension: ".drawio",
        onSave: async (e: any, value: string) => {
          await drawIo.createNewFile(value)
        },
        onCancel: (e: any) => {
          drawIo.setCurrentFolderId(null)
        },
        onClose: (e: any) => {
          drawIo.setCurrentFolderId(null)
        },
      },
    }
    return message
  },
  // items: [createItem],
}

Cuando se haga clic en el botón principal, aparecerá la ventana modal donde puedes escribir el nombre del diagrama y abrir un archivo vacío .drawio.

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Para el plugin draw.io, también necesitarás configurar el tipo de plugin de archivo, que funciona cuando el usuario abre un archivo .drawio específico:

Define el elemento de archivo que se representa como un archivo con la extensión específica (.drawio) y un icono.

 export const drawIoItem: IFileItem = {
     extension: ".drawio",
     fileTypeName: "Diagram",
     fileRowIcon: "drawio-32.svg",
     fileTileIcon: "drawio-32.svg",
     devices: [Devices.desktop],
     onClick,
   }

Define el evento onClick que ejecutará el método editDiagram cada vez que se abra el archivo .drawio.

  const onClick = async (item: File) => {
     return await drawIo.editDiagram(item.id)
   }

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Paso 6. Crear el tipo de plugin de configuración

Configura el tipo de plugin de configuración para proporcionar a los usuarios la configuración del administrador.

Crea un contenedor donde se colocarán los ajustes del plugin:

 const descriptionText: TextGroup = {
     component: Components.text,
     props: {
       text: "To generate API token visit https://www.assemblyai.com",
       color: "#A3A9AE",
       fontSize: "12px",
       fontWeight: 400,
       lineHeight: "16px",
     },
   }
   
   const descGroup: BoxGroup = {
     component: Components.box,
     props: {children: [descriptionText]},
   }
   
   const parentBox: IBox = {
     displayProp: "flex",
     flexDirection: "column",
     // marginProp: "16px 0 0 0",
     children: [tokenGroup, descGroup],
   }

En la descripción de los ajustes, indica que es necesario generar un token de la API para poder trabajar con el plugin.

Configura los ajustes del administrador con el objeto ISettings:

const adminSettings: ISettings = {
     settings: parentBox,
     saveButton: userButtonComponent,
     onLoad: async () => {
       assemblyAI.fetchAPIToken()
   
       tokenInput.value = assemblyAI.apiToken
   
       if (!assemblyAI.apiToken) {
         return {
           settings: parentBox,
         }
       }


       plugin.setAdminPluginSettings(adminSettings)
   
       return {settings: parentBox}
     }

Especifica el evento onLoad, que define qué ajustes del plugin se mostrarán cuando se cargue el bloque de configuración.

Cada elemento de configuración se determina en archivos separados (buttons, token).

Cómo desarrollar un plugin para DocSpace: tutorial completo paso a paso

Paso 7. Crear el archivo principal del código del plugin

Crea un archivo en la carpeta src con el código principal del plugin. Este archivo es obligatorio. Consulta la documentación del servicio de terceros para escribir este código.

Veamos cómo se organiza el archivo AssemblyAI.ts en detalle:

Definir la clase AssemblyAI con todas las variables y métodos necesarios:

  • Variables y su descripción:

apiURL

Define la URL de la API.

apiURL = ""

currentFileId

Define el ID del archivo actual.

const currentFileId: numbernull | number = null

apiToken

Define el token de la API.

 apiToken = ""
  • Métodos y su descripción:

createAPIUrl

Crea la URL de la API.

 createAPIUrl = () => {
     const api = plugin.getAPI()


     this.apiURL = api.origin.replace(/\/+$/, "")


     const params = [api.proxy, api.prefix]


     if (this.apiURL) {
       for (const part of params) {
         if (!part) {
           continue
         }
         const newPart = part.trim().replace(/^\/+/, "")
         if (newPart) {
           if (this.apiURL.length !== 0 && this.apiURL[this.apiURL.length - 1] === "/") {
             this.apiURL += newPart
           } else {
             this.apiURL += `/${newPart}`
           }
         }
       }
     }
   }

setAPIUrl

Establece la URL de la API.

 setAPIUrl = (url: string) => {
     this.apiURL = url
   }

getAPIUrl

Devuelve la URL de la API.

 getAPIUrl = () => {
     return this.apiURL
   }

setAPIToken

Establece el token de la API.

   setAPIToken = (apiToken: string) => {
     this.apiToken = apiToken
   }

getAPIToken

Devuelve el token de la API.

   getAPIToken = () => {
     return this.apiToken
   }

fetchAPIToken

Obtiene el token de la API.

   fetchAPIToken = async () => {
     const apiToken = localStorage.getItem("speech-to-text-api-token")
  
     if (!apiToken) {
       return
     }


     this.setAPIToken(apiToken)
     plugin.updateStatus(PluginStatus.active)
   }

saveAPIToken

Guarda el token de la API.

   saveAPIToken = (apiToken: string) => {
     localStorage.setItem("speech-to-text-api-token", apiToken)
  
     let status
     if (apiToken) {
       status = PluginStatus.active
     } else {
       status = PluginStatus.hide
     }
     plugin.updateStatus(status)
   }

setCurrentFileId

Establece el ID del archivo actual.

 setCurrentFileId = (id: number | null) => {
     this.currentFileId = id
   }

uploadFile

Sube un archivo que será transcrito.

uploadFile = async (apiToken: string, path: string, data: Blob) => {
     console.log(`Uploading file: ${path}`)
  
     const url = "https://api.assemblyai.com/v2/upload"
  
     try {
       const response = await fetch(url, {
         method: "POST",
         body: data,
         headers: {
           "Content-Type": "application/octet-stream",
           "Authorization": apiToken,
         },
       })
  
       if (response.status === 200) {
         const responseData = await response.json()
         return responseData["upload_url"]
       }
       console.error(`Error: ${response.status} - ${response.statusText}`)
       return null
     } catch (error) {
       console.error(`Error: ${error}`)
       return null
     }
   }

transcribeAudio

Transcribe el archivo de audio.

transcribeAudio = async (apiToken: string, audioUrl: string) => {
     console.log("Transcribing audio... This might take a moment.")
  
     const headers = {
       "authorization": apiToken,
       "content-type": "application/json",
     }
     const response = await fetch("https://api.assemblyai.com/v2/transcript", {
       method: "POST",
       body: JSON.stringify({audioUrl}),
       headers,
     })
  
     const responseData = await response.json()
     const transcriptId = responseData.id
  
     const pollingEndpoint = `https://api.assemblyai.com/v2/transcript/${transcriptId}`
  
     while (true) {
       const pollingResponse = await fetch(pollingEndpoint, {headers})
       const transcriptionResult = await pollingResponse.json()
  
       if (transcriptionResult.status === "completed") {
         return transcriptionResult
       } else if (transcriptionResult.status === "error") {
         throw new Error(`Transcription failed: ${transcriptionResult.error}`)
       } else {
         await new Promise((resolve) => {
           setTimeout(resolve, 3000)
         })
       }
     }
   }

speechToText

Implementa la funcionalidad del plugin.

speechToText = async (id: number) => {
     if (!this.apiToken) {
       return
     }


     this.setCurrentFileId(null)
  
     if (!this.apiURL) {
       this.createAPIUrl()
     }


     const response = await fetch(`${this.apiURL}/files/file/${id}`)
     const data = await response.json()
     const {viewUrl, title, folderId, fileExst} = data.response
  
     const file = await fetch(viewUrl)
  
     const fileBlob = await file.blob()
  
     const uploadUrl = await this.uploadFile(this.apiToken, viewUrl, fileBlob)
  
     const transcript = await this.transcribeAudio(this.apiToken, uploadUrl)
  
     const blob = new Blob([transcript.text], {
       type: "text/plain;charset=UTF-8",
     })
  
     const newFile = new File([blob], "blob", {
       type: "",
       lastModified: Date.now(),
     })
  
     const formData = new FormData()
     formData.append("file", newFile)
  
     const newTitle = `${title.replaceAll(fileExst, "")} text`
  
     try {
       const sessionRes = await fetch(
         `${this.apiURL}/files/${folderId}/upload/create_session`,
         {
           method: "POST",
           headers: {
             "Content-Type": "application/json;charset=utf-8",
           },
           body: JSON.stringify({
             createOn: new Date(),
             fileName: `${newTitle}.txt`,
             fileSize: newFile.size,
             relativePath: "",
           }),
         },
       )
       const response = await sessionRes.json()
       const sessionData = response.response.data
  
       const data = await fetch(`${sessionData.location}`, {
         method: "POST",
         body: formData,
       })


       const jsonData = await data.json()
       const {id: fileId} = jsonData.data
  
       return fileId
     } catch (e) {
       console.log(e)
     }


     return {
       actions: [Actions.showToast],
       toastProps: [{type: ToastType.success, title: ""}],
     } as IMessage
   }

Declarar la instancia de la clase AssemblyAI:

const assemblyAI = new AssemblyAI()

Exportar la instancia del plugin creada:

export default assemblyAI

Pruebas

Para comprobar la funcionalidad del plugin y corregir cualquier error potencial, prueba el plugin:

  • Construye el plugin preparado siguiendo las instrucciones aquí.

La carpeta dist se creará en la carpeta raíz del plugin y el archivo comprimido del plugin se colocará allí. Este archivo comprimido es el plugin completado que puede subirse al portal de DocSpace.

  •  Sube tu plugin al portal de DocSpace y prueba su apariencia y funcionalidad de manera exhaustiva.

Nota: Ten en cuenta que solo puedes subir tus propios plugins en la versión autoalojada de DocSpace

Si ocurre algún error, corrige el código fuente de tu plugin y repite el procedimiento de construcción y prueba.

Ahora que tu plugin está probado y funciona correctamente, puedes agregarlo a la versión del servidor DocSpace y comenzar a usarlo.

El plugin para DocSpace ofrece un enfoque poderoso y fácil de usar para la gestión y colaboración de documentos. Al integrarse con las plataformas preferidas por los usuarios, elimina obstáculos comunes y mejora la productividad en diferentes flujos de trabajo. Si tienes alguna pregunta sobre los plugins de DocSpace, no dudes en preguntas a nuestros desarrolladores en el foro de ONLYOFFICE. También puedes solicitar una función o reportar un error publicando un problema en GitHub o compartir tus plugins de DocSpace con nosotros y otros usuarios. ¡Te deseamos mucha suerte en tu exploración!

Crea tu cuenta gratuita de ONLYOFFICE

Visualiza, edita y colabora en documentos, hojas, diapositivas, formularios y archivos PDF en línea.