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

本贴最后更新于 193 天前,其中的信息可能已经事过境迁

一篇散乱的思路整理

最近浏览器上用 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)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    115 引用 • 318 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    954 引用 • 944 回帖 • 1 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 663 关注
  • Visio
    1 引用 • 2 回帖 • 1 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 75 关注
  • IDEA

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

    181 引用 • 400 回帖 • 1 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    59 引用 • 25 回帖
  • jsDelivr

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

    5 引用 • 31 回帖 • 105 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1443 引用 • 10082 回帖 • 496 关注
  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖 • 1 关注
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 148 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖 • 1 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 1 关注
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖 • 1 关注
  • 安全

    安全永远都不是一个小问题。

    199 引用 • 818 回帖
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖 • 1 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 606 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 85 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 248 回帖
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 395 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 567 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 675 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 671 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1432 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    98 引用 • 903 回帖
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    7 引用 • 28 回帖 • 2 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 705 关注