首页
/ OpenTUI 键盘输入处理完全指南:KeyEvent、粘贴事件、文本选择与 Cline CLI 实战解析

OpenTUI 键盘输入处理完全指南:KeyEvent、粘贴事件、文本选择与 Cline CLI 实战解析

2026-09-04 12:22:19作者:羿妍玫Ivan

本篇基于 Cline 仓库内置的 OpenTUI 键盘参考文档(keyboard/REFERENCE.md)展开,系统讲解终端 UI 框架 OpenTUI 中键盘事件的三种接入方式(Core EventEmitter、React/Solid Hook)、KeyEvent 数据结构、按键名与修饰键规则、粘贴事件与文本选择机制,并结合 Cline CLI 的 TUI 源码(如 use-root-keyboard.tsclipboard.ts)说明这些 API 在真实产品中的应用方式。读完后你可以为终端应用设计完整的键盘快捷键体系,并理解 Cline CLI 中 Ctrl+C、双击 Esc 回退、Shift+Tab 切换自动批准等交互的底层实现。

三种键盘接入方式

OpenTUI 对同一套底层键盘事件提供了三个层次的封装,分别适配不同技术栈:

  • Core(命令式)renderer.keyInput EventEmitter,监听 "keypress""paste" 事件,性能与可控性最高,也是构建框架/库时的首选;
  • ReactuseKeyboard() Hook(来自 @opentui/react),在组件内声明式注册按键回调;
  • SoliduseKeyboard() Hook(来自 @opentui/solid),API 与 React 版本一致。

Cline CLI 当前实际使用 Core + React 组合,@opentui/core@opentui/react 均锁定在 0.4.3(见 apps/cli/package.json)。其 TUI 入口通过 createCliRenderer 创建渲染器:

// apps/cli/src/tui/index.tsx
const renderer = await createCliRenderer({
  exitOnCtrlC: false,        // 不交给进程默认行为处理 Ctrl+C,而是走键盘事件流
  autoFocus: false,
  enableMouseMovement: true, // 开启鼠标以支持文本选择
});

这里的 exitOnCtrlC: false 正是后文“常见陷阱”一节提到的关键配置:它让 Ctrl+C 作为一个普通的 KeyEvent 进入 keyInput 事件流,应用代码才能决定是“清空输入框”还是“退出程序”。

KeyEvent 对象:所有按键回调的统一入参

所有键盘处理器都会收到一个 KeyEvent 对象,它是按键判定的唯一依据:

interface KeyEvent {
  name: string          // 按键名:"a"、"escape"、"f1" 等
  sequence: string      // 原始转义序列(escape sequence)
  ctrl: boolean         // 是否按住 Ctrl
  shift: boolean        // 是否按住 Shift
  meta: boolean         // 是否按住 Alt(meta 在多数系统中即 Alt)
  option: boolean       // 是否按住 Option(macOS 特有)
  eventType: "press" | "release" | "repeat"
  repeated: boolean     // 是否为长按产生的重复事件
}

理解这 8 个字段是掌握 OpenTUI 键盘处理的基础:name 给出逻辑按键,修饰键字段给出组合状态,eventTyperepeated 则描述按键生命周期阶段。

基础用法

Core 命令式:直接在渲染器的 keyInput 事件发射器上挂监听器:

import { createCliRenderer, type KeyEvent } from "@opentui/core"

const renderer = await createCliRenderer()

renderer.keyInput.on("keypress", (key: KeyEvent) => {
  if (key.name === "escape") {
    renderer.destroy()
    return
  }

  if (key.ctrl && key.name === "s") {
    saveDocument()
  }
})

React 声明式:在组件内调用 useKeyboard,并通过 useRenderer() 拿到渲染器实例:

import { useKeyboard, useRenderer } from "@opentui/react"

function App() {
  const renderer = useRenderer()
  useKeyboard((key) => {
    if (key.name === "escape") {
      renderer.destroy()
    }
  })

  return <text>Press ESC to exit</text>
}

Solid 声明式:与 React 版本逐行对应,只是从 @opentui/solid 导入:

import { useKeyboard, useRenderer } from "@opentui/solid"

function App() {
  const renderer = useRenderer()
  useKeyboard((key) => {
    if (key.name === "escape") {
      renderer.destroy()
    }
  })

  return <text>Press ESC to exit</text>
}

按键名体系:如何判断用户按了什么

OpenTUI 将物理按键归一化为若干类按键名,判定时按类别对照:

字母键

小写字母:abcz

需要判断大写时,检查修饰键组合:key.shift && key.name === "a" 即表示按下了 Shift+A。也就是说大写字母不产生独立的 name,而是“小写 name + shift 标志”

数字键

0129

功能键

f1f2f3f12

特殊键

按键名 说明
escape Esc 键
enter 回车
return 回车(enter 的别名,两者等价)
tab Tab 键
backspace 退格
delete Delete 键
space 空格

enterreturn 是同一个物理键的两个名字,因此稳健的写法是 key.name === "enter" || key.name === "return"。Cline CLI 的队列提示处理中就是这样同时匹配两个名字的(见 use-root-keyboard.ts 中对 key.name === "enter" || key.name === "return" 的判断)。

方向键

按键名 说明
up 上箭头
down 下箭头
left 左箭头
right 右箭头

导航键

按键名 说明
home Home
end End
pageup Page Up
pagedown Page Down
insert Insert

修饰键与组合键

修饰键不是独立的按键名,而是 KeyEvent 上的布尔标志位。逐个检查即可还原组合:

renderer.keyInput.on("keypress", (key) => {
  if (key.ctrl && key.name === "c") {
    // Ctrl+C
  }

  if (key.shift && key.name === "tab") {
    // Shift+Tab
  }

  if (key.meta && key.name === "s") {
    // Alt+S(meta 在多数系统中等同 Alt)
  }

  if (key.option && key.name === "a") {
    // Option+A(macOS)
  }
})

多修饰键叠加时按位与即可:

// Ctrl+Shift+S
if (key.ctrl && key.shift && key.name === "s") {
  saveAs()
}

// Ctrl+Alt+Delete(注意:可能撞上系统级快捷键!)
if (key.ctrl && key.meta && key.name === "delete") {
  // ...
}

Cline CLI 的 use-root-keyboard.ts 就利用了 shift 标志区分了同键不同修饰的行为:普通 tab 切换“计划/执行”模式(onToggleMode()),而 key.shift && key.name === "tab" 切换自动批准开关(session.toggleAutoApprove())。这正是原文档 Shift+Tab 示例在产品中的落地。

事件类型:press、repeat 与 release

按下的事件(默认)

"keypress" 事件默认覆盖按下行为,eventType === "press" 表示该键首次按下:

renderer.keyInput.on("keypress", (key) => {
  if (key.eventType === "press") {
    // 首次按下
  }
})

重复事件

长按某键时,终端会持续上报 eventType === "repeat" 的事件,同时 repeatedtrue

renderer.keyInput.on("keypress", (key) => {
  if (key.eventType === "repeat" || key.repeated) {
    // 按键正在被长按
  }
})

对“切换选中项”这类不希望长按快速连跳的场景,应过滤掉 repeat 事件。

释放事件(需显式开启)

release 事件是选择性启用的:默认收不到按键释放,必须通过第二个参数 { release: true } 打开:

// React
useKeyboard(
  (key) => {
    if (key.eventType === "release") {
      // 按键抬起
    }
  },
  { release: true }  // 启用 release 事件
)

// Solid
useKeyboard(
  (key) => {
    if (key.eventType === "release") {
      // 按键抬起
    }
  },
  { release: true }
)

实战模式库:菜单、弹窗、Vim 模式与游戏控制

原文档给出了五个经过验证的交互模式,是构建终端应用时可直接套用的参考实现。

导航菜单(vim 式 hjk 快捷键)

function Menu() {
  const [selectedIndex, setSelectedIndex] = useState(0)
  const items = ["Home", "Settings", "Help", "Quit"]

  useKeyboard((key) => {
    switch (key.name) {
      case "up":
      case "k":
        setSelectedIndex(i => Math.max(0, i - 1))
        break
      case "down":
      case "j":
        setSelectedIndex(i => Math.min(items.length - 1, i + 1))
        break
      case "enter":
        handleSelect(items[selectedIndex])
        break
    }
  })

  return (
    <box flexDirection="column">
      {items.map((item, i) => (
        <text
          key={item}
          fg={i === selectedIndex ? "#00FF00" : "#FFFFFF"}
        >
          {i === selectedIndex ? "> " : "  "}{item}
        </text>
      ))}
    </box>
  )
}

该模式的核心技巧:用 Math.max/Math.min 钳制索引边界,并用 fg 属性高亮当前选中项。Cline CLI 的 onboarding 流程 useOnboardingKeyboard 实现了同一套逻辑——up/down 在菜单项间循环移动、return 触发当前项动作,甚至额外支持 ctrl+p/ctrl+n 作为上下方向键的别名(终端 Emacs 习惯)。

模态框 Esc 关闭

function Modal({ onClose, children }) {
  useKeyboard((key) => {
    if (key.name === "escape") {
      onClose()
    }
  })

  return (
    <box border padding={2}>
      {children}
    </box>
  )
}

Vim 风格双模式编辑器

function Editor() {
  const [mode, setMode] = useState<"normal" | "insert">("normal")
  const [content, setContent] = useState("")

  useKeyboard((key) => {
    if (mode === "normal") {
      switch (key.name) {
        case "i":
          setMode("insert")
          break
        case "escape":
          // Already in normal mode
          break
        case "j":
          moveCursorDown()
          break
        case "k":
          moveCursorUp()
          break
      }
    } else if (mode === "insert") {
      if (key.name === "escape") {
        setMode("normal")
      }
      // Input component handles text in insert mode
    }
  })

  return (
    <box flexDirection="column">
      <text>Mode: {mode}</text>
      <textarea
        value={content}
        onChange={setContent}
        focused={mode === "insert"}
      />
    </box>
  )
}

注意最后 <textarea focused={mode === "insert"} />:焦点状态直接由模式驱动,insert 模式下文本输入交给输入组件,normal 模式下 focused 为假、输入组件不再消费字符,全局快捷键因此生效。

游戏控制(基于按键集合)

function Game() {
  const [pressed, setPressed] = useState(new Set<string>())

  useKeyboard(
    (key) => {
      setPressed(keys => {
        const newKeys = new Set(keys)
        if (key.eventType === "release") {
          newKeys.delete(key.name)
        } else {
          newKeys.add(key.name)
        }
        return newKeys
      })
    },
    { release: true }
  )

  // Game logic uses pressed set
  useEffect(() => {
    if (pressed.has("up") || pressed.has("w")) {
      moveUp()
    }
    if (pressed.has("down") || pressed.has("s")) {
      moveDown()
    }
  }, [pressed])

  return <text>WASD or arrows to move</text>
}

这个模式展示了 release: true 的典型用途:维护“当前被按住的所有按键”集合。没有 release 事件,就无法把松开某个键表达出来。

快捷键帮助面板

function ShortcutsHelp() {
  const shortcuts = [
    { keys: "Ctrl+S", action: "Save" },
    { keys: "Ctrl+Q", action: "Quit" },
    { keys: "Ctrl+F", action: "Find" },
    { keys: "Tab", action: "Next field" },
    { keys: "Shift+Tab", action: "Previous field" },
  ]

  return (
    <box border title="Keyboard Shortcuts" padding={1}>
      {shortcuts.map(({ keys, action }) => (
        <box key={keys} flexDirection="row">
          <text width={15} fg="#00FFFF">{keys}</text>
          <text>{action}</text>
        </box>
      ))}
    </box>
  )
}

粘贴事件:拿到的是一串字节,不是文本

粘贴(Paste)事件与按键事件走同一个 keyInput 发射器,但载荷完全不同:Paste 事件交付的是原始字节(raw bytes),不是解码后的文本

PasteEvent 对象结构

import { type PasteEvent } from "@opentui/core"

interface PasteEvent {
  type: "paste"              // 恒为 "paste"
  bytes: Uint8Array          // 原始粘贴字节
  metadata?: PasteMetadata   // 可选元数据
  preventDefault(): void     // 阻止默认粘贴处理
  defaultPrevented: boolean  // preventDefault 是否已被调用
}

interface PasteMetadata {
  mimeType?: string          // MIME 类型(如可用)
  kind?: PasteKind           // 粘贴种类
}

解码粘贴字节

@opentui/core 提供两个工具函数:decodePasteBytes 负责把字节按 UTF-8 解码为字符串;stripAnsiSequences 负责去除剪贴板内容里夹带的 ANSI 转义码(从富文本终端复制内容时常见):

import { decodePasteBytes, stripAnsiSequences } from "@opentui/core"

const text = decodePasteBytes(event.bytes)                              // 解码 UTF-8
const clean = stripAnsiSequences(decodePasteBytes(event.bytes))         // 解码 + 去除 ANSI

Core 监听方式

import { type PasteEvent, decodePasteBytes } from "@opentui/core"

renderer.keyInput.on("paste", (event: PasteEvent) => {
  const text = decodePasteBytes(event.bytes)
  console.log("Pasted:", text)
})

Solid 专属 Hook

Solid 提供了专门的 usePaste Hook:

import { usePaste } from "@opentui/solid"
import { decodePasteBytes } from "@opentui/core"

function App() {
  usePaste((event) => {
    const text = decodePasteBytes(event.bytes)
    console.log("Pasted:", text)
  })

  return <text>Paste something</text>
}

注意usePaste 仅限 Solid。React 没有对应 Hook——React 应用需要通过 Core 事件发射器(renderer.keyInput.on("paste", ...))或输入组件的 onChange 处理粘贴。

文本选择:renderer 统一管理的跨组件 Selection

文本选择由渲染器集中管理,而不是各组件各自为政:renderer 内部持有单个 Selection 对象,遍历可渲染树找到所有参与选择的子节点,并在用户完成选择(鼠标抬起)时发出 "selection" 事件。Selection 对象会自动聚合所有选中 renderable 的文本。

让 renderable 参与选择

renderable 必须设置 selectable: true 才能参与选择。文本类 renderable(TextRenderableTextareaRenderableASCIIFontRenderableTextTableRenderable)支持该属性:

// React / Solid
<text selectable>This text can be selected</text>

// Core
const text = new TextRenderable(renderer, {
  id: "label",
  content: "This text can be selected",
  selectable: true,
})

Cline CLI 在创建渲染器时开启了 enableMouseMovement: true(见 index.tsx),正是为了让这类鼠标驱动的选择交互可用。

选择后复制(Core)

监听 renderer 的 "selection" 事件。Selection 对象的 getSelectedText() 返回按阅读顺序聚合了所有选中 renderable 的文本:

import type { Selection } from "@opentui/core"

renderer.on("selection", (selection: Selection) => {
  const text = selection.getSelectedText()
  if (text) {
    renderer.copyToClipboardOSC52(text)
  }
})

重要:务必调用事件回调里 Selection 对象的 selection.getSelectedText(),而不是 renderer.root.getSelectedText()。单个 renderable 只会返回自己内部的选中文本,只有 Selection 对象才做跨树聚合。

选择后复制(Solid)

import { useSelectionHandler } from "@opentui/solid"

function App() {
  useSelectionHandler((selection) => {
    const text = selection.getSelectedText()
    if (text) {
      renderer.copyToClipboardOSC52(text)
    }
  })

  return <text selectable>Select this text</text>
}

注意useSelectionHandler 仅限 Solid。React 应用应使用 Core 的 renderer.on("selection", ...) 事件。

Selection 对象 API

事件回调传入的 Selection 对象暴露:

selection.getSelectedText()       // 聚合所有选中 renderable 的文本
selection.bounds                  // { startX, startY, endX, endY } 包围矩形
selection.selectedRenderables     // 当前持有选中态的 Renderable[]
selection.isActive                // 选择是否仍活跃

单个 renderable 也各自暴露:

renderable.hasSelection()         // 该 renderable 是否存在选中文本?
renderable.getSelectedText()      // 仅该 renderable 内的选中文本

选择遍历机制

用户拖拽选择时,renderer 的处理流程是:

  1. 识别选择容器(起点与终点的公共祖先);
  2. 遍历选择边界内所有 selectable 后代;
  3. 对每个后代调用 onSelectionChanged(selection),计算局部选中区域;
  4. 将拥有活跃选择的 renderable 记录进 selection.selectedRenderables

由此可以推断一个实用结论:选择天然跨组件——从两个相邻的 <text selectable> 上拖过,两个元素的文本都会被选中,且 selection.getSelectedText() 会用换行符把多段文本连接起来。

Cline CLI 的 root.tsx 中就把 copyToClipboardOSC52 包进了统一工具函数,让选择复制、按钮复制等多个调用点复用同一条“OSC 52 优先、系统剪贴板兜底”的链路。

剪贴板 API(OSC 52)与 Cline 的兜底策略

终端程序无法直接访问系统剪贴板,OpenTUI 的解法是 OSC 52 转义序列:它由终端解释并写回用户本地机器的剪贴板,因此在 SSH 远程会话中依然有效,且被大多数现代终端模拟器支持。

// 复制文本到剪贴板
const success = renderer.copyToClipboardOSC52("text to copy")

// 先探测终端是否支持 OSC 52
if (renderer.isOsc52Supported()) {
  renderer.copyToClipboardOSC52("Hello!")
}

// 清空剪贴板
renderer.clearClipboardOSC52()

// 指定目标剪贴板(X11 场景)
import { ClipboardTarget } from "@opentui/core"
renderer.copyToClipboardOSC52("text", ClipboardTarget.Primary)    // X11 主选择区
renderer.copyToClipboardOSC52("text", ClipboardTarget.Clipboard)   // 系统剪贴板(默认)

Cline CLI 在此之上叠加了一层工程化兜底,值得单独拆解(apps/cli/src/tui/utils/clipboard.ts):

  • SSH 会话下默认跳过本地命令兜底:OSC 52 写的是用户本地终端的剪贴板;而直接 spawn pbcopy/xclip 会写远程主机的剪贴板。源码通过检测 SSH_CONNECTION/SSH_CLIENT/SSH_TTY 环境变量判断远程会话,默认直接放弃兜底(返回 false),只有显式设置 CLINE_CLIPBOARD_FALLBACK_REMOTE=1 才写远端剪贴板;
  • 按平台选择命令链:macOS 用 pbcopy(并强制 LANG=en_US.UTF-8 环境避免编码问题);Windows 用 PowerShell 的 Set-Clipboardpowershell.exepwsh.exe 各试一次);WSL 走 PowerShell 以写入 Windows 剪贴板;普通 Linux 依次尝试 wl-copy(Wayland)与 xclip -selection clipboard(X11);
  • 超时与可观测性:每条命令带 1500ms 超时(CLIPBOARD_COMMAND_TIMEOUT_MS),设 CLINE_DEBUG_CLIPBOARD=1 可打印跳过或失败原因,默认关闭以保持 TUI 画布干净。

也就是说,OSC 52 是“跨 SSH 的正确通道”,本地命令是“终端不支持 OSC 52 时的备用通道”,两者按平台与连接方式组合成一条可靠的复制链路。

焦点与输入组件的冲突规避

输入组件(<input><textarea><select>)在获得焦点时会消费键盘事件,但全局 useKeyboard 依然会收到事件

<input focused />  // 接收键盘输入

// 全局 useKeyboard still fires, but input consumes characters

推荐的做法是记录输入焦点状态,在焦点位于输入组件时主动让路:

function App() {
  const renderer = useRenderer()
  const [inputFocused, setInputFocused] = useState(false)

  useKeyboard((key) => {
    if (inputFocused) return  // 让输入组件处理

    // Global shortcuts
    if (key.name === "escape") {
      renderer.destroy()
    }
  })

  return (
    <input
      focused={inputFocused}
      onFocus={() => setInputFocused(true)}
      onBlur={() => setInputFocused(false)}
    />
  )
}

Cline CLI 的主界面没有用 onFocus 回调,而是采用了“按键特征预判”策略:use-root-keyboard.ts 先计算当前按键是否属于“可能正在编辑 textarea”的按键(单字符、backspacedeletespace 且无 Ctrl/Meta),若是则先从 textarea 同步最新输入文本,再统一判断快捷键;而像 Tab、Esc、Ctrl+ 组合这类结构性按键则直接走全局逻辑。两套思路殊途同归,核心都是让快捷键判断与文本输入解耦

从源码看 Cline CLI 的完整快捷键体系

Cline CLI 的根级键盘 Hook useRootKeyboard 是原文档各概念的一次集中实战,值得逐条对照:

快捷键 行为 源码位置
Ctrl+C 输入框有内容时清空输入;无内容时退出 use-root-keyboard.ts
Ctrl+D 空闲且无输入文本时退出 use-root-keyboard.ts
Esc 分级响应:取消编辑 → 中止运行 → 取消队列选择 → 300ms 内连按两次触发检查点回退 use-root-keyboard.ts
Tab / Shift+Tab 切换模式 / 切换自动批准 use-root-keyboard.ts
Ctrl+L 运行中清屏;空闲时清空会话 use-root-keyboard.ts
Ctrl+S 运行中携带输入时以 steer 方式提交 use-root-keyboard.ts
Ctrl+P 空闲时打开命令面板 use-root-keyboard.ts
上/下箭头 队列提示选择、输入历史导航(preventDefault 阻止 textarea 默认滚动) use-root-keyboard.ts

几个细节印证了原文档的原则:

  1. 双击 Esc 判定用一个 useRef 记录上次 Esc 时间戳,间隔小于 300ms 视为双击并触发 onRestoreCheckpoint()(见 第 243-251 行)——这正是“利用 press 事件做时间序列判定”的范例;
  2. key.preventDefault() 在需要阻断默认行为的分支上被显式调用(如选中队列提示后的上/下/Tab/Enter),说明 KeyEvent 同样具备事件拦截能力;
  3. 多状态机路由:onboarding 阶段的 useOnboardingKeyboard 把 Esc 定义为“逐级返回”(oauth_pending → 中止 OAuth 回菜单、device_code → 中止设备码流程、byo_apikey → 回 provider 选择……),体现了原文档“Multiple Handlers / 协调处理器防止冲突”的建议——不同视图各自持有键盘逻辑,由 appViewisDialogOpen 等状态门控决定哪个处理器生效。

另外,commands/auth.ts 中非 TUI 的交互命令同样使用 exitOnCtrlC: false,说明该配置是 Cline 全产品线的统一约定:进程永远不依赖 shell 默认的 SIGINT 行为,退出意图一律由键盘事件流显式表达

常见陷阱(Gotchas)

原文档明确列出三类终端环境限制,开发时须提前规避:

终端/操作系统的按键抢占

部分组合键会被终端或 OS 先行截获:

  • Ctrl+C 通常发送 SIGINT——必须设置 exitOnCtrlC: false 才能在自己的事件流里处理(Cline 即如此);
  • Ctrl+Z 会挂起进程(SIGTSTP);
  • 某些功能键可能同样被终端拦截。

SSH 与远程会话

按键的转义序列经过 SSH 隧道转发后可能发生变化,键位识别在 SSH 下的表现可能不一致,必须在目标环境实测。剪贴板同理:本地命令兜底会写错机器,需按 clipboard.ts 的思路处理。

多个键盘处理器并存

多个 useKeyboard 调用都会收到每个事件。这是框架刻意保留的广播语义(便于不同组件各自响应),但也要求开发者自行协调处理顺序与 preventDefault,防止两个处理器对同一按键做出冲突动作。Cline CLI 通过“视图状态 + 对话框状态 + 编辑特征预判”三层门控来协调,可参考其组织方式。

相关文档

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
903
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341