关于划词工具的 「AI 提问」功能易用性引发的探索思路

一篇散乱的思路整理

最近浏览器上用 kimi 拓展各种划词提问辅助阅读体验可以说非常棒。

特别是上下文识别能力、划词打开提问窗口后可以继续提问对话这两点。

但是脱离浏览器后,在编辑场景下就很难获得这种快速易用的辅助搜索体验了。在主流编辑器倒是还是通过 copilot 的 ctrl + i 快捷键来快速提问。

但是像写个文档辅助查资料(思源里可以通过直接开伺服在浏览器里临时解决)、一些复杂任务的临时查询还是需要回归到传统的切换窗口然后搜索(包括多屏幕下的切屏),于是最近就想着看一下有哪些能够实现全局划线提问能力的 AI 方案。

在简单搜索后,找到了 讯飞星火豆包 这样标榜包括全局任意划词提问以及全功能 AI 能力并附赠 chromium 内核的方案。实际体验后,都存在一次划线解释提问后继续对话就必须打开它的软件本体切换窗口来继续对话的以及功能太繁杂而没有足够的配置项隐藏它们的问题。

于是就开始想:

“像 pot 这类划词翻译工具是否能拓展一个 AI 搜索功能呢?”

于是顺着在 Github 上搜索划词关键词找到了以下两个具有参考意义的应用

openai-translator: 基于 ChatGPT API 的划词翻译浏览器插件和跨平台桌面端应用

Borber/Tran at v0.2.17

一个是 ai 翻译, 一个是功能足够轻量。
第一个虽然已经引入 ai 润色了,但却没有提问功能(
第二个是功能做到了相对的极简,作为一个临时翻译方案也不错就保留了。

然后思路变成

“如果自己写一个类似的工具需要考虑那些方面呢”

于是问了问 GPT + 之前用 Quicker 写一些获取划选内容的脚本时快捷键设置为 ctrl+c 发生的循环问题。可以确认大概是通过检测

  • 鼠标按下
  • 检测鼠标移动范围
  • 鼠标释放
  • 发送复制事件(模拟 ctrl+c)复制内容,然后通过剪切板获取划取内容
  • 最后通过向剪切板写入空白信息覆盖刚才复制的内容避免污染剪切板

就实现了最关键的全局获取划线内容这一项功能(进一步的监听键盘 shift+方向键ctrl+a 都差不多)

接着让 GPT 补全处理多屏幕问题,就简单实现了一个全局划线后显示一个 web 窗口查询内容的功能。似乎进一步优化性能和逻辑就能够实现一个符合自己需求的划词 AI 提问工具了。

但当前 GPT 给出的都是 python QT 代码,没什么改的欲望,就先贴一份在这吧

1686924591925.jpeg

import sys import threading import time from pynput import mouse, keyboard import pyperclip import pyautogui from PyQt5 import QtWidgets, QtCore, QtGui, QtWebEngineWidgets class SelectionListener(QtCore.QObject): # 定义信号,传递选中文本和坐标 selection_made = QtCore.pyqtSignal(str, int, int) selection_cleared = QtCore.pyqtSignal() def __init__(self): super().__init__() self.current_selection = "" self.run_service = True # 鼠标状态变量 self.mouse_listener = mouse.Listener(on_click=self.on_click, on_move=self.on_move) self.is_dragging = False self.mouse_pressed = False self.last_mouse_time = 0 self.click_count = 0 # 键盘状态变量 self.keyboard_listener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release) self.shift_pressed = False self.arrow_key_pressed = False # 鼠标事件处理 def on_click(self, x, y, button, pressed): if pressed: self.mouse_pressed = True current_time = time.time() if current_time - self.last_mouse_time < 0.4: self.click_count += 1 else: self.click_count = 1 self.last_mouse_time = current_time if self.click_count == 2: # 检测到双击 time.sleep(0.1) self.simulate_copy_and_get_selection() self.click_count = 0 else: if self.mouse_pressed: if self.is_dragging: # 拖拽选择完成 time.sleep(0.1) self.simulate_copy_and_get_selection() else: # 单击未拖拽,可能清除了选择 self.selection_cleared.emit() self.is_dragging = False self.mouse_pressed = False def on_move(self, x, y): if self.mouse_pressed: if not self.is_dragging: pass # 开始拖拽 self.is_dragging = True # 键盘事件处理 def on_press(self, key): try: if key == keyboard.Key.shift: self.shift_pressed = True elif self.shift_pressed and key in [keyboard.Key.left, keyboard.Key.right, keyboard.Key.up, keyboard.Key.down]: self.arrow_key_pressed = True except AttributeError: pass def on_release(self, key): try: if key == keyboard.Key.shift: if self.arrow_key_pressed: # Shift选择完成 time.sleep(0.1) self.simulate_copy_and_get_selection() else: # Shift释放,无箭头键,可能清除了选择 self.selection_cleared.emit() self.shift_pressed = False self.arrow_key_pressed = False except AttributeError: pass def simulate_copy_and_get_selection(self): try: # 保存当前剪贴板内容 previous_clipboard_content = pyperclip.paste() # 模拟复制 pyautogui.hotkey("ctrl", "c") time.sleep(0.05) # 等待剪贴板更新 # 获取选中文本 new_clipboard_content = pyperclip.paste() # 恢复之前的剪贴板内容 pyperclip.copy(previous_clipboard_content) # 获取鼠标位置 cursor_pos = QtGui.QCursor.pos() x = cursor_pos.x() y = cursor_pos.y() # 如果有新文本被选中,发射信号 if new_clipboard_content != previous_clipboard_content and new_clipboard_content.strip(): self.current_selection = new_clipboard_content self.selection_made.emit(self.current_selection, x, y) else: # 没有选中文本,可能清除了选择 self.selection_cleared.emit() except Exception as e: print(f"模拟复制时出错: {e}") def start(self): self.mouse_listener.start() self.keyboard_listener.start() try: while self.run_service: time.sleep(1) # 保持线程存活 except KeyboardInterrupt: self.stop() def stop(self): self.mouse_listener.stop() self.keyboard_listener.stop() self.run_service = False class FloatingWindow(QtWidgets.QWidget): def __init__(self, text, x, y, parent=None): super().__init__(parent) self.text = text self.initUI(x, y) def initUI(self, x, y): self.setWindowFlags( QtCore.Qt.Popup | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint ) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.setAttribute(QtCore.Qt.WA_ShowWithoutActivating) # 调整位置,适应多显示器环境 cursor_pos = QtCore.QPoint(x, y) screen = QtGui.QGuiApplication.screenAt(cursor_pos) if screen is None: screen = QtWidgets.QApplication.primaryScreen() # 创建按钮 self.button = QtWidgets.QPushButton("🔍", self) self.button.setStyleSheet(""" QPushButton { font-size: 18px; background-color: rgba(255, 255, 255, 200); border: none; border-radius: 15px; } QPushButton::hover { background-color: rgba(200, 200, 200, 200); } """) self.button.setFixedSize(30, 30) self.button.clicked.connect(self.show_webview) # 调整大小 self.resize(30, 30) # 移动窗口到鼠标位置 self.move(x - self.width() // 2, y - self.height() // 2) # 设置焦点策略 self.setFocusPolicy(QtCore.Qt.StrongFocus) self.button.setFocusPolicy(QtCore.Qt.NoFocus) # 显示窗口 self.show() def show_webview(self): self.webview = WebViewWindow(self.text) self.webview.show() # 关闭浮动窗口 self.close() def focusOutEvent(self, event): self.close() class WebViewWindow(QtWidgets.QWidget): def __init__(self, text): super().__init__() self.text = text self.initUI() def initUI(self): # 设置无边框窗口 self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.resize(800, 600) layout = QtWidgets.QVBoxLayout(self) # 创建 QWebEngineView self.browser = QtWebEngineWidgets.QWebEngineView(self) # 执行搜索 query = QtCore.QUrl.fromUserInput(f"https://www.google.com/search?q={self.text}") self.browser.load(query) layout.addWidget(self.browser) # 显示窗口 self.show() def start_selection_listener(listener): listener.start() def main(): app = QtWidgets.QApplication(sys.argv) # 创建选择监听器 listener = SelectionListener() # 在单独的线程中启动监听器 listener_thread = threading.Thread(target=start_selection_listener, args=(listener,)) listener_thread.daemon = True listener_thread.start() # 保持浮动窗口的引用 floating_window = None # 处理监听器发出的信号 def on_selection_made(text, x, y): nonlocal floating_window # 关闭任何已存在的浮动窗口 if floating_window is not None: floating_window.close() floating_window = FloatingWindow(text, x, y) def on_selection_cleared(): nonlocal floating_window if floating_window is not None: floating_window.close() floating_window = None listener.selection_made.connect(on_selection_made) # listener.selection_cleared.connect(on_selection_cleared) sys.exit(app.exec_()) if __name__ == "__main__": main()
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    163 引用 • 309 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖 • 2 关注
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    54 引用 • 37 回帖 • 1 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖 • 3 关注
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 110 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    143 引用 • 442 回帖 • 1 关注
  • 创业

    你比 99% 的人都优秀么?

    82 引用 • 1395 回帖 • 1 关注
  • OneNote
    1 引用 • 3 回帖
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    133 引用 • 796 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3198 引用 • 8215 回帖 • 1 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 233 回帖 • 1 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    180 引用 • 408 回帖 • 489 关注
  • CloudFoundry

    Cloud Foundry 是 VMware 推出的业界第一个开源 PaaS 云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题。

    5 引用 • 18 回帖 • 177 关注
  • OneDrive
    2 引用 • 4 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    63 引用 • 289 回帖
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    173 引用 • 414 回帖 • 368 关注
  • 职场

    找到自己的位置,萌新烦恼少。

    127 引用 • 1708 回帖 • 1 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 437 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 6 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 9 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    227 引用 • 476 回帖
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    325 引用 • 1395 回帖
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    116 引用 • 54 回帖 • 2 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    28 引用 • 197 回帖 • 33 关注