Anleitung: Plugin

Das Schach-Plugin ist ein einfaches Beispiel dafür, wie leicht eine Erweiterung mit Echtzeit-Kollaboration für Briefing erstellt werden kann. Der vollständige Quellcode ist unter TODO verfügbar.

index-client.ts

Die Klasse AppPlugin ist der Einstiegspunkt. Zuerst muss ein unbenutzter name vergeben werden. In der setup Methode steht ein AppPluginContext zur Verfügung, der alle weiteren Einstiegspunkte der Applikation enthält, um Funktionalität an bestimmten Stellen hinzuzufügen. Im folgenden Beispiel wird eine Vue-Komponente für den Präsentationsbereich registriert.

import type { AppPlugin, AppPluginContext } from '@briefing/_types/plugin'
import { defineAsyncComponent } from 'vue'
import { pluginName } from './_types'

class Plugin implements AppPlugin {
  name = pluginName

  async setup(context: AppPluginContext): Promise<void> {
    context.addComponent({
      name: pluginName,
      title: 'Chess',
      placement: 'presentation',       
      component: defineAsyncComponent(() => import('./components/chess.vue')) as any,
    })
  }
}

export default Plugin

Vue Component

Der AppGlobalContext steht jeder Komponente über inject zur Verfügung. Realtime Sync ist ebenfalls ein Plugin, das seine Funktionalität über state anderen Plugins zur Verfügung stellt. appState.sync.getMO(pluginName, state).value registriert ein einfaches Sync-Objekt, das nur Records aber keine Arrays unterstützt (LWW CRDT). Dieses kann nun ganz normal als Vue reactive verwendet werden. Alle Änderungen werden automatisch und ohne weiteres Zutun mit allen anderen Clients im Raum geteilt. Neben diesen Managed Object (MO) steht auch eine Unterstützung von Yjs zur Verfügung, die z.B. in den Präsentationen Zeichenen und TipTap verwendet wird.

<script setup lang="ts">
import { pluginName } from '@/_types'
import type { AppGlobalContext } from '@briefing/_types/plugin'
import { inject, ref, watch } from 'vue'
import { BoardApi, BoardConfig, Move, TheChessboard } from '@/chessboard'
import { Logger, LoggerInterface } from "zeed"

import './chess.styl'

type ChessState = Record<number, Move>

let state: ChessState = {}

try {
  const app = inject<AppGlobalContext>('app')
  if (app) {
    const appState = app.state as any
    state = appState.sync.getMO(pluginName, state).value
  }
}
catch (err) {
  log.warn('chess err', err)
}

let ctr = 0

const boardConfig: BoardConfig = {
  events: {
    move: (from, to, capture) => {
      state[ctr++] = { from, to }
    },
  },
}

let boardAPI: BoardApi

watch(() => Object.keys(state), (l) => {
  while(state[ctr]) {
    boardAPI?.move(state[ctr++])
  }
})
</script>

<template>
  <div class="chess">
    <TheChessboard @board-created="(api: any) => (boardAPI = api)" :board-config="boardConfig" />
  </div>
</template>