textshine-browser-extension-bridge
TypeScript icon, indicating that this package has built-in type declarations

0.0.1 • Public • Published

Textshine Bridge

Die Textshine Bridge ist ein API um von einer Website aus Textshine steuern zu können. Achtung: Die Textshine-Browser-Extension muss beim Nutzer installiert sein, ansonsten wird ein Error gethrowed. Der primäre usecase aktuell ist es eine Korrektion aus einer CMS Integration zu starten, in Zukunft könnten weitere Funktionen folgen.

Installation

npm install textshine-browser-extension-bridge

Nutzung

Entweder direkter Import des SDKs (vermeidet global scope pollution):

// ESM-Import (preferred)
import { startCorrection } from 'textshine-browser-extension-bridge'
// ODER CommonJS-Import
const { startCorrection } = require('textshine-browser-extension-bridge');
// Nutzung
startCorrection(params: { type, src }, callback)

oder register script welches das API ins window objekt registriert (Achtung, funktioniert nicht bei SSR weil es window-Existenz voraussetzt):

// ESM (preferred)
import 'textshine-browser-extension-bridge/register'
// or CommonJS
require('textshine-browser-extension-bridge/register');
window.textshineBridge.startCorrection(params: { type, src }, callback)

Sollte kein buildprozess existieren lässt sich auch die Datei node_modules/textshine-browser-extension-bridge/dist/browser/textshine-bridge.min.js kopieren und mittels vanilla html script tag einbetten.

API

Nach Installation der Extension exposed diese ein API zur Nutzung innerhalb einer Website.

Signatur:

type TextshineBridgeCorrectionType = 'html' | 'plaintext'
type TextshineBridgeCorrectionPayload = {
  type: TextshineBridgeCorrectionType
  src: string
}
window.textshineBridge.startCorrection: (params: TextshineBridgeCorrectionPayload, callback: (result: string) => void) => void

Beispiel:

import { startCorrection } from 'textshine-browser-extension-bridge'
const type = 'html'
const src = 'ein gültiger HTML string'
const callback = (result) => someElement.innerHTML = result // result ist das korrigierte HTML
startCorrection(params: { type, src }, callback)

Document Based Editors

Bei CMS/Editoren bei denen sich der gesamte Content gemeinsam ausgeben und wieder ersetzen lässt, muss dieser lediglich als Ganzes in HTML serialisiert und ggbfls. deserialisiert werden.

// wenn html direkt supported wird
const src = getHTMLFromCMS()
const callback = (result) => setHTMLIntoCMS(result)
startCorrection(params: { type: 'html', src }, callback)

// wenn serialisiert und deserialisiert werden muss
const originalSrc = getDataFromCMS()
const src = convertDataToHTML(originalSrc)
const callback = (result) => setDataIntoCMS(convertHTMLToData(result))
startCorrection(params: { type: 'html', src }, callback)

Block Based Editors

Bei CMS/Editoren die block-basiert sind wie Wordpress Gutenberg, bei denen einzelne Blöcke und nicht ein gesamtes Dokument ersetzt werden sollen, muss ein präziserer import/export stattfinden:

  • Ein Importer liest alle Blöcke über das CMS native API
  • Der Importer läuft dann über alle Blöcke und wandelt korrigierbare Blöcke in HTML Fragments um
  • Zu jedem Block wird eine ID erstellt um Korrekturen rückführen zu können
  • Ein exporter wird erstellt, welcher anhand der IDs im DOM Änderungen schreibt
  • Das generierte HTML wird an die Textshine Bridge geschickt mit einem callback des exporters
  • Nach Abschluss der Korrektur wird der callback gecalled und der exporter ersetzt Inhalte im DOM

Beispiel Import Wordpress Gutenberg Blocks

// Recursive function for processing blocks
function processBlocksForImport(blocks: WPBlock[]): { html: string; editableBlocks: WPBlock[] } {
  let html = ''
  const editableBlocks: WPBlock[] = []

  // note that any blocks that are not handled in here are ignored
  for (const block of blocks) {
    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/paragraph/block.json
    if (block.name === 'core/paragraph') {
      html += `<p data-wp-block-id="${block.clientId}">${block.content || ''}</p>`
      editableBlocks.push(block)
    }

    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/heading/block.json
    else if (block.name === 'core/heading') {
      const level = block.attributes.level || 2
      html += `<h${level} data-wp-block-id="${block.clientId}">${block.content || ''}</h${level}>`
      editableBlocks.push(block)
    }

    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/list/block.json
    else if (block.name === 'core/list') {
      // Special handling for lists - no data-wp-block-id attribute
      const isOrdered = block.attributes.ordered === true
      const listTag = isOrdered ? 'ol' : 'ul'

      html += `<${listTag}>`

      // Process list items
      if (block.innerBlocks.length > 0) {
        const result = processBlocksForImport(block.innerBlocks)
        html += result.html
        editableBlocks.push(...result.editableBlocks)
      }

      html += `</${listTag}>`
    }

    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/list-item/block.json
    else if (block.name === 'core/list-item') {
      html += `<li data-wp-block-id="${block.clientId}">${block.content || ''}</li>`
      editableBlocks.push(block)
    }

    // for container blocks we only care about their innerBlocks
    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/group/block.json
    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/columns/block.json
    // https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/column/block.json
    else if (['core/group', 'core/columns', 'core/column'].includes(block.name)) {
      // For container blocks, we only care about their innerBlocks
      if (block.innerBlocks.length > 0) {
        const result = processBlocksForImport(block.innerBlocks)
        html += result.html
        editableBlocks.push(...result.editableBlocks)
      }
    }
  }

  return { html, editableBlocks }
}

// Keep your original exporter function exactly as is
async function exporter(updatedContent: string, blocks: WPBlock[]) {
  // prepare updates array
  const updates = []

  // parse updatedContent html
  const parser = new DOMParser()
  const doc = parser.parseFromString(updatedContent, 'text/html')

  // iterate over all blocks that need to be updated
  for (const block of blocks) {
    // find the block in the DOM
    const blockElement = doc.querySelector(`[data-wp-block-id="${block.clientId}"]`)

    // if the block is not found, we fucked up truly, so its fair to throw
    if (!blockElement) {
      throw new Error('Gutenberg export: block not found')
    }

    // get the content of the block
    const blockContent = blockElement.innerHTML

    // push the update
    updates.push({
      clientId: block.clientId,
      type: block.name,
      attribute,
      value: blockContent
    })
  }

  // send updates to background
  if (updates.length > 0) {
    for (const blockUpdate of blockUpdates) {
      const updateToSet: Record<string, unknown> = {}
      updateToSet[blockUpdate.attribute] = blockUpdate.value
      window.wp.data.dispatch('core/block-editor').updateBlockAttributes(blockUpdate.clientId, updateToSet)
    }
  }
}

// bind this importer onto a CMS button click for example
const importer = async () => {
  const wpblocks = await window.wp.data.select('core/block-editor').getBlocks()

  // most pages wont have wp blocks, so return as early as possible
  if (!wpblocks) {
    return []
  }

  // Process blocks recursively starting with all blocks
  // This will automatically filter for compatible ones
  let { html: combinedHtml, editableBlocks } = processBlocksForImport(wpblocks)

  // If no editable blocks were found, return empty array
  if (editableBlocks.length === 0) {
    return []
  }

  const callback = (updatedSource: correctedHTML) => {
    exporter(updatedSource, editableBlocks)
  }

  // call the textshine bridge
  window.textshineBridge.startCorrect({ type: html, src: combinedHtml }, callback)
}

Readme

Keywords

none

Package Sidebar

Install

npm i textshine-browser-extension-bridge

Weekly Downloads

3

Version

0.0.1

License

All Rights Reserved

Unpacked Size

19.3 kB

Total Files

15

Last publish

Collaborators

  • tom2strobl
  • aseifert