Como desenvolver um plugin DocSpace – tutorial completo passo a passo
Em um mundo onde velocidade e colaboração são essenciais, as ferramentas de produtividade precisam acompanhar esse ritmo. Os plugins do DocSpace oferecem uma maneira moderna de aprimorar a forma como os usuários gerenciam e interagem com documentos. Neste guia, mostraremos como criar seu próprio plugin do DocSpace usando exemplos reais de nossos exemplos oficiais de plugins.
Sobre o ONLYOFFICE DocSpace Plugins SDK
ONLYOFFICE DocSpace Plugins SDK é um pacote npm baseado em mecanismos TypeScript que fornece interfaces que permitem criar seus próprios plugins e incorporá-los ao portal DocSpace. O ciclo de vida de desenvolvimento do plugin inclui planejamento, desenvolvimento, testes e uso.
Para ilustrar como usar nosso SDK de plugins, criamos um repositório com o plugin samples. Vamos mergulhar em todo o processo de desenvolvimento de plugins com base em nossos exemplos de plugins.
Planejamento
Defina a finalidade do plugin, pense onde você irá acessá-lo e quais serviços de terceiros você precisa conectar.
Etapa 1. Instale todos os pacotes e programas necessários
- ONLYOFFICE DocSpace no local.
- @onlyoffice/docspace-plugin-sdk pacote npm
Instale @onlyoffice/docspace-plugin-sdk pacote npm globalmente, use o seguinte comando:
npm i -g @onlyoffice/docspace-plugin-sdk
Etapa 2. Projete a maneira como seu plugin funcionará
Escolha o serviço que permite adicionar a funcionalidade necessária ao seu DocSpace.
Por exemplo, em nossos exemplos de plugins, usamos:
- AssemblyAI para converter fala de arquivos de áudio e vídeo em texto;
- ConvertAPI para converter documentos, planilhas, apresentações e formulários em PDF;
- Draw.io para criar, editar e inserir diagramas com aparência profissional.
Observação! Certifique-se de que a documentação do serviço esteja disponível, verifique sua licença, a disponibilidade dos métodos de API, etc. Para alguns serviços, o usuário precisa obter uma chave de API para começar a usar o plugin.
Pense em onde implementar o plugin, qual será sua estrutura, como o usuário interagirá com seus componentes, etc. Faça uma lista dos tipos e itens de plugin necessários com base nessas informações. Para mais informações, leia o Plugin types e Plugin items seções da documentação do Plugins SDK.
Por exemplo, para o plugin de conversão de fala em texto, usamos as seguintes interfaces de plugin:
- IPlugin. Obrigatório para cada plugin. Contém o plugin PluginStatus variável, usada para incorporar o plugin no DocSpace.
- ApiPlugin. Obrigatório porque implementamos um serviço de terceiros.
- ISettingsPlugin e ISettings. Usado para adicionar um bloco de configurações para a configuração do plugin. Os usuários podem acessar este bloco em Configurações -> Integração -> Plugins para ajustar os parâmetros do plugin.
- IContextMenuPlugin e IContextMenuItem. Usado para implementar uma ação de menu de contexto. No plugin de conversão de fala em texto, estará disponível para arquivos de vídeo e áudio, permitindo que os usuários convertam conteúdo em texto.
A lista de interfaces pode ser maior. Por exemplo, no plugin draw\.io:
- IMainButtonPlugin e IMainButtonItem. Usado para implementar a ação do botão principal. No plugin draw\.io, usamos o botão Ação -> Mais elementos de menu na seção Meus documentos ou na sala selecionada para criar diagramas draw\.io.
- IFilePlugin e IFileItem. Usado para interagir com os tipos de arquivo especificados. Neste caso, com os arquivos .drawio.
Crie a estrutura do plugin. Todos os arquivos necessários estão descritos aqui. Todo o resto pode ser organizado como você preferir. Por exemplo, no plugin draw\.io, colocamos o código para cada tipo de plugin em pastas separadas, enquanto no plugin de conversão de voz em texto, esse código é colocado na pasta src.
Escolha um nome para seu plugin e escreva uma descrição para ele.
Em desenvolvimento
Agora que todo o trabalho de preparação foi concluído, você pode começar a desenvolver seu plugin.
Etapa 1. Crie um modelo de plugin
Crie um modelo de plugin e configure suas configurações, que serão exibidas nas configurações do plugin DocSpace:
Para criar um modelo de plugin, abra o terminal e execute o seguinte comando npx:
npx create-docspace-plugin
Se o comando npm não estiver disponível, instale o @onlyoffice/docspace-plugin-sdk pacote npm globalmente usando o seguinte comando:
npm i -g @onlyoffice/docspace-plugin-sdk
Configure o plugin no terminal, especificando as configurações na caixa de diálogo. Todas as configurações são descritas aqui.
Para o plugin de conversão de fala em texto, você pode usar os seguintes valores:
Você pode alterar todos os parâmetros especificados posteriormente no package.json file.
Você também pode criar um plugin em qualquer projeto adicionando o @onlyoffice/docspace-plugin-sdk pacote npm como uma dependência e especificando todos os campos necessários no arquivo package.json.
Etapa 2. Configurar o ponto de entrada do plugin
O index.ts O ponto de entrada do plugin será criado automaticamente na pasta src durante a etapa de criação do modelo. Este arquivo é necessário.
Este arquivo contém todos os métodos básicos dos tipos de plugins que você selecionou na etapa anterior. Você pode alterá-lo a qualquer momento.
Se você mesmo criar um plugin, sem um modelo, para o ponto de entrada do plugin, poderá usar o código dos nossos exemplos de plugins prontos. Funcionará perfeitamente.
Etapa 3. Adicionar ícones de plugin
Cria a pasta assets na pasta raiz do plugin e adicione todos os ícones do plugin. O número de ícones e seus tamanhos dependerão dos tipos de plugin que você implementar. Para o plugin de conversão de voz em texto, precisamos dos seguintes ícones:
- O tipo de plugin padrão requer uma imagem logo . É igual ao parâmetro logo do arquivo package.json. O logotipo será exibido nas configurações do plugin DocSpace. O tamanho do ícone necessário é 48×48 px. Caso contrário, ele será compactado para este tamanho.
- O plugin do menu de contexto usa um icon no botão Converter em texto. O tamanho do ícone necessário é 16×16 px. Caso contrário, ele será compactado para este tamanho.
Este ícone também pode ser usado para o ícone do botão principal. Por exemplo, no plugin draw\.io, o mesmo ícone é usado para o menu de contexto e do botão principal.
O plugin draw\.io também usa o ícone de arquivo específico próximo aos arquivos .drawio, que são criados com o tipo de plugin de arquivo. O tamanho de ícone preferido para o formato de tabela é 32×32 px.
Etapa 4. Configurar os elementos da interface do plugin
Por exemplo, o plugin draw\.io contém dois elementos principais de interface do usuário: a janela modal e o editor de diagramas. Crie os arquivos para configurar cada elemento. Para sua conveniência, você pode colocar esses arquivos em uma pasta separada do DrawIO.
No arquivo Dialog.ts , configurar as configurações da janela modal. Especifique o IFrame componente de interface do usuário usado para incorporar o site draw\.io em uma janela modal:
export const frameProps: IFrame = {
width: "100%",
height: "100%",
name: "test-drawio",
src: "",
}
Criar o container IBox para adicionar o iframe a ele:
const body: IBox = {
widthProp: "100%",
heightProp: "100%",
children: [
{
component: Components.iFrame,
props: frameProps,
},
],
}
Configure as propriedades da modal window :
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,
No arquivo Editor.ts , Configure o editor de diagramas. Crie a função DiagramEditor com os seguintes parâmetros:
Parâmeter | Tipo | Exemplo | Descrição |
ui | string | “default” | Define o tema da interface do usuário do editor. |
dark | string | “auto” | Define o tema escuro do editor. |
off | boolean | false | Especifica se o modo offline está ativo ou não. |
lib | boolean | false | Especifica se as bibliotecas estão habilitadas ou não. |
lang | string | “auto” | Define o idioma do editor. |
url | string | `https://embed.diagrams.net` | Define a URL para o editor. |
showSaveButton | boolean | true | Especifica se o botão **Salvar** será exibido no editor. |
Em seguida, especifique métodos para trabalhar com diagramas:
Método | Descrição |
startEditing | Inicia o editor com os dados fornecidos. |
getData | Retorna os dados do diagrama. |
getTitle | Retorna o título do diagrama. |
getFormat | Retorna o formato do diagrama. |
getFrameId | Retorna o ID do quadro do editor. |
getFrameUrl | Retorna a URL para o iframe. |
handleMessage | Manipula a mensagem fornecida. |
initializeEditor | Envia a mensagem *load* para o editor. |
save | Salva os dados fornecidos. |
O código completo do *DiagramEditor* pode ser encontrado aqui
Etapa 5. Criar tipos de plugin
Agora que o plugin padrão está pronto, você pode começar a codificar outros tipos de plugin.
Cada tipo de plugin possui itens de plugin específicos. Defina o item do menu de contexto que será exibido quando você clicar com o botão direito em arquivos de áudio ou vídeo:
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,
}
Você pode adicionar mais tipos de plugins. Por exemplo, o plugin draw\.io pode ser acessado a partir do menu principal do botão, então precisamos especificar o item do botão 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],
}
Quando o item do botão principal é clicado, a janela modal aparece onde você pode digitar o nome do diagrama e abrir um arquivo .drawio vazio.
Para o plugin draw\.io, você também precisa configurar o tipo de plugin de arquivo que funciona quando o usuário abre o arquivo .drawio específico:
Defina o item do arquivo que é representado como um arquivo com a extensão específica (.drawio) e ícone:
export const drawIoItem: IFileItem = {
extension: ".drawio",
fileTypeName: "Diagram",
fileRowIcon: "drawio-32.svg",
fileTileIcon: "drawio-32.svg",
devices: [Devices.desktop],
onClick,
}
Defina o evento onClick que executará o método editDiagram sempre que o arquivo .drawio for aberto:
const onClick = async (item: File) => {
return await drawIo.editDiagram(item.id)
}
Etapa 6. Crie o tipo de plugin de configurações
Configure o tipo de plugin de configurações para fornecer aos usuários as configurações de administrador.
Crie um contêiner onde as configurações do plugin serão armazenadas:
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],
}
Na descrição das configurações, indique que é necessário gerar um token de API para poder trabalhar com o plugin.
Configure as configurações de administrador com o 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}
}
Especifique o evento onLoad, que define quais configurações do plugin serão exibidas quando o bloco de configurações for carregado.
Cada item de configuração é definido em arquivos separados. (botões, token).
Etapa 7. Crie o arquivo de código do plugin principal
Crie um arquivo na pasta src com o código principal do plugin. Este arquivo é necessário. Consulte a documentação de um serviço de terceiros para escrever este código.
Vamos ver como o arquivo AssemblyAI.ts é organizado em detalhes:
Defina a classe AssemblyAI com todas as variáveis e métodos necessários:
- Variáveis e sua descrição:
apiURL
Define a URL da API.
apiURL = ""
currentFileId
Define o ID do arquivo atual.
const currentFileId: numbernull | number = null
apiToken
Define o token da API.
apiToken = ""
- Métodos e sua descrição
createAPIUrl
Cria a URL da 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
Define a URL da API.
setAPIUrl = (url: string) => {
this.apiURL = url
}
getAPIUrl
Retorna a URL da API.
getAPIUrl = () => {
return this.apiURL
}
setAPIToken
Define o token da API.
setAPIToken = (apiToken: string) => {
this.apiToken = apiToken
}
getAPIToken
Retorna o token da API.
getAPIToken = () => {
return this.apiToken
}
fetchAPIToken
Busca o token da API.
fetchAPIToken = async () => {
const apiToken = localStorage.getItem("speech-to-text-api-token")
if (!apiToken) {
return
}
this.setAPIToken(apiToken)
plugin.updateStatus(PluginStatus.active)
}
saveAPIToken
Salva o token da 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
Define o ID para o arquivo atual.
setCurrentFileId = (id: number | null) => {
this.currentFileId = id
}
uploadFile
Carrega um arquivo 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
Transcreve o arquivo de áudio.
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 a funcionalidade do 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
}
Declare a instância da classe AssemblyAI:
const assemblyAI = new AssemblyAI()
Exporte a instância do plugin criada:
export default assemblyAI
Teste
Para verificar a funcionalidade do plugin e corrigir possíveis bugs, teste o plugin:
- Crie o plugin preparado seguindo as instruções aqui
A pasta dist será criada na pasta raiz do plugin e o arquivo do plugin será armazenado nela. Este arquivo é o plugin completo que pode ser carregado no portal DocSpace.
- Upload seu plugin para o portal DocSpace e teste completamente sua aparência e funcionalidade.
Observação! Observe que você só pode enviar seus próprios plugins na versão DocSpace do servidor.
Caso ocorra algum erro, corrija o código-fonte do seu plugin e repita o procedimento de compilação e teste.
Agora que seu plugin foi testado e funciona corretamente, você pode adicioná-lo à versão DocSpace do servidor e começar a usá-lo.
O plugin DocSpace oferece uma abordagem poderosa e intuitiva para gerenciamento e colaboração de documentos. Ao se integrar às plataformas preferidas dos usuários, ele remove obstáculos comuns e melhora a produtividade em diferentes fluxos de trabalho. Se você tiver alguma dúvida sobre os plugins DocSpace, não hesite em perguntar aos nossos desenvolvedores no fórum ONLYOFFICE (é necessário registro). Você também pode solicitar um recurso ou relatar um bug postando um problema no GitHub ou compartilhe seus plugins do DocSpace conosco e com outros usuários. Boa sorte em seus empreendimentos exploratórios!
Crie sua conta gratuita no ONLYOFFICE
Visualize, edite e colabore em documentos, planilhas, slides, formulários e arquivos PDF online.