Go语言实现的在终端演示幻灯片工具
最近比较喜欢去GitHub上猎奇,这不又看到一个有意思的工具,它可以让你在终端中,演示你的幻灯片,而且幻灯片只需要使用Markdown标记语言即可书写,这对我们来说太方便了。
想象一下,你不再需要繁琐的PowerPoint,只需要一个文本编辑工具,使用Markdown标记语言即可写一个幻灯片,然后在终端里演示,这是非常酷的一件事情,现在让我们开始行动吧。
—— 1 ——
首先,你需要安装 https://github.com/maaslalani/slides 这个工具,它完全使用Go语言编写,安装也比较简单。
如果你是Go语言开发者,那么你只需要使用如下命令即可安装它:
go install github.com/maaslalani/slides
如果你是MacOS用户,使用brew即可安装它。
brew install slides
当然,你也可以从这 https://github.com/maaslalani/slides/releases 下载,Mac、Linux的都有,甚至你可以从源代码安装,都随你,都很简单。
—— 2 ——
安装成功后,打开你的终端,输入slides -h就可以看到一些帮助提示了。
slides -h
Slides is a terminal based presentation tool
Usage:
slides <file.md> [flags]
Flags:
--help help for slides
从帮助中,你可以看到,它的确非常简单,只需要一个md文档即可使用。
—— 3 ——
现在,我们使用一个官方的例子,看下演示的效果。
比如我把它下载到我的桌面上,然后我运行如下命令,就可以看到幻灯片的效果了。
➜ Desktop slides slides.md
如上图所示,这就是一个被slides渲染出来的幻灯片的效果,你可以从右下角看到,它一共有7页,当前是第1页。
左下角显示的是我的计算机的用户名称和时间,上面中间这部分,就是当前页的内容。
这时候你可以按上下键翻页,也可以一直敲回车键下一页,进行演示,效果非常棒。
—— 4 ——
slides不仅可以从本地读取md文件,也可以读取网上的md文件,比如刚刚这个md文件,我们可以使用curl读取,然后作为输入流给slides即可。
curl https://github.com/maaslalani/slides/blob/main/examples/slides.md | slides
以上这个命令的效果,和我们上面演示的从桌面读取的,是一样的。
这个总结就是说,slides可以从一个输入流中读取md内容,解析成幻灯片。
—— 5 ——
md文件的内容就是我们常用的、普通的markdown标记语言,那么slides是如何解析成一页页内容的呢?
这个是slides中的约定,它使用---来分页,也就是markdown里面的分割线。遇到一个---,slides就认为要分页了。我们来看下它的分页代码实现:
const (
delimiter = "\n---\n"
altDelimiter = "\n~~~\n"
)
content = strings.ReplaceAll(content, altDelimiter, delimiter)
slides := strings.Split(content, delimiter)
其实就是基于分隔符,使用strings.Split分成一个切片,这个切片就是需要演示的一页页内容。
从源代码中,我们也可以看到,slides其实也支持~~~这个分隔符分页。
—— 6 ——
现在已经分成了一页页内容,那么每一页的md内容,是如何显示到终端里的呢?
相信你自己也能想出来,思路就是要解析MD内容,然后显示到终端中。要自己做这个功能,会比较繁琐,还要考虑样式美观等。
索性有开源,有现成的库可以帮我们做这个事情,它就是 https://github.com/charmbracelet/glamour ,这个是一个可以把md内容渲染到终端的库,并且还自带样式。
Stylesheet-based markdown rendering for your CLI apps.
https://github.com/charmbracelet/glamour
我们看下,在slides中是如何使用它的。
func (m Model) View() string {
r, _ := glamour.NewTermRenderer(m.Theme, glamour.WithWordWrap(0))
slide, err := r.Render(m.Slides[m.Page])
if err != nil {
slide = fmt.Sprintf("Error: Could not render markdown! (%v)", err)
}
slide = styles.Slide.Render(slide)
left := styles.Author.Render(m.Author) + styles.Date.Render(m.Date)
right := styles.Page.Render(fmt.Sprintf("Slide %d / %d", m.Page+1, len(m.Slides)))
status := styles.Status.Render(styles.JoinHorizontal(left, right, m.viewport.Width))
return styles.JoinVertical(slide, status, m.viewport.Height)
}
从以上代码可以看到,new一个Render,然后渲染就可以了。
同时这里也可以看到,渲染出的幻灯片,要和作者、时间以及幻灯片状态,合在一起,组成一个终端的界面,也就是这一页幻灯片。
这里可以看到很多styles.Slide,styles.Author等,这些其实就是用于控制终端布局的,它使用的是lipgloss这个库控制的。
Style definitions for nice terminal layouts. Built with TUIs in mind.
https://github.com/charmbracelet/lipgloss
这个库的使用这里暂不讲,有兴趣的朋友可以研究下,这是个很不错的东西,可以让你写出来更好看的CLI Apps。
—— 7 ——
以上讲到的代码,都是一个Model接口的实现,通过它我们才能使用bubbletea这个终端UI框架,把幻灯片的内容、作者、时间等信息,显示在终端中,也就是我上面的截图看到的这些。
p := tea.NewProgram(model.Model{
Slides: slides,
Page: 0,
Author: user.Name,
Date: time.Now().Format("2006-01-02"),
Theme: styles.SelectTheme(m.Theme),
tea.WithAltScreen())
err = p.Start()
以上是部分代码,用于把幻灯片展示到终端。完整的Model接口实现代码如下所示。
type Model struct {
Slides []string
Page int
Author string
Date string
Theme glamour.TermRendererOption
viewport viewport.Model
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.viewport.Width = msg.Width
m.viewport.Height = msg.Height
return m, nil
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case " ", "down", "k", "right", "l", "enter", "n":
if m.Page < len(m.Slides)-1 {
m.Page++
}
case "up", "j", "left", "h", "p":
if m.Page > 0 {
m.Page--
}
}
}
return m, nil
}
func (m Model) View() string {
r, _ := glamour.NewTermRenderer(m.Theme, glamour.WithWordWrap(0))
slide, err := r.Render(m.Slides[m.Page])
if err != nil {
slide = fmt.Sprintf("Error: Could not render markdown! (%v)", err)
}
slide = styles.Slide.Render(slide)
left := styles.Author.Render(m.Author) + styles.Date.Render(m.Date)
right := styles.Page.Render(fmt.Sprintf("Slide %d / %d", m.Page+1, len(m.Slides)))
status := styles.Status.Render(styles.JoinHorizontal(left, right, m.viewport.Width))
return styles.JoinVertical(slide, status, m.viewport.Height)
}
—— 8 ——
关于bubbletea这个精美的终端UI框架,我这里不做过多介绍,你可以在GitHub上搜索看下。
经常关注GitHub是一件非常有意思的事情,他可以让你知道哪些Repo正在流行,是否可以学习它,甚至引入到团队、公司,提升整体的实力和效率。
同时,通过研究这些Repo,你可以看到它的实现,引用了哪些第三方的库,然后通过这些Repo,又可以学到更多,更有趣的第三方库的使用,这是一件非常有意思的事情,驱动着你对Go语言越来越了解。
—— 精彩推荐 ——
扫码关注
分享、点赞、在看就是最大的支持