跳至内容

Playground

在浏览器中编写演示文稿,幻灯片实时跟随:编译为 WebAssembly 的 go-slides,带自动补全、帮助和示例。

打开 Playground →

Playground 就是 go-slides 本身(v0.20.2),编译为 WebAssembly 后在你的浏览器中运行。 在左侧输入演示文稿,右侧的幻灯片会随之更新。不会向服务器发送任何内容:演示文稿不会,分享链接也不会。

Playground 自身的界面(功能区、帮助、提示信息)使用页面所用的语言: 打开 /zh/playground/ 即为简体中文,本站的十二种语言同理。页面上的语言选择器可以切换语言, 你的选择会保存在此浏览器中;链接中指明了语言时,始终以链接为准。示例演示文稿仍为英文。 乌克兰语、俄语、中文、日语和韩语使用 Noto Sans 的子集(0.06 至 2.3 MiB)绘制,页面只为这些语言下载它。

功能

  • 实时预览 每张幻灯片,渲染在页面主线程之外进行(Web Worker),因此输入时永远不必等待。点击一张幻灯片,编辑器会跳到对应的行; 移动光标,预览会滚动到对应的幻灯片。

  • 放映:即演示本身,支持键盘、屏幕控制器和过渡效果。

  • 演讲者视图:Present(或在放映中按 p)会在第二个窗口中打开它: 当前幻灯片、下一步、演讲者备注(演示文稿中的 <!-- comments -->)、时钟和计时器。 方向键、Space、Home 和 End 在两个窗口中都可使用,两个窗口相互同步, 编辑演示文稿时也是如此。如果窗口没有打开,请允许该站点弹出窗口。 概览视图(o)仅在下载的页面中提供。

  • 自动补全,边输入边提示:指令键(注释中的单页指令 _)、 kind(幻灯片类型)、theme、size、paginate 的取值,以及 {{ }} 内的模板函数和数据字段。

  • 帮助,针对光标下的内容:指令、kind(及其设置的内容)、模板函数。 帮助面板中也有指向本文档各页的链接。自 v0.16.0 起, 指令帮助会跟随界面语言,支持 11 种语言。

  • 问题 显示在对应的行上:模板、YAML 和 HCL 错误会在编辑器中高亮, 并在其下方列出,预览则保留最后一次成功渲染的演示文稿。

  • 示例:欢迎演示文稿、生成幻灯片的模板、幻灯片 kind、 HCL front matter、主题与单页指令,以及数学公式、图片和背景 (![bg]、分栏的 ![bg left:40%]、滤镜)。

  • 文件:编辑器左侧的面板就是演示文稿的项目,其结构与 命令行工具和 VS Code 扩展的读取方式一致:deck.md、 slides.yaml、themes/、kinds/、media/。

    • 点击文件即可编辑。编辑器会按文件语言着色 (Markdown、YAML、HCL、JSON、CSS),并标出其中的问题。
    • 右键(或 Shift+F10)可使用 New File、New Folder、Rename(F2)、 Delete(会先确认)以及 New theme / New kind。把文件拖到文件夹上即可移动。 键盘操作遵循 WAI-ARIA 树形控件模式,包括输入时自动定位。
    • Project ▸ New theme 会创建 themes/my-gaia.css(也可基于 default、 uncover):其中包含对内置主题的 @import 以及你添加的规则。 它会加入 Theme 列表。New kind 和 New data file 同样从模板开始。 每个新文件都会登记在 slides.yaml 中,因此 slides build deck.md 能找到它。
    • Project ▸ Download project (.zip) 会保存全部内容。解压后, 用 slides build deck.md 即可构建。Open project 可以重新打开一个 .zip 或 文件夹。会跳出其所在文件夹(..、绝对路径)、包含超过 256 个文件或单个文件超过 8 MiB, 或解压后膨胀超过 100 倍的压缩包会被拒绝。
    • 所有内容都与草稿一起保存在当前浏览器中。一个项目最多可容纳 64 MiB,文件列表下方的一行会显示已用空间。
  • 媒体:将图片、视频或音频拖放或粘贴到编辑器中,它会被放进 media/,并在演示文稿中引用:![](media/photo.png)。把文件拖到面板中的某个文件夹上, 即可放到该文件夹中。演示文稿保持精简易读,也不会上传任何内容。 预览的沙盒无需放宽限制即可加载这些媒体(由页面把字节数据交给它)。Save as ▸ HTML page 会把它们放进页面中。https: 图片同样可用,但浏览器会从其服务器获取, 服务器因此会看到你的地址。预览不发送 referrer。

  • 可调整大小的窗格:文件面板、编辑器、幻灯片缩略图、预览和帮助之间以可拖动的分隔条隔开 (指针移到分隔条上时会变为调整大小的光标)。分隔条旁边的窗格占用调整出的空间, 其他窗格保持大小不变。双击分隔条,或在其上按 Enter,可以收起窗格,再次操作则恢复。 使用键盘时,F6 在分隔条之间切换,方向键每次移动 16 px,Home 和 End 将分隔条前面的窗格 调到最小和最大,Escape 返回编辑器。View 选项卡仍可关闭窗格。 你设置的尺寸会与草稿一起保存在当前浏览器中;默认尺寸(190、168 和 280 px, 编辑器与预览按 6 比 5 分配剩余空间)从不保存。

  • 关闭窗格:每个窗格的标题栏都有一个带提示的小 × 按钮。其他窗格分享它的空间, 窗格再打开时恢复原来的宽度。要重新打开,可使用 View 选项卡里它的开关(每个窗格都有一个)。 编辑器和预览可以各自关闭, 但不能同时关闭:最后一个会拒绝关闭,它的按钮变灰,提示会说明原因。使用键盘时,先按 F6, 再按 Tab 或 Shift+Tab 到达 × 按钮,按 Enter 或空格关闭窗格,键盘随后回到编辑器。 哪些窗格是打开的,会与尺寸一起保存在当前浏览器中(为任务而打开的 Versions、GitHub 和主题编辑器,启动时是关闭的)。

  • 格式:Format 选项卡会替你编辑 Markdown,每按一次就是一步撤销。

    • 文本:Bold、Italic、Strikethrough 和 Inline code 作用于所选内容 或光标所在的单词,并可切换(Ctrl+B、Ctrl+I、Ctrl+Shift+X 和 Ctrl+E;Mac 上用 Cmd)。光标位于相应标记内时,按钮显示为按下状态。 Markdown 没有下划线:Underline(Ctrl+U)会用 <u>…</u> 包裹文本, 这个标签在 Marp 默认的 HTML 白名单中,因此关闭原始 HTML 时也能生效。
    • 段落:Heading 1 到 3(Ctrl+Alt+1..3)、项目符号(Ctrl+Shift+8)、 编号(Ctrl+Shift+7)和引用作用于所选的行,并可切换。 水平线使用 <hr> 标签:因为在 Marp 中,---、*** 和 ___ 都会开始一张新幻灯片。
    • 表格:通过网格选择列数和行数。光标位于表格中时,可以添加或删除行、列, 并设置列的对齐方式(:--、:-:、 --:);每次编辑后表格都会重新对齐。
    • 幻灯片文本 左对齐、居中或右对齐,会在当前幻灯片中写入一个 <style scoped> 块。 Marp 没有对齐语法,style 属性也无法通过默认的 HTML 白名单,但 scoped 块可以, 因此关闭原始 HTML 时也能生效,并会显示在预览、HTML 页面和 PDF 中。
    • 链接(Ctrl+K)会写出 [text](url),并选中其中的 url。
  • 高亮 front matter 中任意层级的键,支持 YAML(包括 {a: 1} 形式的映射)和 HCL。

  • 主题 选择器:default、gaia、uncover、项目中的主题 (themes/*.css),以及演示文稿自己定义的主题(<style> 中的 /* @theme name */)。 它会把 theme: 写入 front matter(YAML 或 HCL),因此分享链接会保留主题;编辑 front matter 时,选择器也会随之更新。

  • 输入时预览会保持位置:只替换发生变化的幻灯片,切换主题后预览会重新定位到同一张幻灯片。

  • 分享链接:演示文稿就包含在链接中(压缩后放在其 # 部分), 因此打开链接即可打开演示文稿。链接最多可携带压缩后 64 KiB 的内容,且只包含演示文稿:当项目还有其他内容 (主题、kind、媒体)时,Share 会给出提示并提供 .zip。

  • 另存为:

    • HTML 页面,与 slides deck.md 写出的相同;
    • PDF(矢量,文本可选中,带书签大纲);
    • 可编辑的 PowerPoint 或 OpenDocument 演示文稿。

    它们都在你的浏览器中生成,所用代码与 slides --pdf、 --pptx 和 --odp 相同:100 张幻灯片基准演示文稿的 PDF 与命令行生成的 逐字节相同。第一次导出时会下载导出引擎(约 5 MiB,仅下载一次;浏览器会保留它直到下次更新), 并显示进度,边下载边编译。之后只下载幻灯片实际用到的字体。在 3 MB/s 的网络上, 欢迎演示文稿的第一个 PDF 不到 3 秒即可完成。之后导出 100 张幻灯片的演示文稿约需 3 秒。 https: 图片只有在其服务器允许时(CORS)才会被包含;data: 图片则总会被包含。

  • 草稿会被保留:

    • 演示文稿会在输入时保存在当前浏览器中(IndexedDB,仅限本站点), 重新加载页面后会恢复。不会发送到任何地方。项目中的文本文件 (主题、kind、数据、slides.yaml)也以同样方式保存,即使你刚输入完就关闭标签页也不会丢失; 媒体文件在拖入时即被保存。
    • 打开分享链接会打开其中的演示文稿,同时保留你的草稿:Draft ▸ Restore my draft 可将草稿恢复。
    • 打开示例、Draft ▸ Reset to the example(会先确认)或另一个标签页也都不会抹掉草稿: Draft ▸ Restore replaced 可将其换回。
    • Draft ▸ Forget my drafts 会删除所有已保存的内容。

每次导出渲染的都是预览所显示的项目:包括其 slides.yaml、 主题、kind、数据文件和媒体,媒体会嵌入文档中。

以文件形式输出图片(slides --images)无法在浏览器中完成:请使用 命令行工具或 VS Code 扩展。

主题编辑器

开始 ▸ 外观 ▸ 主题编辑器会打开一个面板,用控件编辑项目中的一个主题,预览随之更新。主题以一个主题包文件夹的形式保存,即 theme-src/<name>/;预览和导出读取的样式表 themes/<name>.css 就由它编译而来,所以你看到的就是你导出的。你导入的主题包(面板中的导入…、**添加媒体…**或拖放)会把它的文件保留在那里,因此可以编辑。

  • 调色板:背景、文字、标题、强调色和链接的颜色。选取一种颜色,或直接输入(#1a2b3c、rgb()、hsl() 或颜色名称)。
  • 字体排印:正文、标题和代码的字体,以及它们的字号和字重。可选字体是通用字体和常见字体,以及你添加的字体(添加字体…:.woff2、.woff、.ttf 或 .otf)。
  • 徽标:一张图片(.svg、.png)、它的位置(某个角落或整个背景)和大小。页码和页脚:页码放在哪里,以及大小和对齐方式。
  • 详细信息:theme.hcl 所写的内容:版本、说明、作者、许可证、主页、基础主题(extends)以及所需的 go-slides 版本。
  • 对比度(WCAG):对每一种文字与其背景,实时显示对比度及其等级,AA 或 AAA,或“不达标”。标题按大号文字计算(3:1 即可达到 AA;普通文字需要 4.5:1)。

编辑 CSS 会在编辑器中显示主题的样式表。控件只写入由两条 gsta:visual 注释围起来的代码块,并在你手动修改其中的值时重新读取。你写在代码块之外的内容永远不会被改动。如果你在代码块内加入了控件无法显示的内容,控件就会停止写入,面板会告诉你;恢复该代码块会仅根据其中所含的值重写它,别无其他。

导入… 可打开 .gsta.xz 或单独的 .css。主题包的检查方式与 slides theme verify 相同,限制也相同:不安全的主题包(指向包外的路径、链接、SVG 中的脚本、远程 url()、不匹配的校验和)会被拒绝,并在“问题”中显示其 theme.* 代码,且不会有任何改动。在单独的样式表中,@import "gaia"; 会成为基础主题(extends),因为主题包在那里声明其基础。你添加的字体在你于“详细信息”中填写之前,许可证为 LicenseRef-unspecified。

导出 .gsta.xz 在你的浏览器中构建主题包,并像 slides theme verify 那样重新读取它,因此该文件能通过检查;导出 CSS 会下载编译后的样式表。不会上传任何内容。每次编辑都是撤销的一步(同一控件连续的编辑算作一步),并把被替换的内容保留为一个版本;保存版本… 会以主题名称保存该主题及其版本,显示在“版本”面板中。

撤销、重做与版本

Undo(撤销)和 Redo(重做)位于 Home 选项卡,也可用 Ctrl+Z、Ctrl+Shift+Z 或 Ctrl+Y(Mac 上为 Cmd)。它们先撤销当前所示文件的最后一次编辑(每个文件有自己的历史), 再撤销对整个项目的最后一次操作:选取的示例、Reset、打开的 .zip 或文件夹、恢复的草稿、 删除或重命名的文件或文件夹、恢复的版本。Redo 会把一切恢复到按下 Undo 时的样子。

版本把整个项目保存在此浏览器中,不会向任何地方发送内容。停止输入后不久会保存一个 (最多每 2 分钟一次),对整个项目的每次操作之前、每次导出和生成分享链接时也会保存。 Save version… 会以名称保存一个。Home ▸ Versions 打开 Versions 窗格:

  • Preview 在预览中显示某个版本。
  • Compare with now 逐行显示每个文件的变化,可内联或并排显示; F7 和 Shift+F7 在变化之间移动。
  • Restore 把某个版本放回去;Undo 可撤销这次恢复。
  • Delete 删除某个版本。

保留的内容:最新的 20 个自动版本,之后一天内每小时一个,一个月内每天一个。 命名版本会一直保留,直到你删除(最多 50 个)。所有版本合计最多 96 MiB, 未变化的文件只存储一次。浏览器存储已满时,最旧的自动版本最先被清除。

GitHub

Home ▸ History ▸ GitHub 打开一个窗格,从 GitHub 仓库载入演示文稿的文件夹, 并把你的修改以拉取请求的形式提回去。请填写:

  • 仓库(所有者/名称);
  • 分支(留空:默认分支);
  • 文件夹(留空:根目录):文件夹中必须有 deck.md。

这些字段上方,窗格显示 git 框架:一个小面板,你在其中选择 forge,输入 令牌(已遮蔽), 勾选 在此设备上记住,然后点击 连接(或 用 GitLab 登录)或 断开。该框架是另一个站点 go-slides-usercontent.github.io 的页面,嵌入在 playground 中:playground 页面永远看不到你的令牌。

令牌是细粒度个人访问令牌(创建一个), 仅限所选仓库,权限为 Contents 读写和 Pull requests 读写(Metadata 只读为隐含)。然后:

  • 载入 用 GitHub 上的文件夹替换项目。Undo 可以撤回,原来的项目会保存为 “来自 GitHub” 版本。
  • 提出修改… 询问标题,创建新分支 slides/<标题>-<日期时间> 并打开拉取请求。 它从不写入已有分支或默认分支。打开拉取请求 会带你到那里。
  • 检查变化 在 Compare 视图中比较 GitHub 上较新的版本与你的版本。 采用对方的 会载入它。

GitHub 不使用代理:框架直接与它通信。

令牌在框架中输入,并留在框架所在的站点。playground 页面无法读取它(框架是另一个源,有自己的存储), 页面能发送的任何命令都不会返回它,页面的 Content Security Policy 也完全不允许页面连接 GitHub 或 GitLab。框架只把令牌发送到 api.github.com(或 gitlab.com)。它从不保存到草稿、版本、分享链接或 导出中,也从不到达幻灯片预览或渲染 worker。在此设备上记住 会把它存入框架自己的 localStorage (位于 go-slides-usercontent.github.io,而不是 playground 的存储),直到 断开(或 Forget my drafts)。

如果你用过早期版本: 早期版本保存在 playground 页面自身存储中的令牌,会在你第一次打开此版本时被删除, 窗格会告诉你。请在框架中重新输入(如果想保留,请勾选“记住”)。

提出修改… 属于写入操作,所以框架会先询问你:它显示标题、仓库和文件数量,并等待你点击它自己的 允许 按钮(最多两分钟,一次只处理一个提案)。在你点击之前不会写入任何内容。

仍有两项风险,因为它们是嵌入框架的页面固有的。注入到 playground 页面的脚本可以读取框架的 nonce 并发送框架的 命令:读取你已载入的文件夹,或请求提出提案(你仍须在框架中允许)。它还可以在框架上方绘制一个假面板,自己向你索要 令牌:如果框架看起来不对,或不在应在的位置,请不要输入令牌。两种情况都无法让它读取框架所持有的令牌。请优先使用 只限于所需仓库的令牌。对于团队,自托管服务器模式(即将推出)把令牌 保存在服务器上。

GitLab.com

窗格中有一个 forge 列表:GitHub 和 GitLab.com。使用 GitLab 时,可以提供范围为 api 的个人访问令牌,或点击 用 GitLab 登录(带 PKCE 的 OAuth 授权码,无需密钥)。 登录会打开该框架所在站点的一个小窗口并返回到它;你正在使用的 playground 页面不会被跳转走。 登录得到的令牌只留在框架的内存中(即使选了 在此设备上记住 也不会记在设备上),会在过期前自动刷新, 断开 会在 GitLab 撤销它们。不是由框架发起的登录,或重复使用的链接,都会被拒绝。 本站的 GitLab 应用注册后即可使用登录;在此之前请使用个人访问令牌。

请求只发往 api.github.com 和 gitlab.com,并且只来自框架:playground 页面不发出任何请求, 也不发送 referrer。

为什么需要 api?GitLab 的范围表 对 write_repository 写的是 “Does not support API authentication”:它只用于基于 HTTP 的 git,而 GitLab 提供该服务时不带 CORS,浏览器无法使用。提出修改走 REST API,需要 api, 即 “complete read and write access to the API”。被盗的 api 令牌可以做其用户通过 API 能做的一切,涉及该用户能访问的所有项目。请优先使用只限于演示文稿仓库的项目或群组访问令牌, 或只作为演示文稿项目成员的专用 GitLab 账号。

安全

演示文稿显示在一个不具有页面来源(origin)的沙盒 iframe 中:它既无法访问 Playground, 也无法访问其存储,不能提交表单,也不能进行任何导航。其页面带有内容安全策略(Content Security Policy), 只运行它自己的脚本。演示文稿中的原始 HTML 遵循 Marp 的白名单,与 slides 的默认行为相同。 运行超过 5 秒的渲染(例如陷入循环的模板)会被终止。

演讲者窗口同样处于该沙盒中。它由 Playground 本身打开, 并在以同样方式沙盒化的 iframe 中显示演示文稿。两个 iframe 无法共享存储,因此由 Playground 通过消息在它们之间转发幻灯片序号。 任何运行演示文稿的框架都不会获得 Playground 的来源。

项目中的媒体以消息中的字节数据形式传给沙盒框架, 每个框架在自己的来源中为其创建各自的 blob: URL。由 Playground 创建的 blob: URL 在框架中无法加载,而框架的策略仅为媒体和字体额外放开了 blob:。

Playground 可以从你自己的机器上提供服务,并置于登录之后:参见 自托管。

体积与速度

按 GitHub Pages 提供的形式(gzip;Pages 不提供 brotli),模块大小为 8.8 MiB。它只会下载一次并显示进度条,之后由浏览器缓存,直到下次部署。 在 Apple M4 Max 和 Chrome 155 上针对线上页面测量,页面打开后 2.1 s 编辑器即可使用, 其中 1.5 s 用于下载,首批幻灯片在 2.4 s 时出现。修改 100 张幻灯片的基准演示文稿后,约 30 ms 即可反映到预览中:go-slides 只渲染发生变化的幻灯片,预览中也只替换这些幻灯片。

Created on by tannevaled

Modified on by tannevaled