文档/Clipboard SDK

Clipboard SDK

通用开发

Clipboard SDK

概述

Clipboard SDK 提供插件访问系统剪贴板的能力,包括:

  • 基础操作:读写剪贴板内容(文本、HTML、图片、文件)
  • 历史管理:查询、搜索、删除剪贴板历史记录
  • 复制粘贴:将内容复制到剪贴板并自动粘贴到当前活动应用
  • 选中文本:读取当前活动应用的文本选区,并在 fallback 后恢复原剪贴板内容
  • 自动标签:为文本内容打标签(URL、API Key、Token、账号/密码、邮箱),便于分类与匹配

所有操作通过 TuffTransport 在主进程执行,避免 WebContents 焦点问题导致的剪贴板操作失败。

通信策略(Transport First)

  • useClipboard() 已切换到 ClipboardEvents.* 事件域(如 getHistory、read、copyAndPaste)。
  • Clipboard SDK 不再依赖 clipboard:* raw channel 事件作为主通道。
  • 插件代码必须使用 SDK / typed transport 入口。历史 clipboard:* raw channel 名称仅作为迁移参考,不再作为受支持的扩展面。
  • system.captureSelection() 使用 typed App/System event,要求 verified plugin 与 clipboard.read;不暴露 raw selection channel。

介绍

快速开始

EXAMPLE.TYPESCRIPT
import { system, useClipboard } from '@talex-touch/utils/plugin/sdk'

const clipboard = useClipboard()

// 写入文本
await clipboard.writeText('Hello World')

// 读取剪贴板
const content = await clipboard.read()
console.log(content.text, content.formats)

// 复制并粘贴到当前应用
await clipboard.copyAndPaste({ text: 'Pasted content' })

// 读取当前活动应用的选中文本
const selection = await system.captureSelection()
if (!selection.text) console.warn(selection.issueCode)

API 参考

useClipboard()

返回统一的剪贴板 SDK 实例。

EXAMPLE.TYPESCRIPT
const clipboard = useClipboard()

选中文本捕获

system.captureSelection() / captureSelectedText()

读取当前活动应用的文本选区。宿主会在访问辅助功能 API、快捷键或剪贴板前验证插件 identity 与 clipboard.read 权限。macOS 优先使用 AXSelectedText;fallback 会保存全部剪贴板格式、发送平台复制快捷键并恢复原快照。

返回值包含 text、supportLevel、capturedAt,以及可选的 issueCode、issueMessage、limitations。empty、disabled、failed、unsupported 都是显式非成功状态。不得把选中文本写入普通日志、持久历史或审计 metadata。

EXAMPLE.TYPESCRIPT
import { system } from '@talex-touch/utils/plugin/sdk'

const result = await system.captureSelection()
if (!result.text) {
  console.warn(result.issueCode, result.issueMessage)
  return
}

// 仅在本次用户触发的动作中使用 result.text。

基础操作

writeText(text)

写入纯文本到剪贴板。

EXAMPLE.TYPESCRIPT
await clipboard.writeText('Hello World')

write(options)

写入多种格式内容到剪贴板。

EXAMPLE.TYPESCRIPT
// 写入文本和 HTML
await clipboard.write({
  text: 'Hello',
  html: '<b>Hello</b>'
})

// 写入图片(data URL)
await clipboard.write({
  image: 'data:image/png;base64,...'
})

// 写入文件路径
await clipboard.write({
  files: ['/path/to/file.txt', '/path/to/image.png']
})

参数:

字段类型说明
textstring纯文本内容
htmlstringHTML 格式内容
imagestring图片 data URL
filesstring[]文件路径数组

read()

读取当前剪贴板内容。

EXAMPLE.TYPESCRIPT
const content = await clipboard.read()
// {
//   text: 'Hello',
//   html: '<b>Hello</b>',
//   hasImage: false,
//   hasFiles: false,
//   formats: ['text/plain', 'text/html']
// }

返回值:

字段类型说明
textstring文本内容
htmlstringHTML 内容
hasImageboolean是否包含图片
hasFilesboolean是否包含文件
formatsstring[]可用格式列表

readImage()

读取剪贴板中的图片。

EXAMPLE.TYPESCRIPT
const image = await clipboard.readImage()
if (image) {
  console.log(image.dataUrl, image.width, image.height)
}

返回值:{ dataUrl: string, width: number, height: number } | null

getHistoryImageUrl(id)

获取历史图片条目的原图地址(tfile://),适合避免在插件侧传输大体积 base64。

EXAMPLE.TYPESCRIPT
const url = await clipboard.getHistoryImageUrl(123)
if (url) {
  console.log('original image:', url)
}

history.annotate({ id, note, tags })

给一条历史记录写上用户自己的备注和标签。

两个字段互相独立:只传 note 不会动标签,只传 tags 不会动备注,所以一个只编辑备注的输入框不必始终持有最新标签。传 null 或空字符串清空备注,传空数组清空标签。

返回的是实际入库的内容——主进程会去首尾空白、把换行压成空格、按不区分大小写去重、并按上限截断(备注 200 字符,标签 24 字符 / 最多 12 个)。拿返回值回填界面,而不是回填用户输入的原文。

标签和备注写在记录的 metadata 里,而 metadata 已经在关键词搜索的匹配范围内,所以打完标签立刻就能搜到。

EXAMPLE.TYPESCRIPT
const { note, tags } = await clipboard.history.annotate({
  id: 123,
  note: 'ego lite 的生产 key',
  tags: ['prod', 'openai'],
})

返回值:{ updated: boolean, note: string | null, tags: string[] },updated 为 false 表示这条记录已经不存在。

previewHistoryImage(id)

把历史图片条目交给操作系统自己的预览器打开(macOS 上是「预览」,其他平台是系统注册的默认程序)。

传的是记录 id 而不是路径:主进程自己在剪贴板图片目录里查文件,所以插件无法借此打开目录之外的任何文件。记录里没有可用原图时返回 false。

EXAMPLE.TYPESCRIPT
const opened = await clipboard.previewHistoryImage(123)
if (!opened) {
  console.warn('这条记录没有可预览的原图')
}

返回值:boolean

readFiles()

读取剪贴板中的文件路径。

EXAMPLE.TYPESCRIPT
const files = await clipboard.readFiles()
// ['/path/to/file1.txt', '/path/to/file2.png']

clear()

清空剪贴板。

EXAMPLE.TYPESCRIPT
await clipboard.clear()

复制并粘贴

copyAndPaste(options)

将内容写入剪贴板,然后模拟粘贴快捷键到当前活动应用。这是插件"输出"内容到用户当前应用的推荐方式。

EXAMPLE.TYPESCRIPT
// 粘贴文本
await clipboard.copyAndPaste({ text: 'Hello' })

// 粘贴带格式的文本
await clipboard.copyAndPaste({
  text: 'Hello',
  html: '<b>Hello</b>'
})

// 粘贴图片
await clipboard.copyAndPaste({
  image: 'data:image/png;base64,...'
})

// 粘贴文件
await clipboard.copyAndPaste({
  files: ['/path/to/file.txt']
})

// 自定义延迟和行为
await clipboard.copyAndPaste({
  text: 'Hello',
  delayMs: 200,        // 粘贴前延迟(默认 150ms)
  hideCoreBox: true    // 粘贴前隐藏 CoreBox(默认 true)
})

参数:

字段类型说明
textstring文本内容
htmlstringHTML 内容
imagestring图片 data URL
filesstring[]文件路径
delayMsnumber粘贴前延迟毫秒数
hideCoreBoxboolean是否在粘贴前隐藏 CoreBox

返回值:Promise<boolean> - 操作是否成功


历史记录

通过 clipboard.history 访问历史记录相关操作。

history.getLatest()

获取最新的剪贴板记录。

EXAMPLE.TYPESCRIPT
const latest = await clipboard.history.getLatest()
if (latest) {
  console.log(latest.type, latest.content)
}

history.getHistory(options)

分页获取剪贴板历史。

EXAMPLE.TYPESCRIPT
const { history, total, page, pageSize } = await clipboard.history.getHistory({
  page: 1,
  pageSize: 20
})

history.searchHistory(options)

高级搜索剪贴板历史。

EXAMPLE.TYPESCRIPT
// 关键词搜索
const result = await clipboard.history.searchHistory({
  keyword: 'hello'
})

// 按类型筛选
const textItems = await clipboard.history.searchHistory({
  type: 'text'
})

// 时间范围筛选
const recent = await clipboard.history.searchHistory({
  startTime: Date.now() - 24 * 60 * 60 * 1000  // 最近 24 小时
})

// 组合筛选
const filtered = await clipboard.history.searchHistory({
  type: 'text',
  isFavorite: true,
  sourceApp: 'com.apple.Safari',
  page: 1,
  pageSize: 10
})

搜索参数:

字段类型说明
keywordstring内容关键词
type'text' | 'image' | 'files'类型筛选
startTimenumber开始时间戳
endTimenumber结束时间戳
isFavoriteboolean是否收藏
sourceAppstring来源应用
pagenumber页码
pageSizenumber每页数量
sortOrder'asc' | 'desc'排序方向

history.setFavorite(options)

设置收藏状态。

EXAMPLE.TYPESCRIPT
await clipboard.history.setFavorite({
  id: 123,
  isFavorite: true
})

history.deleteItem(options)

删除历史记录。

EXAMPLE.TYPESCRIPT
await clipboard.history.deleteItem({ id: 123 })

history.clearHistory()

清空所有历史记录。

EXAMPLE.TYPESCRIPT
await clipboard.history.clearHistory()

监听剪贴板变化

history.onDidChange(callback)

监听剪贴板变化事件。

重要:插件必须先调用 box.allowClipboard(types) 声明要监听的类型,否则不会收到任何事件。

EXAMPLE.TYPESCRIPT
import { useBox, ClipboardType } from '@talex-touch/utils/plugin/sdk'

const box = useBox()
const clipboard = useClipboard()

// 1. 先声明要监听的类型
await box.allowClipboard(ClipboardType.TEXT | ClipboardType.IMAGE)

// 2. 注册监听器
const unsubscribe = clipboard.history.onDidChange((item) => {
  console.log('剪贴板变化:', item.type, item.content)
})

// 3. 停止监听
unsubscribe()

ClipboardType 枚举:

值二进制说明
TEXT0b0001文本
IMAGE0b0010图片
FILE0b0100文件

预设组合:

EXAMPLE.TYPESCRIPT
import { ClipboardTypePresets } from '@talex-touch/utils/plugin/sdk'

// 仅文本
await box.allowClipboard(ClipboardTypePresets.TEXT_ONLY)

// 文本和图片
await box.allowClipboard(ClipboardTypePresets.TEXT_AND_IMAGE)

// 所有类型
await box.allowClipboard(ClipboardTypePresets.ALL)

剪贴板项目结构

EXAMPLE.TYPESCRIPT
interface PluginClipboardItem {
  id?: number
  type: 'text' | 'image' | 'files'
  content: string           // 文本内容 / 图片 dataURL / 文件路径 JSON
  thumbnail?: string        // 缩略图 dataURL
  rawContent?: string       // HTML 原始内容
  sourceApp?: string        // 来源应用
  timestamp?: Date          // 时间戳
  isFavorite?: boolean      // 是否收藏
  meta?: Record<string, unknown>  // 元数据(含 tags)
}

自动标签(tags)

当剪贴板内容为文本时,系统会进行轻量规则识别并写入 meta.tags,用于快速分类、提示与匹配。标签仅表示类别,不会包含原始敏感值。

当前标签集合:

  • url:URL/链接
  • api_key:API Key(常见前缀或字段形式)
  • token:Token/Bearer
  • password:密码字段
  • account:账号/用户名字段
  • email:邮箱

使用示例:

EXAMPLE.TYPESCRIPT
const latest = await clipboard.history.getLatest()
const tags = Array.isArray(latest?.meta?.tags) ? latest?.meta?.tags : []

if (tags.includes('api_key') || tags.includes('password')) {
  // 例如:提示用户注意敏感信息
}

建议:对 history.searchHistory() 的结果在插件侧按 meta.tags 做二次筛选,以实现更精准的匹配逻辑。


技术原理

  • 读写、历史记录与自动粘贴均在主进程执行,通过 TuffTransport 返回结果,避免渲染进程焦点导致的剪贴板失败。
  • 历史记录落盘在数据库中,meta/metadata 承载附加信息并用于筛选与匹配。
  • 自动标签基于轻量规则匹配生成,只输出类别,不暴露原始敏感内容。

迁移指南

从 useClipboardHistory 迁移

useClipboardHistory() 已废弃,请迁移到 useClipboard():

EXAMPLE.TYPESCRIPT
// 旧代码
const history = useClipboardHistory()
await history.getLatest()
await history.applyToActiveApp({ text: 'Hello' })

// 新代码
const clipboard = useClipboard()
await clipboard.history.getLatest()
await clipboard.copyAndPaste({ text: 'Hello' })

最佳实践

  1. 使用 copyAndPaste 而非手动操作:copyAndPaste 会自动处理 CoreBox 隐藏、延迟、跨平台粘贴等细节。
  2. 合理设置监听类型:只监听需要的类型,避免不必要的性能开销。
  3. 及时取消监听:在插件卸载或不需要时调用 unsubscribe()。
  4. 处理空值:readImage() 和 history.getLatest() 可能返回 null。