umbraco-tiptap-statusbar-extension

Umbraco Tiptap Statusbar Extension

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "umbraco-tiptap-statusbar-extension" with this command: npx skills add umbraco/umbraco-cms-backoffice-skills/umbraco-umbraco-cms-backoffice-skills-umbraco-tiptap-statusbar-extension

Umbraco Tiptap Statusbar Extension

What is it?

A Tiptap Statusbar Extension adds components to the status bar at the bottom of the Rich Text Editor. Common uses include showing element path (breadcrumb navigation), word count, character count, or other editor state information. Unlike toolbar extensions, statusbar extensions are purely visual/informational elements.

Documentation

Always fetch the latest docs before implementing:

Related Skills

Tiptap Extension: For adding editor functionality

  • Reference skill: umbraco-tiptap-extension

Umbraco Element: For implementing the statusbar element

  • Reference skill: umbraco-umbraco-element

Context API: For accessing the Tiptap RTE context

  • Reference skill: umbraco-context-api

Workflow

  • Fetch docs - Use WebFetch on the URLs above

  • Ask questions - What information to display? Real-time updates needed?

  • Generate files - Create manifest + element based on latest docs

  • Explain - Show what was created and how to test

Minimal Examples

Manifest (manifests.ts)

import type { ManifestTiptapStatusbarExtension } from '@umbraco-cms/backoffice/extension-registry';

const manifest: ManifestTiptapStatusbarExtension = { type: 'tiptapStatusbarExtension', alias: 'My.TiptapStatusbar.WordCount', name: 'Word Count Statusbar', element: () => import('./word-count.statusbar-element.js'), forExtensions: [], // Optional: link to specific tiptap extensions meta: { alias: 'wordCount', icon: 'icon-document', label: 'Word Count', }, };

export const manifests = [manifest];

Statusbar Element (word-count.statusbar-element.ts)

import { html, css, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_TIPTAP_RTE_CONTEXT } from '@umbraco-cms/backoffice/tiptap';

@customElement('my-word-count-statusbar') export class WordCountStatusbarElement extends UmbLitElement { @state() private _wordCount = 0;

@state() private _charCount = 0;

constructor() { super();

this.consumeContext(UMB_TIPTAP_RTE_CONTEXT, (context) => {
  this.observe(context.editor, (editor) => {
    if (editor) {
      // Update counts when editor content changes
      editor.on('update', () => this.#updateCounts(editor));
      // Initial count
      this.#updateCounts(editor);
    }
  });
});

}

#updateCounts(editor: any) { const text = editor.getText(); this._charCount = text.length; this._wordCount = text.trim() ? text.trim().split(/\s+/).length : 0; }

render() { return html <span class="count">Words: ${this._wordCount}</span> <span class="count">Characters: ${this._charCount}</span> ; }

static styles = css` :host { display: flex; gap: var(--uui-size-space-4); font-size: var(--uui-type-small-size); color: var(--uui-color-text-alt); }

.count {
  padding: 0 var(--uui-size-space-2);
}

`; }

export default WordCountStatusbarElement;

declare global { interface HTMLElementTagNameMap { 'my-word-count-statusbar': WordCountStatusbarElement; } }

Element Path Statusbar (Breadcrumb Navigation)

import { html, css, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_TIPTAP_RTE_CONTEXT } from '@umbraco-cms/backoffice/tiptap';

@customElement('my-element-path-statusbar') export class ElementPathStatusbarElement extends UmbLitElement { @state() private _path: string[] = [];

constructor() { super();

this.consumeContext(UMB_TIPTAP_RTE_CONTEXT, (context) => {
  this.observe(context.editor, (editor) => {
    if (editor) {
      editor.on('selectionUpdate', () => this.#updatePath(editor));
      this.#updatePath(editor);
    }
  });
});

}

#updatePath(editor: any) { const { $from } = editor.state.selection; const path: string[] = [];

for (let depth = $from.depth; depth > 0; depth--) {
  const node = $from.node(depth);
  path.unshift(node.type.name);
}

this._path = path;

}

#handleClick(index: number) { // Could implement navigation to that element console.log('Navigate to:', this._path[index]); }

render() { return html ${this._path.map( (name, index) => html ${index > 0 ? html&#x3C;span class="separator">›&#x3C;/span> : ''} <button @click=${() => this.#handleClick(index)}>${name}</button> )} ; }

static styles = css` :host { display: flex; align-items: center; font-size: var(--uui-type-small-size); }

button {
  background: none;
  border: none;
  padding: var(--uui-size-space-1) var(--uui-size-space-2);
  cursor: pointer;
  color: var(--uui-color-text-alt);
}

button:hover {
  color: var(--uui-color-text);
  text-decoration: underline;
}

.separator {
  color: var(--uui-color-border);
  margin: 0 var(--uui-size-space-1);
}

`; }

export default ElementPathStatusbarElement;

Cursor Position Statusbar

import { html, css, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_TIPTAP_RTE_CONTEXT } from '@umbraco-cms/backoffice/tiptap';

@customElement('my-cursor-position-statusbar') export class CursorPositionStatusbarElement extends UmbLitElement { @state() private _line = 1;

@state() private _column = 1;

constructor() { super();

this.consumeContext(UMB_TIPTAP_RTE_CONTEXT, (context) => {
  this.observe(context.editor, (editor) => {
    if (editor) {
      editor.on('selectionUpdate', () => this.#updatePosition(editor));
    }
  });
});

}

#updatePosition(editor: any) { const { from } = editor.state.selection; // Simplified line/column calculation const doc = editor.state.doc; let pos = 0; let line = 1;

doc.descendants((node: any, nodePos: number) => {
  if (nodePos >= from) return false;
  if (node.isBlock) line++;
  return true;
});

this._line = line;
this._column = from - pos;

}

render() { return html &#x3C;span>Ln ${this._line}, Col ${this._column}&#x3C;/span> ; }

static styles = css :host { font-size: var(--uui-type-small-size); color: var(--uui-color-text-alt); } ; }

export default CursorPositionStatusbarElement;

Meta Properties

Property Description

alias

Unique identifier for the statusbar item

icon

Icon (used in configuration UI)

label

Display name

Accessing Editor Context

Use UMB_TIPTAP_RTE_CONTEXT to access the Tiptap editor instance and subscribe to events like:

  • update

  • Content changed

  • selectionUpdate

  • Cursor/selection changed

  • focus / blur

  • Focus state changed

That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

umbraco-backoffice

No summary provided by upstream source.

Repository SourceNeeds Review
General

umbraco-dashboard

No summary provided by upstream source.

Repository SourceNeeds Review
General

umbraco-quickstart

No summary provided by upstream source.

Repository SourceNeeds Review
General

umbraco-extension-template

No summary provided by upstream source.

Repository SourceNeeds Review