mcpField
U
umbraco-tiptap-statusbar-extension

umbraco-tiptap-statusbar-extension

github.com/umbraco开发27374 次安装6 周前更新
代码仓库

关于 umbraco-tiptap-statusbar-extension

用于构建 Umbraco Tiptap 状态栏扩展的技能,可在编辑器中显示信息。它指导开发者查阅最新文档、创建清单与元素,并说明测试方式。

主要功能

  1. 01状态栏扩展指导
  2. 02清单与元素示例
  3. 03访问编辑器上下文和事件
  4. 04优先查阅文档的工作流

适用场景

  1. 01显示字数或字符数
  2. 02呈现元素路径导航
  3. 03跟踪光标位置或编辑器状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/umbraco/umbraco-cms-backoffice-skills --skill umbraco-tiptap-statusbar-extension

安装后在 Claude Code 中输入/umbraco-tiptap-statusbar-extension确认已加载。

详细介绍

Umbraco Tiptap 状态栏扩展

这是什么?

Tiptap 状态栏扩展会在富文本编辑器底部的状态栏中添加组件。常见用途包括显示元素路径(面包屑导航)、字数、字符数或其他编辑器状态信息。与工具栏扩展不同,状态栏扩展纯粹用于显示信息。

文档

实现前请务必获取最新文档:

相关技能

  • Tiptap 扩展:用于添加编辑器功能

    • 参考技能:umbraco-tiptap-extension
  • Umbraco 元素:用于实现状态栏元素

    • 参考技能:umbraco-umbraco-element
  • 上下文 API:用于访问 Tiptap RTE 上下文

    • 参考技能:umbraco-context-api

工作流

  1. 获取文档 - 对上述 URL 使用 WebFetch
  2. 提出问题 - 要显示哪些信息?是否需要实时更新?
  3. 生成文件 - 根据最新文档创建清单和元素
  4. 说明 - 展示已创建的内容以及如何测试

最简示例

清单(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];
状态栏元素(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;
  }
}
元素路径状态栏(面包屑导航)
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`<span class="separator">›</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;
光标位置状态栏
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`
      <span>Ln ${this._line}, Col ${this._column}</span>
    `;
  }

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

export default CursorPositionStatusbarElement;

元属性

属性描述
alias状态栏项的唯一标识符
icon图标(用于配置界面)
label显示名称

访问编辑器上下文

使用 UMB_TIPTAP_RTE_CONTEXT 访问 Tiptap 编辑器实例,并订阅以下事件:

  • update - 内容已更改
  • selectionUpdate - 光标/选区已更改
  • focus / blur - 焦点状态已更改

就是这样!请始终获取最新文档,保持示例精简,并生成完整可运行的代码。

评论

支持 Markdown