npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@itshixun/qckeditor-plugin-audio

v1.0.1

Published

CKEditor 5 plugin for audio upload and playback

Readme

@itshixun/qckeditor-plugin-audio

CKEditor 5 插件:音频上传与播放。在编辑器中插入 <audio> 元素,支持本地上传和直接插入音频 URL,可显示音频名称(figcaption)。

安装

npm install @itshixun/qckeditor-plugin-audio
# 或
pnpm add @itshixun/qckeditor-plugin-audio

依赖 ckeditor5(peerDependencies,需自行安装)。

基本使用

1. 引入样式

import '@itshixun/qckeditor-plugin-audio/dist/index.css';

2. 注册插件

import { ClassicEditor } from 'ckeditor5';
import { QstAudio } from '@itshixun/qckeditor-plugin-audio';

const editor = await ClassicEditor.create(element, {
  plugins: [
    // ... 其他插件
    QstAudio,
  ],
  toolbar: [
    // ... 其他按钮
    'uploadAudio',  // 工具栏按钮名称:上传音频
  ],
  qstAudio: {
    upload: {
      types: ['mp3', 'wav', 'ogg'],
      maxSize: 50 * 1024 * 1024, // 50MB
    },
    progress: {
      showProgressBar: true,
      useReadOnlyLock: false,
    },
  },
});

3. Vue 中使用

<script setup lang="ts">
import { ClassicEditor } from 'ckeditor5';
import { QstAudio } from '@itshixun/qckeditor-plugin-audio';

const config = {
  editor: ClassicEditor,
  plugins: [/* ... */, QstAudio],
  toolbar: ['bold', 'italic', 'uploadAudio'],
  qstAudio: {
    upload: {
      types: ['mp3', 'wav'],
      maxSize: 20 * 1024 * 1024,
    },
  },
};
</script>

<template>
  <ckeditor :editor="config.editor" :config="config" />
</template>

配置项

| 属性 | 类型 | 必填 | 说明 | |------|------|------|------| | upload.types | string[] | 否 | 允许上传的文件扩展名列表,如 ['mp3', 'wav', 'ogg'] | | upload.maxSize | number | 否 | 最大文件大小(字节),超出会提示警告 | | progress.showProgressBar | boolean | 否 | 是否显示上传进度条,默认 true | | progress.useReadOnlyLock | boolean | 否 | 上传期间是否锁定编辑器为只读模式,默认 false |

上传配置

本插件依赖 CKEditor 5 的 FileRepository 插件处理文件上传。你需要配置一个自定义的 UploadAdapter,否则上传功能无法正常工作。

自定义 UploadAdapter 实现示例

import type { FileLoader, UploadAdapter } from 'ckeditor5';

class MyUploadAdapter implements UploadAdapter {
  private loader: FileLoader;
  private xhr?: XMLHttpRequest;

  constructor(loader: FileLoader) {
    this.loader = loader;
  }

  upload(): Promise<{ default: string }> {
    return this.loader.file.then(
      (file) =>
        new Promise((resolve, reject) => {
          this._initRequest();
          this._initListeners(resolve, reject, file!);
          this._sendRequest(file!);
        })
    );
  }

  abort(): void {
    if (this.xhr) {
      this.xhr.abort();
    }
  }

  private _initRequest(): void {
    const xhr = (this.xhr = new XMLHttpRequest());
    xhr.open('POST', 'https://your-api.com/upload', true);
    xhr.responseType = 'json';
  }

  private _initListeners(
    resolve: (value: { default: string }) => void,
    reject: (reason?: string) => void,
    file: File
  ): void {
    const xhr = this.xhr!;
    const loader = this.loader;
    const genericErrorText = `无法上传文件: ${file.name}.`;

    xhr.addEventListener('error', () => reject(genericErrorText));
    xhr.addEventListener('abort', () => reject());
    xhr.addEventListener('load', () => {
      const response = xhr.response;

      if (!response || response.error) {
        return reject(response?.error?.message || genericErrorText);
      }

      // 响应必须包含 default 字段(音频 URL)
      resolve({ default: response.url });
    });

    if (xhr.upload) {
      xhr.upload.addEventListener('progress', (evt) => {
        if (evt.lengthComputable) {
          loader.uploadTotal = evt.total;
          loader.uploaded = evt.loaded;
        }
      });
    }
  }

  private _sendRequest(file: File): void {
    const data = new FormData();
    data.append('file', file);
    this.xhr!.send(data);
  }
}

在编辑器中注册 UploadAdapter

function MyUploadAdapterPlugin(editor: any) {
  editor.plugins.get('FileRepository').createUploadAdapter = (loader: FileLoader) => {
    return new MyUploadAdapter(loader);
  };
}

const editor = await ClassicEditor.create(element, {
  plugins: [
    // ... 其他插件
    MyUploadAdapterPlugin, // 必须先注册,再注册 QstAudio
    QstAudio,
  ],
  toolbar: ['uploadAudio'],
});

重要:

  • MyUploadAdapterPlugin 必须在 QstAudio 之前注册到 plugins 数组中
  • 上传响应必须返回 { default: '音频URL' } 格式,default 字段会被提取为音频的 src
  • 上传失败时会自动移除占位音频元素,并通过 Notification 插件显示警告

命令说明

插件注册了两个命令,分别对应不同的使用场景:

audioInsert — 插入已有音频

通过音频 URL 直接插入已有音频(不上传):

editor.execute('audioInsert', {
  src: 'https://example.com/audios/demo.mp3',
  name: '示例音频.mp3', // 可选,显示在 figcaption 中
  controls: true, // 可选,默认 true
});

uploadAudio — 上传本地音频

通常由工具栏按钮自动触发,也可手动调用:

const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = fileInput.files![0];

editor.execute('uploadAudio', { file });

输出 HTML

保存态(Data Downcast)

<figure class="audio ck-qst-audio">
  <audio controls src="https://example.com/audios/demo.mp3"></audio>
  <figcaption>示例音频.mp3</figcaption>
</figure>

编辑态(Editing Downcast)

编辑态会渲染为 CKEditor 5 的 Widget,带有选中高亮和进度条:

<figure class="audio ck-qst-audio ck-widget" contenteditable="false">
  <audio controls src="https://example.com/audios/demo.mp3"></audio>
  <figcaption>示例音频.mp3</figcaption>
  <!-- 上传时显示进度条 -->
  <div class="ck-qst-audio-progress-bar" style="width: 45%"></div>
</figure>

数据兼容性

插件在 upcast(粘贴/加载 HTML)时兼容旧版格式:

| 新版本 | 旧版本 | |--------|--------| | <figure class="ck-qst-audio"> | <figure class="audio"> | | <audio controls src="..."> | <audio class="ck-file ck-file-audio" controls src="..."> |

旧版 HTML 会被自动转换为新版数据模型,保存时输出新版格式(同时保留 audio 类以兼容旧样式):

<!-- 旧版输入 -->
<figure class="audio">
  <audio controls src="..."></audio>
  <figcaption>名称</figcaption>
</figure>

<!-- 新版输出 -->
<figure class="audio ck-qst-audio">
  <audio controls src="..."></audio>
  <figcaption>名称</figcaption>
</figure>

类型支持

导入插件后,EditorConfig 类型自动扩展,TypeScript 可直接识别 qstAudio 配置:

const config: EditorConfig = {
  // 无类型错误,qstAudio 已声明
  qstAudio: {
    upload: { types: ['mp3'], maxSize: 1024 * 1024 },
  },
};

导出 API

import {
  QstAudio,              // 主插件(Editing + Upload)
  QstAudioEditing,       // 编辑插件(Schema + Conversion + audioInsert 命令)
  QstAudioInsertCommand, // 插入已有音频的命令
  QstAudioUpload,        // 上传功能聚合插件
  QstAudioUploadEditing, // 上传编辑插件(自动上传处理 + 进度管理)
  QstAudioUploadUI,      // 上传 UI 插件(工具栏按钮)
  QstAudioUploadProgress, // 上传进度条插件
  QstAudioUploadCommand, // 上传音频的命令
  insertAudio,           // 工具函数:插入音频元素
  isAudioAllowed,        // 工具函数:检查是否允许插入音频
  type QstAudioConfig,
  type AudioInsertCommandOptions,
  type UploadAudioCommandOptions,
} from '@itshixun/qckeditor-plugin-audio';