words and images

This commit is contained in:
Will McGugan
2024-12-06 09:10:21 +00:00
parent 2791266e4e
commit d506af3a62
9 changed files with 185 additions and 9 deletions

View File

@@ -0,0 +1,21 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 623.0110473632812 172.49923719399783" width="623.0110473632812" height="172.49923719399783">
<!-- svg-source:excalidraw -->
<!-- payload-type:application/vnd.excalidraw+json --><!-- payload-version:2 --><!-- payload-start -->eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nM2U22rbQFx1MDAxMIbv/Vx1MDAxNEa9bZQ9XHUwMDFmXHUwMDAypZA0hLTBXHI4pTilXHUwMDE0WVpZQrKkSms7aci7d6Q4XsWtQ1posS5cdTAwMTZ2Znfmn9lPczdcdTAwMThcdTAwMGU9e1tcdTAwMTnvaOiZmzDI06hcdTAwMGVW3uvWvjR1k5ZcdTAwMDW4SLdvykVcdTAwMWR2J1x1MDAxM2ur5ujwcFx1MDAxZdSZsVVcdTAwMWWExl+mzVwiyFx1MDAxYruI0tJcdTAwMGbL+WFqzbx5266jYG7eVOU8srXvklx1MDAxY5gotWX9kMvkZm5cbttA9C+wXHUwMDFmXHUwMDBl77q1p642oVxyilluulx1MDAwYp3LXHTUets4KotOq4KPY6Tp5kDavINs1kTgjUGxcZ7W5H2Y1MnFdzxNwtPl9XjUyNE0XHUwMDFiuaRxmudje5t3oppcdTAwMTJqcb7G1mVmPqeRTVx1MDAxZbvWs++6VZeLWVKYpi1cdTAwMWVvrGVcdTAwMTWEqb1tbVxibaxcdTAwMGZcdTAwMWQ4XHUwMDFhOstccuxcdTAwMGVcdTAwMDT1OeKcUKGwYEhcdTAwMTC+8bdcdTAwMTEolz5WmnGmpaBcdTAwMTRJviXtpMzhJUDaK6wlXHUwMDBliVx1MDAxMzdccsJsXHUwMDA2XG6LyJ1cdHik4tidWa1cdTAwMGJcdTAwMTaI+lxiY8QkXHUwMDE1lCjsXHUwMDFhkJh0lti2J8pXjDGCqVx1MDAxMlx1MDAxMqu+XHUwMDEw072IpFxuMyWE3Dja7NV51KHx1T1DXHJQnbc3ikWe93tZROtePlwi5CCia8u9K689f9qDz2VYVFHwQFx0lpQyLFxiVko6jvK0yLbT52WYObBcdTAwMDa9XFx/xjMnZFx1MDAxN9BcdTAwMWEhXHQ4I/xioC+zM/w+mVxcXHUwMDEwXHUwMDFkf7sqP02v8/LjeN+BJj5RQkO/XHUwMDExXHUwMDEzfUza+0Qz4ExcdTAwMTNcIjXhwPRumkmsXHJjz9M8JTGZTn+lmXLkKy4lZtBvIVXvn3I4XHUwMDBin1x0LFx1MDAxMFxmmVx1MDAwZWe2jTOmXHUwMDE4PEyjveSZw9/2X3iGXHUwMDE3l7uAplx1MDAxMlx0xEHNy4E+WY1XJrnKJseJvsTV5KxcdTAwMTlcdTAwMWTsN9BcdTAwMThrXHUwMDFmXHUwMDAxXGJcbmhVhCqknlx1MDAxMk1hgFMmqMZCI457429cdTAwMWJpgyhA9TzScVx1MDAxY+pQ/yOkXHUwMDA1jG8msaD7SDTBfzGhYe2CekFVjS2E3EiD0tJonP4wT8J4y9Ssjn/b9/bzXHUwMDA2a/0tiaar835w/1x1MDAxM/xcdTAwMThbnyJ9<!-- payload-end -->
<defs>
<style class="style-fonts">
@font-face {
font-family: "Virgil";
src: url("https://file%2B.vscode-resource.vscode-cdn.net/Users/willmcgugan/.vscode/extensions/pomdtr.excalidraw-editor-3.7.4/public//dist/excalidraw-assets/Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
src: url("https://file%2B.vscode-resource.vscode-cdn.net/Users/willmcgugan/.vscode/extensions/pomdtr.excalidraw-editor-3.7.4/public//dist/excalidraw-assets/Cascadia.woff2");
}
@font-face {
font-family: "Assistant";
src: url("https://file%2B.vscode-resource.vscode-cdn.net/Users/willmcgugan/.vscode/extensions/pomdtr.excalidraw-editor-3.7.4/public//dist/excalidraw-assets/Assistant-Regular.woff2");
}
</style>
</defs>
<rect x="0" y="0" width="623.0110473632812" height="172.49923719399783" fill="#ffffff"></rect><g stroke-linecap="round" transform="translate(10 133.65481580727908) rotate(0 301.5055236816406 14.422210693359375)"><path d="M7.21 0 C153.8 1.19, 300.24 1.5, 595.8 0 C599.7 3.6, 604.82 -0.83, 603.01 7.21 C603.55 10.8, 600.38 13.49, 603.01 21.63 C601.83 25.14, 598.51 26.61, 595.8 28.84 C462.34 25.32, 327.98 25.56, 7.21 28.84 C3.48 26.79, -2.06 23.32, 0 21.63 C-1.23 16.03, 1.94 10.15, 0 7.21 C1.99 2.32, 4.84 1.65, 7.21 0" stroke="none" stroke-width="0" fill="#a5d8ff"></path><path d="M7.21 0 C131.49 1.68, 256.9 1.46, 595.8 0 M7.21 0 C187.79 2.19, 367.96 2.56, 595.8 0 M595.8 0 C598.99 0.97, 604.55 2.5, 603.01 7.21 M595.8 0 C598.7 -1.96, 603.13 1.07, 603.01 7.21 M603.01 7.21 C603.95 12.35, 601.66 13.96, 603.01 21.63 M603.01 7.21 C603.48 12.17, 603.53 18.82, 603.01 21.63 M603.01 21.63 C604.26 28.11, 600.97 29.34, 595.8 28.84 M603.01 21.63 C602.96 28.33, 599.84 29.21, 595.8 28.84 M595.8 28.84 C458.45 28.69, 319.07 28.35, 7.21 28.84 M595.8 28.84 C447.81 26.6, 300.16 27.23, 7.21 28.84 M7.21 28.84 C3.28 29.1, -0.43 27.58, 0 21.63 M7.21 28.84 C2.01 29.02, 1.9 24.45, 0 21.63 M0 21.63 C1.1 16, 0.95 10.94, 0 7.21 M0 21.63 C0.85 16.58, 0.69 12.1, 0 7.21 M0 7.21 C-0.1 3.34, 2.5 -1.83, 7.21 0 M0 7.21 C-0.31 2.23, 2.09 0.6, 7.21 0" stroke="#1971c2" stroke-width="2" fill="none"></path></g><g stroke-linecap="round" transform="translate(11.218536376953125 70.48458875649783) rotate(0 175.42885735483912 13.230804443359375)"><path d="M6.62 0 C91.52 1.52, 175.9 4.86, 344.24 0 C350.46 -3.23, 349.59 0.49, 350.86 6.62 C348.62 10.65, 350.14 16.33, 350.86 19.85 C348.76 22.02, 346.06 25.93, 344.24 26.46 C264.64 22.58, 183.37 22.39, 6.62 26.46 C0.15 23.34, 1.07 22.34, 0 19.85 C1.78 14.81, 1.44 13.33, 0 6.62 C2.43 3.85, 3.06 0.6, 6.62 0" stroke="none" stroke-width="0" fill="#b2f2bb"></path><path d="M6.62 0 C81.03 -0.09, 153.93 1.16, 344.24 0 M6.62 0 C76.19 0.52, 146.43 0.18, 344.24 0 M344.24 0 C350.19 0.09, 349.2 0.5, 350.86 6.62 M344.24 0 C348.77 -1.34, 350.19 1.73, 350.86 6.62 M350.86 6.62 C349.73 10.18, 352.02 15.66, 350.86 19.85 M350.86 6.62 C351.43 10.61, 351.36 14.04, 350.86 19.85 M350.86 19.85 C351.22 24.75, 348.6 28.1, 344.24 26.46 M350.86 19.85 C350.09 24.63, 347.15 26.23, 344.24 26.46 M344.24 26.46 C246.39 26.62, 150.1 26.7, 6.62 26.46 M344.24 26.46 C271.17 25.35, 197.87 24.87, 6.62 26.46 M6.62 26.46 C1.78 27.6, -0.34 24.41, 0 19.85 M6.62 26.46 C4.1 24.47, 0.57 24.22, 0 19.85 M0 19.85 C0.77 14.51, 0 10.39, 0 6.62 M0 19.85 C0.38 15.53, -0.16 12.72, 0 6.62 M0 6.62 C0.09 0.38, 1.94 -0.15, 6.62 0 M0 6.62 C-0.31 2.81, 1.03 -0.35, 6.62 0" stroke="#2f9e44" stroke-width="2" fill="none"></path></g><g stroke-linecap="round" transform="translate(192.5730086398787 10) rotate(0 175.42885735483912 13.230804443359375)"><path d="M6.62 0 C87.01 3.99, 169.82 -0.59, 344.24 0 C347.38 -1.72, 354.44 3.13, 350.86 6.62 C349.85 9.61, 349.24 12.84, 350.86 19.85 C348.27 23.73, 346.47 28.48, 344.24 26.46 C265.99 23.08, 184.92 22.36, 6.62 26.46 C3.27 24.55, -2.01 22.35, 0 19.85 C1.15 16.25, 1.44 13.86, 0 6.62 C0.86 2.81, 3.33 2.32, 6.62 0" stroke="none" stroke-width="0" fill="#ffc9c9"></path><path d="M6.62 0 C97.63 1.79, 187.23 1.42, 344.24 0 M6.62 0 C109.53 -1.9, 214.03 -2.2, 344.24 0 M344.24 0 C346.99 -1.7, 350.96 1.04, 350.86 6.62 M344.24 0 C347.99 -0.47, 351.86 3.76, 350.86 6.62 M350.86 6.62 C351.81 11.66, 350.2 17.68, 350.86 19.85 M350.86 6.62 C351.37 10.15, 351.07 12.74, 350.86 19.85 M350.86 19.85 C350.81 25.9, 347.99 26.78, 344.24 26.46 M350.86 19.85 C349.35 24.02, 348.34 28.42, 344.24 26.46 M344.24 26.46 C235.52 25.75, 126.99 25.63, 6.62 26.46 M344.24 26.46 C268.25 28.47, 191.53 28.76, 6.62 26.46 M6.62 26.46 C1.86 26.61, 1.65 22.53, 0 19.85 M6.62 26.46 C2.77 26.43, 0.26 26, 0 19.85 M0 19.85 C0.02 15.53, -0.34 14.98, 0 6.62 M0 19.85 C-0.13 17.3, -0.07 13.54, 0 6.62 M0 6.62 C-0.27 2.05, 1.94 0.53, 6.62 0 M0 6.62 C-1.17 1.85, 1.09 0.09, 6.62 0" stroke="#e03131" stroke-width="2" fill="none"></path></g></svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 38 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -12,6 +12,116 @@ Version 1.0 announcements often come with a happy hour and merch.
Well we haven't got any merch.
## The Compositor
The job of the compositor is to combine widgets in to a single view.
We do this because the terminal itself has no notion of overlapping windows in the way a desktop does.
If an app wants to display overlapping components it must combine them into a single update.
Here's a video I generated over a year ago, demonstrating the output of the compositor:
<div class="video-wrapper">
<iframe width="100%" height="auto" src="https://www.youtube.com/embed/T8PZjUVVb50" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
### The algorithm
You could be forgiven in thinking that the terminal is regular grid of characters and we can treat it like a 2D array.
If that were the case, we could use [painter's algorithm](https://en.wikipedia.org/wiki/Painter's_algorithm) to handle the overlapping widgets.
In other words, sort them back to front and render them as though they were bitmaps.
Unfortunately the terminal is *not* a true grid.
Some characters such as CJK (Chinese, Japanese, and Korean) and many emoji are double the width of latin alphabet characters &mdash; which complicates things (to put it mildly).
Textual's way of handling this is inherited from [Rich](https://github.com/Textualize/rich).
Anything you print in Rich, first generates a list of [Segments](https://github.com/Textualize/rich/blob/master/rich/segment.py) which consist of a string and associated style.
These Segments are only converted into text with [ansi escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) at the very last moment.
Textual works with same Segment object.
Widgets all produce a list of segments, which are further processed by the compositor.
!!! tip "Switch the Primitive"
I hope I've earned the opertunity to dispense unsolicited pearls of wisdom.
If a problem is intractable, it can often be simplified by changing what you consider to be the fundamental atomic data and operations you are working with.
I call this "switching the primitive".
### Thinking in Segments
In the following illustration we have an app with three widgets; the "screen" (in blue) plus two floating widgets (in red and green).
There will be many more widgets in a typical app, but this is enough to show how it works.
<div class="excalidraw">
--8<-- "docs/blog/images/compositor/widgets.excalidraw.svg"
</div>
The lines are lists of Segments produced by the widget renderer.
The compositor will combine those lists in to a single list where nothing overlaps.
To illustrate how this process works, let's consider the highlighted line about a quarter of the way down.
### Compositing a line
Imagine you could view the terminal and widgets side on, so that you see a cross section of the terminal and the floating widgets.
It would appear something like the following:
<div class="excalidraw">
--8<-- "docs/blog/images/compositor/cuts0.excalidraw.svg"
</div>
Those lines are produced when the widget is rendered and consist of a list of segments.
We want to combine these lists into a single list that covers the width of the terminal.
That way we can update everything in a single write.
### Step 1. Finding the cuts.
First thing the compositor does is to find every offset where a list of segments begins or ends.
<div class="excalidraw">
--8<-- "docs/blog/images/compositor/cuts1.excalidraw.svg"
</div>
### Step 2. Applying the cuts.
The next step is to divide every list of segments at the cut offsets.
This will produce smaller lists of segments, which in the compositor code we refer to as *chops*.
<div class="excalidraw">
--8<-- "docs/blog/images/compositor/cuts2.excalidraw.svg"
</div>
### Step 3. Discard chops.
Only the top-most chops will actually be visible to the viewer, so we can discard any chop that isn't at the top.
<div class="excalidraw">
--8<-- "docs/blog/images/compositor/cuts3.excalidraw.svg"
</div>
### Step 4. Combine.
Now all that's left is to combine the top-most chops in to a single list of Segments.
It is this list of segments that becomes a line in the terminal.
<div class="excalidraw">
--8<-- "docs/blog/images/compositor/cuts4.excalidraw.svg"
</div>
### What I omitted
There is more going on than this explanation may suggest.
Widgets may contain other widgets which are clipped to their *parent's* boundaries, and widgets that contain other widgets may also scroll &mdash; the compositor must take all of this in to account.
Additionally, the compositor can do partial updates.
In other words, if you click a button and it changes color the compositor can update just that button.
The compositor does all of this fast enough to enable smooth scrolling, even with a metric tonne of widgets on screen.
Surprising fast, given it is essentially number crunching, which isn't considered Python's forté.

View File

@@ -28,6 +28,8 @@
src: url("https://unpkg.com/@excalidraw/excalidraw@0.12.0/dist/excalidraw-assets/Cascadia.woff2");
}
</style>
<script lang="js">

View File

@@ -20,6 +20,7 @@ h3 .doc-heading code {
body[data-md-color-primary="black"] .excalidraw svg {
will-change: filter;
filter: invert(100%) hue-rotate(180deg);
width: 100%;
}
body[data-md-color-primary="black"] .excalidraw svg rect {