docs(plugins): Add more info on Windows, Splits, and SplitContainers (#6949)

From
https://github.com/Chatterino/chatterino2/pull/6687#discussion_r3068317999,
this adds more documentation on splits and so on. It also adds a utility
to dump the layout to GraphViz which can be handy.

Reviewed-by: Mm2PL <mm2pl+gh@kotmisia.pl>
Reviewed-by: pajlada <rasmus.karlsson@pajlada.com>
Parent-pr: 6687
This commit is contained in:
Nerixyz
2026-04-26 15:02:53 +00:00
committed by GitHub
parent 3ab369982e
commit 103de42bdb
4 changed files with 166 additions and 5 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

+137
View File
@@ -0,0 +1,137 @@
-- SPDX-FileCopyrightText: 2026 Contributors to Chatterino <https://chatterino.com>
--
-- SPDX-License-Identifier: CC0-1.0
--[=[
Dumps the window tree in a Chatterino instance to GraphViz.
Usage: /windows
]=]
--
local function enum_to_string(enum)
local rev = {}
for k, v in pairs(enum) do
rev[v] = k
end
return function(v)
local r = rev[v]
if r ~= nil then
return r
end
return "unknown-value(" .. v .. ")"
end
end
local SPCNT_to_string = enum_to_string(c2.SplitContainerNodeType)
local WindowType_to_string = enum_to_string(c2.WindowType)
--- Slightly changed version of 11.6 String Buffers: https://www.lua.org/pil/11.6.html
local function new_strbuf()
local s = { "" }
---@param ... string
function s:push(...)
local size = #self
for i, v in ipairs({ ... }) do
self[size + i] = v
end
for i = #self - 1, 1, -1 do
if string.len(self[i]) > string.len(self[i + 1]) then
break
end
self[i] = self[i] .. table.remove(self)
end
end
function s:flush()
return table.concat(self)
end
return s
end
local outbuf = new_strbuf()
---@param s c2.Split|nil
---@param id string
local function do_split(s, id)
if not s then
outbuf:push(id, '[label="(nil)"]')
return
end
outbuf:push(id, '[label="Split(channel=', s.channel:get_name(), ')"];\n')
end
---@param p c2.SplitContainerNode|nil
---@param id string
local function do_node(p, id)
if not p then
outbuf:push(id, '[label="(nil)"]')
return
end
outbuf:push(
id,
'[label="SplitContainerNode(type=',
SPCNT_to_string(p.type),
", hflex=",
string.format("%.2f", p.horizontal_flex),
", vflex=",
string.format("%.2f", p.vertical_flex),
')"];\n'
)
if p.split then
local sid = id .. "s"
do_split(p.split, sid)
outbuf:push(id, "->", sid, "\n")
end
for i, child in ipairs(p:children()) do
local inner_id = id .. "c" .. i
do_node(child, inner_id)
outbuf:push(id, "->", inner_id, "\n")
end
end
---@param p c2.SplitContainer|nil
---@param id string
local function do_container(p, id)
if not p then
outbuf:push(id, '[label="(nil)"]')
return
end
outbuf:push(id, '[label="SplitContainer"];\n')
local base_id = id .. "b"
do_node(p.base_node, base_id)
outbuf:push(id, "->", base_id, "\n")
end
---@param win c2.Window
---@param id string
local function do_window(win, id)
outbuf:push(id, '[label="Window(type=', WindowType_to_string(win.type), ')"];\n')
for i = 1, win.notebook.page_count do
local inner_id = id .. "p" .. tostring(i)
do_container(win.notebook:page_at(i - 1), inner_id)
outbuf:push(id, "->", inner_id, "\n")
end
end
c2.register_command("/windows", function(ctx)
outbuf = new_strbuf()
outbuf:push("digraph {\nrankdir=TB;\nnode[shape=rect];\n")
for i, win in ipairs(c2.windows:all()) do
local id = "w" .. i
do_window(win, id)
end
outbuf:push("}\n")
local s = outbuf:flush()
local msg = c2.Message.new({
elements = {
{
type = "text",
color = "link",
link = { type = c2.LinkType.CopyToClipboard, value = s },
text = "Copy",
},
},
})
ctx.channel:add_message(msg)
end)
+28 -5
View File
@@ -897,12 +897,22 @@ Requires the [network permission](#permissions).
#### `Split`
A split. See [Anatomy of a Chatterino window](https://wiki.chatterino.com/Glossary/#anatomy-of-a-chatterino-window).
This holds a `channel` ([Channel](#channel)) that's open in this split.
A leaf node in the tab-tree of a Chatterino window.
It shows a `channel` ([Channel](#channel)) along with a header and an input box.
See [Anatomy of a Chatterino window](https://wiki.chatterino.com/Glossary/#anatomy-of-a-chatterino-window).
#### `SplitContainerNode`
A node in a split container. It has the following fields:
A node in a split container.
It can be one of the following `type`s (`SplitContainerNodeType`):
- `EmptyRoot`: This is the only node in the `SplitContainer` and it's empty.
- `Split`: This is a leaf node which holds a `split`.
- `VerticalContainer`: The children of this node are arranged vertically. Each child's `vertical_flex` indicates how much space it takes.
- `HorizontalContainer`: The children of this node are arranged horizontally. Each child's `horizontal_flex` indicates how much space it takes.
It has the following fields:
- `type` (`SplitContainerNodeType`) The type of this node
- `split` ([`Split`](#split)?) The split contained in this code (if this is a split node)
@@ -916,7 +926,8 @@ Get all children ([`SplitContainerNode`](#splitcontainernode)) of this node.
#### `SplitContainer`
A container with potentially multiple splits. It has the following fields:
A container with potentially multiple splits each tab in Chatterino contains one `SplitContainer`.
It has the following fields:
- `selected_split` ([`Split`](#split)) The currently selected split.
- `base_node` ([`SplitContainerNode`](#splitcontainernode)) The top level node.
@@ -927,6 +938,8 @@ Get all splits ([`Split`](#split)) contained in this container.
#### `SplitNotebook`
The tab bar in a Chatterino window. Each tab is called a "page" which holds a `SplitContainer`.
- `selected_page` ([`SplitContainer`](#splitcontainer)?) The currently selected page.
- `page_count` (`integer`) The number of pages/tabs.
@@ -945,7 +958,17 @@ It has the following fields:
#### `WindowManager`
It has the following fields:
Conceptually the windows in Chatterino are the root nodes of a tree with split containers as intermediate nodes and splits as leaf nodes.
[`docs/resources/window-graph.lua`](resources/window-graph.lua) is a utility to generate a GraphViz description of the window state.
For example, the following tab is shown below:
![Screenshot of a Chatterino tab](resources/window-example.png)
GraphViz output:
![GraphViz output for tab](resources/window-example.svg)
`WindowManager` has the following fields:
- `main_window` ([`Window`](#window)) The main window.
- `last_selected_window` ([`Window`](#window)) The last selected window (or the main window if none were selected last).