AI书签管理工具开发全记录(十三):TUI基本框架搭建

news2025/6/7 22:13:59

文章目录

  • AI书签管理工具开发全记录(十三):TUI基本框架搭建
    • 前言 📝
    • 1.TUI介绍 🔍
    • 2. 框架选择 ⚙️
    • 3. 功能梳理 🎯
    • 4. 基础框架搭建⚙️
      • 4.1 安装
      • 4.2 参数设计
      • 4.3 绘制ui
        • 4.3.1 设计结构体
        • 4.3.2 创建头部
        • 4.3.3 创建主体
        • 4.3.4 创建日志栏
        • 4.3.5 创建整体布局
        • 4.3.6 创建启动函数
        • 4.3.6 对接cobra命令行
    • 5.效果测试 ✅

AI书签管理工具开发全记录(十三):TUI基本框架搭建

前言 📝

在上一篇文章中,我们实现了MCP查询集成,集成了常用的查询场景,在支持MCP的客户端如cherry studio中可以查询书签数据了。但有时候,我们需要快速的浏览书签书签,没有比命令行的方式更快速的了,但是命令行操作太繁琐,所以我们本篇文章采用更进一步的TUI。

1.TUI介绍 🔍

TUI(Terminal User Interface) 是命令行工具与图形界面的完美融合体。不同于传统CLI需要记忆复杂指令的操作方式,TUI通过直观的文本界面组件(如菜单、列表、状态栏)实现可视化交互;相较于GUI应用,它又保留了终端操作的高效性与轻量级优势。经典的Linux工具如htopncduvim都证明了TUI在提升生产力方面的价值——无需离开终端窗口,即可实现键盘驱动的快速导航与操作。

在书签管理场景中,TUI能让我们通过方向键即时浏览收藏链接,用快捷键执行搜索/跳转/删除等操作,避免图形界面加载的延迟和命令行输入的低效。

2. 框架选择 ⚙️

在Go语言的TUI框架生态中,rivo/tview 以其组件化的设计强大的布局能力卓越的性能脱颖而出,成为我们构建书签管理TUI的首选。它提供了一套丰富且高度可定制的基础组件(如列表 List、表格 Table、文本框 TextView、表单 Form、模态框 Modal 等)和灵活的布局管理器(如 Flex, Grid, Pages),极大地简化了复杂终端界面的构建过程。其核心优势在于:

  1. 声明式布局:通过组合嵌套布局和组件,可以直观地构建出结构清晰、响应式的界面,适应不同终端尺寸。
  2. 高效渲染与键盘导航tview 采用智能渲染机制,确保界面更新流畅。它内置了强大且可自定义的键盘事件处理系统,让我们能够轻松为书签的浏览、选择、搜索、操作(打开、删除、编辑)定义直观的快捷键,实现真正的“键盘驱动”体验。
  3. 丰富的样式与主题:支持设置文本颜色、背景色、边框样式、标题等,允许我们为书签的不同状态(如已读/未读、分类)设计清晰的视觉区分。
  4. 活跃的社区与成熟度:作为Go领域最受欢迎的TUI库之一,tview 拥有良好的文档、活跃的社区支持和经过大量项目验证的稳定性。

选择 tview,意味着我们能高效地实现一个性能出色交互流畅视觉清晰的书签管理终端应用,完美契合我们追求快速浏览与操作的核心目标。接下来,我们将开始搭建基于 tview 的应用基础框架。

3. 功能梳理 🎯

在开始搭建基础框架前,我们先规划一下tui应用需求和布局

功能列表:

  • 需要搜索功能,可以快速搜索书签
  • 可以快速浏览器分类、书签

ui设计:

  • 最顶层是搜索框,可以输入关键词进行搜索
  • 中间是三栏数据,第一栏分类、第二栏书签、第三栏书签描述
  • 最下层是日志框,可以隐藏

4. 基础框架搭建⚙️

4.1 安装

go get -u github.com/rivo/tview

4.2 参数设计

需求:

  1. 启动时可以输入关键词,查询书签
  2. 可以关闭日志栏
// cmd/root.go:Execute
rootCmd.Flags().StringP("search", "s", "", "Search for a command")
rootCmd.Flags().BoolP("showlog", "l", false, "Show log panel")

4.3 绘制ui

4.3.1 设计结构体
// internal/tui/app.go

type TuiView struct {
	app             *tview.Application
	searchBox       *tview.InputField
	categoryList    *tview.List
	bookmarkList    *tview.List
	descriptionView *tview.TextView
	logView         *tview.TextView
	main            *tview.Flex
	modal           *tview.Flex
	pages           *tview.Pages
	focusable       []tview.Primitive
	showLog         bool
	focusIndex      int
}
4.3.2 创建头部
// internal/tui/ui.go

func (t *TuiView) CreateHeader() *tview.TextView {
	return tview.NewTextView().
		SetDynamicColors(true).
		SetTextAlign(tview.AlignCenter).
		SetText("[::b]AiBookMark[::-] [darkcyan] ← →:切换 ↑↓:导航 CTRL+F:搜索 /CTRL+R:重置 Q:退出")
}
4.3.3 创建主体
// internal/tui/ui.go

func (t *TuiView) CreateCategoryList() *tview.List {
	t.categoryList = tview.NewList().ShowSecondaryText(true)
	t.categoryList.SetBorder(true).SetTitle(" 分类 ")
	t.categoryList.SetBorderColor(tcell.ColorWhite) // 添加默认边框颜色
	return t.categoryList
}

func (t *TuiView) CreateBookmarkList() *tview.List {
	t.bookmarkList = tview.NewList().ShowSecondaryText(true)
	t.bookmarkList.SetBorder(true).SetTitle(" 书签 ")
	t.bookmarkList.SetBorderColor(tcell.ColorWhite) // 添加默认边框颜色
	return t.bookmarkList
}

func (t *TuiView) CreateDescriptionView() *tview.TextView {
	t.descriptionView = tview.NewTextView().
		SetDynamicColors(true).
		SetScrollable(true).
		SetWrap(true)
	t.descriptionView.SetBorder(true).SetTitle(" 描述 ")
	t.descriptionView.SetBorderColor(tcell.ColorWhite) // 添加默认边框颜色
	return t.descriptionView
}


func (t *TuiView) CreateMain() *tview.Flex {
	// 菜单列表
	t.CreateCategoryList()
	// 书签列表
	t.CreateBookmarkList()
	// 书签描述
	t.CreateDescriptionView()

	// 创建布局
	mainFlex := tview.NewFlex()
	mainFlex.AddItem(t.categoryList, 35, 0, true)
	mainFlex.AddItem(tview.NewFlex().
		AddItem(t.bookmarkList, 0, 1, true).
		AddItem(t.descriptionView, 0, 2, false), 0, 1, false)
	return mainFlex
}
4.3.4 创建日志栏
// internal/tui/ui.go

func (t *TuiView) CreateLogs() *tview.TextView {
	t.logView = tview.NewTextView().
		SetDynamicColors(true).
		SetScrollable(true).
		SetWrap(false)
	t.logView.SetBorder(true).SetTitle(" 日志 ")
	return t.logView
}
4.3.5 创建整体布局
// internal/tui/ui.go

func (t *TuiView) CreateMainFlex() *tview.Flex {
	mainFlex := tview.NewFlex()
	mainFlex.SetDirection(tview.FlexRow)
	// Header
	mainFlex.AddItem(t.CreateHeader(), 3, 0, false)
	// 搜索框
	mainFlex.AddItem(t.CreateSearchBox(), 3, 0, false)
	// CategoryList
	mainFlex.AddItem(t.CreateMain(), 0, 1, false)
	if t.showLog {
		mainFlex.AddItem(t.CreateLogs(), 8, 10, false)
	}
	return mainFlex
}
4.3.6 创建启动函数
// internal/tui/app.go
func (t *TuiView) Run(searchKeyWord string, showLog bool) {
	t.showLog = showLog
	// 初始化应用程序
	t.app = tview.NewApplication()
	// enable鼠标
	t.app.EnableMouse(false)
	// 创建mainFlex
	t.main = t.CreateMainFlex()
	// 初始化可聚焦组件列表
	t.focusable = []tview.Primitive{t.categoryList, t.bookmarkList, t.descriptionView}
	t.focusIndex = 0
	// 设置根节点
	t.app.SetRoot(t.main, true)
	// 设置焦点
	t.Focus(t.focusable[t.focusIndex])
	// 设置初始焦点组件的边框颜色
	if focusable, ok := t.focusable[t.focusIndex].(interface{ SetBorderColor(tcell.Color) }); ok {
		focusable.SetBorderColor(tcell.ColorSkyblue)
	}
	if err := t.app.Run(); err != nil {
		fmt.Println("TUI启动失败: " + err.Error())
	}
}
4.3.6 对接cobra命令行
// internal/tui/app.go

// 获取搜索关键字
searchKeyword, _ := cmd.Flags().GetString("search")
// 是否显示日志
show, _ := cmd.Flags().GetBool("showlog")
tui := tui.TuiView{}
tui.Run(searchKeyword, show)

5.效果测试 ✅

运行

go run main.go

image.png

带日志启动
image.png

基本框架搭建完毕,后续就是从数据库中获取数据,渲染到终端上。


往期系列

  • Ai书签管理工具开发全记录(一):项目总览与技术蓝图
  • Ai书签管理工具开发全记录(二):项目基础框架搭建
  • AI书签管理工具开发全记录(三):配置及数据系统设计
  • AI书签管理工具开发全记录(四):日志系统设计与实现
  • AI书签管理工具开发全记录(五):后端服务搭建与API实现
  • AI书签管理工具开发全记录(六):前端管理基础框框搭建 Vue3+Element Plus
  • AI书签管理工具开发全记录(七):页面编写与接口对接
  • AI书签管理工具开发全记录(八):Ai创建书签功能实现
  • AI书签管理工具开发全记录(九):用户端页面集成与展示
  • AI书签管理工具开发全记录(十):命令行中结合ai高效添加书签
  • AI书签管理工具开发全记录(十一):MCP集成
  • # AI书签管理工具开发全记录(十二):MCP集成查询

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2403398.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初识结构体,整型提升及操作符的属性

目录 一、结构体成员访问操作符1.1 结构体二、操作符的属性:优先级、结合性2.1 优先级2.2 结合性C 运算符优先级 三、表达式求值3.1 整型提升3.2 算数转化 总结 一、结构体成员访问操作符 1.1 结构体 C语言已经提供了内置类型,如:char,shor…

检测到 #include 错误。请更新 includePath。已为此翻译单元(D:\软件\vscode\test.c)禁用波形曲线

原文链接:【VScodeMinGw】安装配置教程 下载mingw64 打开可以看到bin文件夹下是多个.exe文件,gcc.exe地址在环境配置中要用到 原文链接:VSCode中出现“#include错误,请更新includePath“问题,解决方法 重新VScode后…

2025年,百度智能云打响AI落地升维战

如果说从AI到Agent是对于产品落地形态的共识,那么如今百度智能云打响的恰是一个基于Agent进行TO B行业表达的AI生产力升维战。 在这个新的工程体系能力里,除了之前百度Create大会上提出的面向Agent的RAG能力等通用能力模块,对更为专业、个性…

Seed1.5-VL登顶,国产闭源模型弯道超车丨多模态模型5月最新榜单揭晓

随着图像、文本、语音、视频等多模态信息融合能力的持续增强,多模态大模型在感知理解、逻辑推理和内容生成等任务中的综合表现不断提升,正在展现出愈发接近人类的智能水平。多模态能力也正在从底层的感知理解,迈向具备认知、推理、决策能力的…

第3章——SSM整合

一、整合持久层框架MyBatis 1.准备数据库表及数据 创建数据库:springboot 使用IDEA工具自带的mysql插件来完成表的创建和数据的准备: 创建表 表创建成功后,为表准备数据,如下: 2.创建SpringBoot项目 使用脚手架创建…

VTK 显示文字、图片及2D/3D图

1. 基本环境设置 首先确保你已经安装了VTK库&#xff0c;并配置好了C开发环境。 #include <vtkSmartPointer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> 2. 显示文字 2D文字 #include &l…

小白如何在cursor中使用mcp服务——以使用notion的api为例

1. 首先安装node.js,在这一步的时候不要勾选不要勾选 2. 安装完之后,前往notion页面 我的创作者个人资料 | Notion 前往集成页面&#xff0c;添加新集成&#xff0c;自己输入名字&#xff0c;选择内部 新建完之后&#xff0c;进入选择只读 复制密匙 然后前往cursor页面 新建…

引领AI安全新时代 Accelerate 2025北亚巡展·北京站成功举办

6月5日&#xff0c;网络安全行业年度盛会——"Accelerate 2025北亚巡展北京站"圆满落幕&#xff01;来自智库、产业界、Fortinet管理层及技术团队的权威专家&#xff0c;与来自各行业的企业客户代表齐聚一堂&#xff0c;围绕"AI智御全球引领安全新时代"主题…

为什么说数列是特殊的函数

文章目录 前情概要函数特性特殊之处典例剖析前情概要 高三的学生几乎都听老师说过,数列是特殊的函数,那么如何理解这句话呢,无外乎需要关注两点:①函数性,②特殊性,以下举例说明,帮助各位学子理解。 函数特性 既然是按照一定的次序排列而成的一列数字,那么这些数字(…

解决uniapp开发app map组件最高层级 遮挡自定义解决底部tabbar方法

subNvue&#xff0c;是 vue 页面的原生子窗体&#xff0c;把weex渲染的原生界面当做 vue 页面的子窗体覆盖在页面上。它不是全屏页面&#xff0c;它给App平台vue页面中的层级覆盖和原生界面自定义提供了更强大和灵活的解决方案。它也不是组件&#xff0c;就是一个原生子窗体。 …

96. 2017年蓝桥杯省赛 - Excel地址(困难)- 进制转换

96. Excel地址&#xff08;进制转换&#xff09; 1. 2017年蓝桥杯省赛 - Excel地址&#xff08;困难&#xff09; 标签&#xff1a;2017 省赛 1.1 题目描述 Excel 单元格的地址表示很有趣&#xff0c;它使用字母来表示列号。 比如&#xff0c; A 表示第 1 列&#xff0c;…

PPT转图片拼贴工具 v1.0

软件介绍 这个软件的作用就是将单个PPT的每一页转换为单独的图片&#xff0c;然后将图片进行拼接起来。 但是我没有还没有解决一次性处理多个文件。 效果展示如下&#xff1a; 软件安装 软件源码 import os import re import win32com.client from PIL import Imagedef con…

【行驶证识别成表格】批量OCR行驶证识别与Excel自动化处理系统,行驶证扫描件和照片图片识别后保存为Excel表格,基于QT和华为ocr识别的实现教程

在车辆管理、物流运输、保险理赔等领域&#xff0c;经常需要处理大量的行驶证信息。传统的人工录入方式效率低、易出错&#xff0c;而使用 OCR 技术可以自动识别行驶证图片中的文字信息&#xff0c;极大提高数据处理效率。该系统可以应用于以下场景&#xff1a; 保险公司快速…

Linux--进程的状态

1.进程状态在所有系统中宏观的大致模型 1.1、进程状态与变迁 基础状态&#xff1a;涵盖创建、就绪、运行、阻塞、结束等核心状态&#xff0c;描述进程从诞生到消亡的生命周期流转&#xff0c;如创建后进入就绪&#xff0c;争抢 CPU 进入运行&#xff0c;遇 I/O 或资源等待则转…

(nice!!!)(LeetCode每日一题)2434. 使用机器人打印字典序最小的字符串(贪心+栈)

题目&#xff1a;2434. 使用机器人打印字典序最小的字符串 思路&#xff1a;贪心栈&#xff0c;时间复杂度0(n)。 字符串t其实就是栈&#xff0c;后进先出。要让p的字典序最小&#xff0c;那当然是t每次弹出的字符&#xff0c;都小于或等于“剩下未入t里的字符串的字符”&#…

008-libb64 你有多理解base64?-C++开源库108杰

正确认识二进制数据和文本数据的关系;深刻理解 base64 编码核心等式&#xff1a;256256256 64646464 经常听到——以至 AI 也会这么回答的&#xff1a;base64 编码用于将二进制数据&#xff0c;转换为文本数据。但是&#xff0c;众所周知&#xff0c;在数字电子计算机中&#…

电子电路基础2(杂乱)

电容器 容抗 滤波电路&#xff08;半波&#xff09; 全波整流 因为A点的电压比D点的电压高&#xff0c;所以D点会走向C点 电感基础 什么是电感器&#xff1f; 一种把电能转换成磁能&#xff0c;并可以将磁能存储起来的元器件。 在嵌入式开发中&#xff0c;电感主要用于动态能量…

LazyOwn RedTeam/APT 框架是第一个具有人工智能驱动的 CC 的 RedTeam 框架

一、软件介绍 文末提供程序和源码下载 LazyOwn RedTeam/APT 框架是第一个具有人工智能驱动的 C&C 的 RedTeam 框架&#xff0c;具有隐藏活动的 rootkit、与 Windows/Linux/Mac OSX 兼容的不可检测的可塑植入物&#xff0c;以及自配置后门。凭借其 Web 界面和强大的…

电脑的ip地址会自动变怎么办?原因解析和解决方法

在当今互联网时代&#xff0c;IP地址是每台联网设备的"身份证"&#xff0c;但很多用户都遇到过IP地址自动变化的情况。这种现象既可能发生在内网&#xff08;局域网&#xff09;环境中&#xff0c;也可能出现在外网&#xff08;公网&#xff09;连接中。要理解IP地址…

PDF 转 HTML5 —— HTML5 填充图形不支持 Even-Odd 奇偶规则?(第一部分)

在填充 PDF 中的图形时&#xff08;以及许多其他技术中&#xff09;&#xff0c;你可以选择使用 Even-Odd&#xff08;奇偶&#xff09; 或 Non-Zero&#xff08;非零&#xff09; 填充规则。 对于那些已经在想“你在说啥&#xff1f;”的朋友&#xff0c;别担心&#xff0c;我…