diff options
| author | matthew <matt.kosarek@canonical.com> | 2026-03-24 16:23:58 -0400 |
|---|---|---|
| committer | matthew <matt.kosarek@canonical.com> | 2026-03-24 16:23:58 -0400 |
| commit | 5df5ae2fc3f3bcbd53a9e34ba6b8ddaf4b00036a (patch) | |
| tree | 3346cc0490a993d5b993740bb5e1884b399be350 /.astro | |
| parent | ada238192cb091645ad3ac1850cb0be2bb2d62b7 (diff) | |
Diffstat (limited to '.astro')
| -rw-r--r-- | .astro/data-store.json | 2 | ||||
| -rw-r--r-- | .astro/settings.json | 2 |
2 files changed, 2 insertions, 2 deletions
diff --git a/.astro/data-store.json b/.astro/data-store.json index c267528..c719d93 100644 --- a/.astro/data-store.json +++ b/.astro/data-store.json @@ -1 +1 @@ -[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.18.1","content-config-digest","43a771c3b74a5605","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://matthewkosarek.xyz\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{\"light\":\"github-light\",\"dark\":\"dracula\"},\"defaultColor\":false,\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"actionBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false}}","posts",["Map",11,12,41,42,61,62,81,82],"dec_29_2025",{"id":11,"data":13,"body":18,"filePath":19,"digest":20,"rendered":21,"legacyId":40},{"title":14,"date":15,"tags":16},"Update December 29, 2025","2025-12-29",[17],"update","## What have I been up to?\n\n2025 has been one busy year for me! I feel as though I've been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it's getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical.\n\n## Miracle Update\n\nMiracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too.\n\nOne of the major upcoming features in Miracle is a **plugin system**. Having a plugin system in Miracle will empower 3rd party authors to extend the compositor in ways that I haven't currently imagined while also providing me with the flexibility to iterate quickly on designs. Many compositors already have plugin systems. For example, GNOME does this via JavaScript and Hyprland does this by dynamically loading shared libraries at runtime (note: this is my understanding as of writing this). Both of these solutions are reasonable, but they come with a few downsides.\n\nThe \"true\" scripting language solution comes with the overhead of shipping a complicated interpreter inside of the compositor. In addition to this, plugin developers are forced to use a particular language, perhaps one that they are unfamiliar with. While you get increased programming flexibility from using a scripting language, you have to balance this with the introduced complexity of that language.\n\nThe shared library approach also has its downsides. While dynamically loading a shared library at runtime is lightweight, it prevents users of the compositor from safely running plugins unless they first trust the plugin author. The shared library will be running inside of the same process as your very privileged compositor. This increases the attack surface to an extent that I would feel uncomfortable shipping to users.\n\nFor these reasons, I decided that Miracle plugins will be written in **WebAssembly** with the help of [WasmEdge](https://wasmedge.org). The benefits of this approach are:\n\n1. WebAssembly runs in a lightweight bytecode engine\n2. Many languages can compile down to WebAssembly (Rust will have first-class support to start)\n3. WebAssembly plugins will only be able to access APIs that we provide (reducing the requirement of \"trusting\" the plugin author)\n\nThe WebAssembly modules will implement certain functions by signature. Miracle will search for a particular signature in the module. If that signature is found, Miracle will delegate the function call defined by that signature to the WebAssembly module instead of Miracle's internal implementation. In this way, the WASM plugin will only have the data that it needs to perform an action. It is perfectly isolated from the rest of the process while also running quickly in the bytecode engine. Here is an example of an animation plugin that will linearly fade in a window:\n\n```rust\n#[unsafe(no_mangle)]\npub extern \"C\" fn animate(\n data: MiracleAnimationFrameData,\n) -> MiracleAnimationFrameResult {\n\n let progress = data.runtime_seconds / data.duration_seconds;\n let opacity = data.opacity_start + (data.opacity_end - data.opacity_start) * progress;\n MiracleAnimationFrameResult {\n completed: 0,\n has_area: 1,\n area: [data.destination[0], data.destination[1], data.destination[2], data.destination[3]],\n has_transform: 0,\n transform: [0.0; 16],\n has_opacity: 1,\n opacity,\n }\n}\n```\n\nWhile this is still in a prototype phase, the types defined here will be provided by a Rust crate in the future. The system should be very easy to use from Rust.\n\nIn parallel with this work, I have been working on improving the **shell authoring** experience in Miracle. The beginning of this has been the initial [implementation of a background on floating containers](https://github.com/miracle-wm-org/miracle-wm/pull/745), but work is proceeding to things like built-in context menus, workspace overview modes, and much more. The idea is to have simple, built-in versions for many shell components while allowing users to provide their own custom clients for each shell element. To this end, I have been working on [miracle.dart](https://github.com/miracle-wm-org/miracle.dart), which is a Dart API that should enable users to easily interact with Miracle in Flutter and provide Flutter apps as shell elements. This API is still in early stages, however.\n\nAt the same time, I have been working on improving the **floating window management** in Miracle. Miracle should be a competent floating window manager for those who need it.\n\nv0.9.0 of Miracle will probably be a long time in the making. My estimate is that early spring will see it released. However, it should be the penultimate release before I am ready to make v1.0.0 the first, official stable release. And who knows - maybe we'll even have a shell to go along with it!\n\n## Conclusion\n\n2025 has been a whirlwind of a year, and I'm sure that 2026 won't slow down at all for me. A lot of the long term projects that I've been working on are finally coming together, and I feel as though I am on the cusp of making software that I'm truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else.\n\nKeep on making great stuff ✌️","src/content/posts/dec_29_2025.md","038649b01aa2629d",{"html":22,"metadata":23},"\u003Ch2 id=\"what-have-i-been-up-to\">What have I been up to?\u003C/h2>\n\u003Cp>2025 has been one busy year for me! I feel as though I’ve been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it’s getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical.\u003C/p>\n\u003Ch2 id=\"miracle-update\">Miracle Update\u003C/h2>\n\u003Cp>Miracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too.\u003C/p>\n\u003Cp>One of the major upcoming features in Miracle is a \u003Cstrong>plugin system\u003C/strong>. Having a plugin system in Miracle will empower 3rd party authors to extend the compositor in ways that I haven’t currently imagined while also providing me with the flexibility to iterate quickly on designs. Many compositors already have plugin systems. For example, GNOME does this via JavaScript and Hyprland does this by dynamically loading shared libraries at runtime (note: this is my understanding as of writing this). Both of these solutions are reasonable, but they come with a few downsides.\u003C/p>\n\u003Cp>The “true” scripting language solution comes with the overhead of shipping a complicated interpreter inside of the compositor. In addition to this, plugin developers are forced to use a particular language, perhaps one that they are unfamiliar with. While you get increased programming flexibility from using a scripting language, you have to balance this with the introduced complexity of that language.\u003C/p>\n\u003Cp>The shared library approach also has its downsides. While dynamically loading a shared library at runtime is lightweight, it prevents users of the compositor from safely running plugins unless they first trust the plugin author. The shared library will be running inside of the same process as your very privileged compositor. This increases the attack surface to an extent that I would feel uncomfortable shipping to users.\u003C/p>\n\u003Cp>For these reasons, I decided that Miracle plugins will be written in \u003Cstrong>WebAssembly\u003C/strong> with the help of \u003Ca href=\"https://wasmedge.org\">WasmEdge\u003C/a>. The benefits of this approach are:\u003C/p>\n\u003Col>\n\u003Cli>WebAssembly runs in a lightweight bytecode engine\u003C/li>\n\u003Cli>Many languages can compile down to WebAssembly (Rust will have first-class support to start)\u003C/li>\n\u003Cli>WebAssembly plugins will only be able to access APIs that we provide (reducing the requirement of “trusting” the plugin author)\u003C/li>\n\u003C/ol>\n\u003Cp>The WebAssembly modules will implement certain functions by signature. Miracle will search for a particular signature in the module. If that signature is found, Miracle will delegate the function call defined by that signature to the WebAssembly module instead of Miracle’s internal implementation. In this way, the WASM plugin will only have the data that it needs to perform an action. It is perfectly isolated from the rest of the process while also running quickly in the bytecode engine. Here is an example of an animation plugin that will linearly fade in a window:\u003C/p>\n\u003Cpre class=\"astro-code astro-code-themes github-light dracula\" style=\"--shiki-light:#24292e;--shiki-dark:#F8F8F2;--shiki-light-bg:#fff;--shiki-dark-bg:#282A36; overflow-x: auto;\" tabindex=\"0\" data-language=\"rust\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">#[\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">unsafe\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(no_mangle)]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">pub\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> extern\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"C\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> animate\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MiracleAnimationFrameData\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">->\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MiracleAnimationFrameResult\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> progress \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">runtime_seconds \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">/\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">duration_seconds;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> opacity \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">opacity_start \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">+\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> (data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">opacity_end \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">-\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">opacity_start) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">*\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> progress;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MiracleAnimationFrameResult\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> completed\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> has_area\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 1\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> area\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> [data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">], data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">1\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">], data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">2\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">], data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">3\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">]],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> has_transform\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> transform\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> [\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">0.0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">; \u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">16\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> has_opacity\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 1\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> opacity,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>While this is still in a prototype phase, the types defined here will be provided by a Rust crate in the future. The system should be very easy to use from Rust.\u003C/p>\n\u003Cp>In parallel with this work, I have been working on improving the \u003Cstrong>shell authoring\u003C/strong> experience in Miracle. The beginning of this has been the initial \u003Ca href=\"https://github.com/miracle-wm-org/miracle-wm/pull/745\">implementation of a background on floating containers\u003C/a>, but work is proceeding to things like built-in context menus, workspace overview modes, and much more. The idea is to have simple, built-in versions for many shell components while allowing users to provide their own custom clients for each shell element. To this end, I have been working on \u003Ca href=\"https://github.com/miracle-wm-org/miracle.dart\">miracle.dart\u003C/a>, which is a Dart API that should enable users to easily interact with Miracle in Flutter and provide Flutter apps as shell elements. This API is still in early stages, however.\u003C/p>\n\u003Cp>At the same time, I have been working on improving the \u003Cstrong>floating window management\u003C/strong> in Miracle. Miracle should be a competent floating window manager for those who need it.\u003C/p>\n\u003Cp>v0.9.0 of Miracle will probably be a long time in the making. My estimate is that early spring will see it released. However, it should be the penultimate release before I am ready to make v1.0.0 the first, official stable release. And who knows - maybe we’ll even have a shell to go along with it!\u003C/p>\n\u003Ch2 id=\"conclusion\">Conclusion\u003C/h2>\n\u003Cp>2025 has been a whirlwind of a year, and I’m sure that 2026 won’t slow down at all for me. A lot of the long term projects that I’ve been working on are finally coming together, and I feel as though I am on the cusp of making software that I’m truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else.\u003C/p>\n\u003Cp>Keep on making great stuff ✌️\u003C/p>",{"headings":24,"localImagePaths":35,"remoteImagePaths":36,"frontmatter":37,"imagePaths":39},[25,29,32],{"depth":26,"slug":27,"text":28},2,"what-have-i-been-up-to","What have I been up to?",{"depth":26,"slug":30,"text":31},"miracle-update","Miracle Update",{"depth":26,"slug":33,"text":34},"conclusion","Conclusion",[],[],{"title":14,"date":15,"tags":38},[17],[],"dec_29_2025.md","jul_28_2025",{"id":41,"data":43,"body":47,"filePath":48,"digest":49,"rendered":50,"legacyId":60},{"title":44,"date":45,"tags":46},"Update July 28, 2025","2025-07-28",[17],"## What have I been up to?\n\nWhoops! I missed this month's update by a *long* shot, but I still want to get it out there before the end of the month.\n\nThis month was busy busy. I released [v0.6.0 of miracle-wm](https://github.com/miracle-wm-org/miracle-wm/releases/tag/v0.6.0) which adds a bunch of new features, go and check it out if you haven't already! I am nearly finished with the sway/i3 IPC support and have big plans to wrap that up before the middle of August. There really isn't much more to go on that front so I might as well close that chapter. On top of that, the more interesting features of miracle that I have planned (e.g. some built-in shell integrations) are motivating me to wrap up the boring parts first.\n\nAside from miracle, I've continued to land a bunch of things in Mir recently around accessibility. The magnifier work is finally done, so go check that out if you use that feature in your day-to-day! I also did a *ton of work* to fix screenshooting on rotated displays. We were ignoring [wl_surface::set_buffer_transform](https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform) in a big way. Now that we're not doing that, your screenshots should look perfect every time!\n\nLast but not least, the team has been making waves implementing multi-window in the Flutter toolkit. That's some really interesting and exciting work, so stay tuned for that if you're a Flutter developer!\n\nThat's all I got! I will make another post shortly, maybe once miracle v0.7.0 is out. Have a great rest of your summer/winter 🪐","src/content/posts/jul_28_2025.md","eb7072bb3c1eff0c",{"html":51,"metadata":52},"\u003Ch2 id=\"what-have-i-been-up-to\">What have I been up to?\u003C/h2>\n\u003Cp>Whoops! I missed this month’s update by a \u003Cem>long\u003C/em> shot, but I still want to get it out there before the end of the month.\u003C/p>\n\u003Cp>This month was busy busy. I released \u003Ca href=\"https://github.com/miracle-wm-org/miracle-wm/releases/tag/v0.6.0\">v0.6.0 of miracle-wm\u003C/a> which adds a bunch of new features, go and check it out if you haven’t already! I am nearly finished with the sway/i3 IPC support and have big plans to wrap that up before the middle of August. There really isn’t much more to go on that front so I might as well close that chapter. On top of that, the more interesting features of miracle that I have planned (e.g. some built-in shell integrations) are motivating me to wrap up the boring parts first.\u003C/p>\n\u003Cp>Aside from miracle, I’ve continued to land a bunch of things in Mir recently around accessibility. The magnifier work is finally done, so go check that out if you use that feature in your day-to-day! I also did a \u003Cem>ton of work\u003C/em> to fix screenshooting on rotated displays. We were ignoring \u003Ca href=\"https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform\">wl_surface::set_buffer_transform\u003C/a> in a big way. Now that we’re not doing that, your screenshots should look perfect every time!\u003C/p>\n\u003Cp>Last but not least, the team has been making waves implementing multi-window in the Flutter toolkit. That’s some really interesting and exciting work, so stay tuned for that if you’re a Flutter developer!\u003C/p>\n\u003Cp>That’s all I got! I will make another post shortly, maybe once miracle v0.7.0 is out. Have a great rest of your summer/winter 🪐\u003C/p>",{"headings":53,"localImagePaths":55,"remoteImagePaths":56,"frontmatter":57,"imagePaths":59},[54],{"depth":26,"slug":27,"text":28},[],[],{"title":44,"date":45,"tags":58},[17],[],"jul_28_2025.md","may_06_2025",{"id":61,"data":63,"body":67,"filePath":68,"digest":69,"rendered":70,"legacyId":80},{"title":64,"date":65,"tags":66},"Update May 06, 2025","2025-05-06",[17],"## What have I been up to?\n\nI've been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let's start :)\n\nIn the world of miracle-wm, I've been hard at work writing a new settings application for the compositor called [miracle-settings](https://github.com/miracle-wm-org/miracle-settings). While the application is written in Flutter, the logic behind the application is entirely implemented in `libmiracle-wm-config.so`, a new library that will ship with miracle as part of `v0.6.0`. If Flutter isn't your cup of tea, you should be able to implement your own settings app for miracle in the language/toolkit of your choice by simply binding to the C (or C++!) API.\n\nI also implemented [wlr-output-management](https://wayland.app/protocols/wlr-output-management-unstable-v1) in miracle and changed how we do display configuration in a big way. The display configuration will now always be loaded from `$HOME/.config/miracle-wm/display.yaml`. Users should now be able to use apps like [wdisplays](https://github.com/artizirk/wdisplays) to change the output configuration at runtime, which is pretty cool!\n\nOn the Mir project, I've been working on accessibility features (e.g. magnification) in addition to exposing some facilities for compositor authors to write end-to-end tests for Mir-based compositors. These new testing facilities should help us write tests in miracle in a big way.\n\nAlso - my cat who ate a sewing needle last year has turned two! 🐱","src/content/posts/may_06_2025.md","83f373d568b44b68",{"html":71,"metadata":72},"\u003Ch2 id=\"what-have-i-been-up-to\">What have I been up to?\u003C/h2>\n\u003Cp>I’ve been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let’s start :)\u003C/p>\n\u003Cp>In the world of miracle-wm, I’ve been hard at work writing a new settings application for the compositor called \u003Ca href=\"https://github.com/miracle-wm-org/miracle-settings\">miracle-settings\u003C/a>. While the application is written in Flutter, the logic behind the application is entirely implemented in \u003Ccode>libmiracle-wm-config.so\u003C/code>, a new library that will ship with miracle as part of \u003Ccode>v0.6.0\u003C/code>. If Flutter isn’t your cup of tea, you should be able to implement your own settings app for miracle in the language/toolkit of your choice by simply binding to the C (or C++!) API.\u003C/p>\n\u003Cp>I also implemented \u003Ca href=\"https://wayland.app/protocols/wlr-output-management-unstable-v1\">wlr-output-management\u003C/a> in miracle and changed how we do display configuration in a big way. The display configuration will now always be loaded from \u003Ccode>$HOME/.config/miracle-wm/display.yaml\u003C/code>. Users should now be able to use apps like \u003Ca href=\"https://github.com/artizirk/wdisplays\">wdisplays\u003C/a> to change the output configuration at runtime, which is pretty cool!\u003C/p>\n\u003Cp>On the Mir project, I’ve been working on accessibility features (e.g. magnification) in addition to exposing some facilities for compositor authors to write end-to-end tests for Mir-based compositors. These new testing facilities should help us write tests in miracle in a big way.\u003C/p>\n\u003Cp>Also - my cat who ate a sewing needle last year has turned two! 🐱\u003C/p>",{"headings":73,"localImagePaths":75,"remoteImagePaths":76,"frontmatter":77,"imagePaths":79},[74],{"depth":26,"slug":27,"text":28},[],[],{"title":64,"date":65,"tags":78},[17],[],"may_06_2025.md","june_08_2025",{"id":81,"data":83,"body":87,"filePath":88,"digest":89,"rendered":90,"legacyId":100},{"title":84,"date":85,"tags":86},"Update June 08, 2025","2025-06-08",[17],"## What have I been up to?\n\nAnother month has gone by, so I guess it's time to see what I've been up to.\n\nCanonical hosted our company's sprint in Frankfurt, Germany during the month of May. It was a very productive time for the whole team. I always enjoy seeing everyone in person, talking through big issues face-to-face, and exploring a new city. I also got to spend some time in Zurich, Switzerland before the sprint began. Zurich is pretty awesome 🏔️!\n\nIn miracle-wm, I have been going down quite the rabbit-hole 🐇! What began as a plan to fix the remaining warnings in the source code has snowballed into me implementing the rest of the IPC mechanism and testing the entire thing. This has been no small feat, as both sway and i3 implement a *lot* of different commands, and a failure to implement any one of them often leads to half-broken clients. I've had to make some \"executive\" decisions to ignore parts of the protocol that I deem irrelevant now (especially many of the X-specific bits), but it is a mostly compatible implementation. The good news is that I've nearly completed this journey and should be ready to release version `0.6.0` some time in the middle of June.\n\nMiracle is getting closer-and-closer to my vision of it every day. The only problem right now is finding the bandwidth to implement everything that I have in my head :)\n\nOn the Mir side of things, I am still implementing the magnifier glass accessibility feature from before, but with a much-improved technical direction that we arrived at during our time in Frankfurt. Unfortunately, this required a quick detour to properly implement the `overlay_cursor` flag of [zwlr_screencopy_manager_v1::capture_output_region](https://wayland.app/protocols/wlr-screencopy-unstable-v1#zwlr_screencopy_manager_v1:request:capture_output:arg:overlay_cursor), as both screencopy and magnification rely on this same code path. The good news is that I'm quite close on this and it should be landing in full any day now 🤞\n\nI also fixed [this very breaking bug](https://github.com/canonical/mir/pull/3968) that was actively preventing miracle from rendering on my second monitor, so that's good.\n\nIn addition to these two projects, I am also reinvolving myself in the Flutter multi-window work. For those who don't know, we're trying to make it so that the Flutter toolkit can render to multiple surfaces. This is no small feat, as Flutter was originally written with the assumption that only a single \"view\" would ever be drawn to. However we've managed to make some great progress on it thus far, and we're very excited to land the first pull request imminently with the help of the folks over at Google!\n\nI hope you're having a great and productive summer 😎","src/content/posts/june_08_2025.md","50d5a26d5c525d33",{"html":91,"metadata":92},"\u003Ch2 id=\"what-have-i-been-up-to\">What have I been up to?\u003C/h2>\n\u003Cp>Another month has gone by, so I guess it’s time to see what I’ve been up to.\u003C/p>\n\u003Cp>Canonical hosted our company’s sprint in Frankfurt, Germany during the month of May. It was a very productive time for the whole team. I always enjoy seeing everyone in person, talking through big issues face-to-face, and exploring a new city. I also got to spend some time in Zurich, Switzerland before the sprint began. Zurich is pretty awesome 🏔️!\u003C/p>\n\u003Cp>In miracle-wm, I have been going down quite the rabbit-hole 🐇! What began as a plan to fix the remaining warnings in the source code has snowballed into me implementing the rest of the IPC mechanism and testing the entire thing. This has been no small feat, as both sway and i3 implement a \u003Cem>lot\u003C/em> of different commands, and a failure to implement any one of them often leads to half-broken clients. I’ve had to make some “executive” decisions to ignore parts of the protocol that I deem irrelevant now (especially many of the X-specific bits), but it is a mostly compatible implementation. The good news is that I’ve nearly completed this journey and should be ready to release version \u003Ccode>0.6.0\u003C/code> some time in the middle of June.\u003C/p>\n\u003Cp>Miracle is getting closer-and-closer to my vision of it every day. The only problem right now is finding the bandwidth to implement everything that I have in my head :)\u003C/p>\n\u003Cp>On the Mir side of things, I am still implementing the magnifier glass accessibility feature from before, but with a much-improved technical direction that we arrived at during our time in Frankfurt. Unfortunately, this required a quick detour to properly implement the \u003Ccode>overlay_cursor\u003C/code> flag of \u003Ca href=\"https://wayland.app/protocols/wlr-screencopy-unstable-v1#zwlr_screencopy_manager_v1:request:capture_output:arg:overlay_cursor\">zwlr_screencopy_manager_v1::capture_output_region\u003C/a>, as both screencopy and magnification rely on this same code path. The good news is that I’m quite close on this and it should be landing in full any day now 🤞\u003C/p>\n\u003Cp>I also fixed \u003Ca href=\"https://github.com/canonical/mir/pull/3968\">this very breaking bug\u003C/a> that was actively preventing miracle from rendering on my second monitor, so that’s good.\u003C/p>\n\u003Cp>In addition to these two projects, I am also reinvolving myself in the Flutter multi-window work. For those who don’t know, we’re trying to make it so that the Flutter toolkit can render to multiple surfaces. This is no small feat, as Flutter was originally written with the assumption that only a single “view” would ever be drawn to. However we’ve managed to make some great progress on it thus far, and we’re very excited to land the first pull request imminently with the help of the folks over at Google!\u003C/p>\n\u003Cp>I hope you’re having a great and productive summer 😎\u003C/p>",{"headings":93,"localImagePaths":95,"remoteImagePaths":96,"frontmatter":97,"imagePaths":99},[94],{"depth":26,"slug":27,"text":28},[],[],{"title":84,"date":85,"tags":98},[17],[],"june_08_2025.md"]
\ No newline at end of file +[["Map",1,2,9,10],"meta::meta",["Map",3,4,5,6,7,8],"astro-version","5.18.1","content-config-digest","43a771c3b74a5605","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://matthewkosarek.xyz\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{\"light\":\"github-light\",\"dark\":\"dracula\"},\"defaultColor\":false,\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[null,[null,{\"behavior\":\"prepend\",\"properties\":{\"class\":\"heading-anchor\",\"ariaHidden\":\"true\",\"tabIndex\":-1},\"content\":{\"type\":\"text\",\"value\":\"#\"}}]],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"actionBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false}}","posts",["Map",11,12,35,36,55,56,102,103,122,123],"may_06_2025",{"id":11,"data":13,"body":18,"filePath":19,"digest":20,"rendered":21,"legacyId":34},{"title":14,"date":15,"tags":16},"Update May 06, 2025","2025-05-06",[17],"update","## What have I been up to?\n\nI've been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let's start :)\n\nIn the world of miracle-wm, I've been hard at work writing a new settings application for the compositor called [miracle-settings](https://github.com/miracle-wm-org/miracle-settings). While the application is written in Flutter, the logic behind the application is entirely implemented in `libmiracle-wm-config.so`, a new library that will ship with miracle as part of `v0.6.0`. If Flutter isn't your cup of tea, you should be able to implement your own settings app for miracle in the language/toolkit of your choice by simply binding to the C (or C++!) API.\n\nI also implemented [wlr-output-management](https://wayland.app/protocols/wlr-output-management-unstable-v1) in miracle and changed how we do display configuration in a big way. The display configuration will now always be loaded from `$HOME/.config/miracle-wm/display.yaml`. Users should now be able to use apps like [wdisplays](https://github.com/artizirk/wdisplays) to change the output configuration at runtime, which is pretty cool!\n\nOn the Mir project, I've been working on accessibility features (e.g. magnification) in addition to exposing some facilities for compositor authors to write end-to-end tests for Mir-based compositors. These new testing facilities should help us write tests in miracle in a big way.\n\nAlso - my cat who ate a sewing needle last year has turned two! 🐱","src/content/posts/may_06_2025.md","83f373d568b44b68",{"html":22,"metadata":23},"\u003Ch2 id=\"what-have-i-been-up-to\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#what-have-i-been-up-to\">#\u003C/a>What have I been up to?\u003C/h2>\n\u003Cp>I’ve been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let’s start :)\u003C/p>\n\u003Cp>In the world of miracle-wm, I’ve been hard at work writing a new settings application for the compositor called \u003Ca href=\"https://github.com/miracle-wm-org/miracle-settings\">miracle-settings\u003C/a>. While the application is written in Flutter, the logic behind the application is entirely implemented in \u003Ccode>libmiracle-wm-config.so\u003C/code>, a new library that will ship with miracle as part of \u003Ccode>v0.6.0\u003C/code>. If Flutter isn’t your cup of tea, you should be able to implement your own settings app for miracle in the language/toolkit of your choice by simply binding to the C (or C++!) API.\u003C/p>\n\u003Cp>I also implemented \u003Ca href=\"https://wayland.app/protocols/wlr-output-management-unstable-v1\">wlr-output-management\u003C/a> in miracle and changed how we do display configuration in a big way. The display configuration will now always be loaded from \u003Ccode>$HOME/.config/miracle-wm/display.yaml\u003C/code>. Users should now be able to use apps like \u003Ca href=\"https://github.com/artizirk/wdisplays\">wdisplays\u003C/a> to change the output configuration at runtime, which is pretty cool!\u003C/p>\n\u003Cp>On the Mir project, I’ve been working on accessibility features (e.g. magnification) in addition to exposing some facilities for compositor authors to write end-to-end tests for Mir-based compositors. These new testing facilities should help us write tests in miracle in a big way.\u003C/p>\n\u003Cp>Also - my cat who ate a sewing needle last year has turned two! 🐱\u003C/p>",{"headings":24,"localImagePaths":29,"remoteImagePaths":30,"frontmatter":31,"imagePaths":33},[25],{"depth":26,"slug":27,"text":28},2,"what-have-i-been-up-to","#What have I been up to?",[],[],{"title":14,"date":15,"tags":32},[17],[],"may_06_2025.md","jul_28_2025",{"id":35,"data":37,"body":41,"filePath":42,"digest":43,"rendered":44,"legacyId":54},{"title":38,"date":39,"tags":40},"Update July 28, 2025","2025-07-28",[17],"## What have I been up to?\n\nWhoops! I missed this month's update by a *long* shot, but I still want to get it out there before the end of the month.\n\nThis month was busy busy. I released [v0.6.0 of miracle-wm](https://github.com/miracle-wm-org/miracle-wm/releases/tag/v0.6.0) which adds a bunch of new features, go and check it out if you haven't already! I am nearly finished with the sway/i3 IPC support and have big plans to wrap that up before the middle of August. There really isn't much more to go on that front so I might as well close that chapter. On top of that, the more interesting features of miracle that I have planned (e.g. some built-in shell integrations) are motivating me to wrap up the boring parts first.\n\nAside from miracle, I've continued to land a bunch of things in Mir recently around accessibility. The magnifier work is finally done, so go check that out if you use that feature in your day-to-day! I also did a *ton of work* to fix screenshooting on rotated displays. We were ignoring [wl_surface::set_buffer_transform](https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform) in a big way. Now that we're not doing that, your screenshots should look perfect every time!\n\nLast but not least, the team has been making waves implementing multi-window in the Flutter toolkit. That's some really interesting and exciting work, so stay tuned for that if you're a Flutter developer!\n\nThat's all I got! I will make another post shortly, maybe once miracle v0.7.0 is out. Have a great rest of your summer/winter 🪐","src/content/posts/jul_28_2025.md","eb7072bb3c1eff0c",{"html":45,"metadata":46},"\u003Ch2 id=\"what-have-i-been-up-to\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#what-have-i-been-up-to\">#\u003C/a>What have I been up to?\u003C/h2>\n\u003Cp>Whoops! I missed this month’s update by a \u003Cem>long\u003C/em> shot, but I still want to get it out there before the end of the month.\u003C/p>\n\u003Cp>This month was busy busy. I released \u003Ca href=\"https://github.com/miracle-wm-org/miracle-wm/releases/tag/v0.6.0\">v0.6.0 of miracle-wm\u003C/a> which adds a bunch of new features, go and check it out if you haven’t already! I am nearly finished with the sway/i3 IPC support and have big plans to wrap that up before the middle of August. There really isn’t much more to go on that front so I might as well close that chapter. On top of that, the more interesting features of miracle that I have planned (e.g. some built-in shell integrations) are motivating me to wrap up the boring parts first.\u003C/p>\n\u003Cp>Aside from miracle, I’ve continued to land a bunch of things in Mir recently around accessibility. The magnifier work is finally done, so go check that out if you use that feature in your day-to-day! I also did a \u003Cem>ton of work\u003C/em> to fix screenshooting on rotated displays. We were ignoring \u003Ca href=\"https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform\">wl_surface::set_buffer_transform\u003C/a> in a big way. Now that we’re not doing that, your screenshots should look perfect every time!\u003C/p>\n\u003Cp>Last but not least, the team has been making waves implementing multi-window in the Flutter toolkit. That’s some really interesting and exciting work, so stay tuned for that if you’re a Flutter developer!\u003C/p>\n\u003Cp>That’s all I got! I will make another post shortly, maybe once miracle v0.7.0 is out. Have a great rest of your summer/winter 🪐\u003C/p>",{"headings":47,"localImagePaths":49,"remoteImagePaths":50,"frontmatter":51,"imagePaths":53},[48],{"depth":26,"slug":27,"text":28},[],[],{"title":38,"date":39,"tags":52},[17],[],"jul_28_2025.md","miracle_plugins",{"id":55,"data":57,"body":64,"filePath":65,"digest":66,"rendered":67,"legacyId":101},{"title":58,"date":59,"tags":60},"Creating a WebAssembly Plugin System for Window Management (and beyond) in Miracle","2026-03-24",[61,62,63],"tech","wayland","miracle","Nothing quite makes you feel like you're using an exciting technology like a\nplugin system. In fact, I would argue that this is one of the greatest draws of\nthe web today, at least from a business perspective.\nBrowsers provide a sandbox that runs JavaScript, HTML, CSS, and more. Developers\nbuild apps using these technologies that the browser downloads and\nexecutes safely. The entire program is delivered to the end-user on demand\nwithin the confines of the browser.\n\nExtensibility is a large part of what makes the modern web useful for businesses\nand tinkerers alike. In my opinion, it is also a large part of what makes\nsoftware exciting in general, as it provides a point of entry for hobbyists to\ncraft a system to their liking.\n\n## The State of Pluggable Window Management in Wayland\n\nEver since the transition from X11 to Wayland, the Linux desktop has lost the\nability to make window management pluggable. Different\nwindow managers and desktops have explored ways of accomplishing this, each with pros and\ncons. Hyprland has a [plugin system](https://hypr.land/plugins/) that\ndynamically loads and executes `.so` files. GNOME has an [extension\nsystem](https://extensions.gnome.org/)\nwhere users write extensions in JavaScript. River has introduced a [new\nWayland protocol](https://isaacfreund.com/blog/river-window-management/) for\nwindow management. Lots of ideas are floating around, but this is definitely\na place that is still being explored 🔭.\n\nIn my opinion, the ideal version of pluggable window management in Wayland would have\nthe following properties:\n\n- **Lightweight runtime**: running plugins does not slow down the system\n- **Secure**: plugins are only provided with the minimal amount of information that\n they need to do something useful\n- **Synchronous**: async makes everything harder\n- **Thread safe**: having to reason about thread safety makes everything hard\n- **Language agnostic**: being tied to some weird scripting language makes everyone sad\n- **Window manager agnostic**: it would be nice if the plugin worked across compositors\n\nWithout going into too much detail, each of the solutions mentioned previously\nmakes some tradeoff between these considerations. You can read more\nabout what I view as the pros and cons of each solution [below](#ps-survey-of-other-approaches).\n\n## Miracle's WebAssembly Plugin System\n\nMiracle's new plugin system is based on \u003Cu>WebAssembly\u003C/u>. WebAssembly is\na lightweight, bytecode language that can be embedded in multiple contexts.\nWhile it began in the web, it is truly an agnostic runtime that has utility\neverywhere from embedded devices, to browsers, and now desktops.\n\nBy using WebAssembly, we've created a plugin environment that very nearly meets\nall of the criteria of my ideal plugin environment, minus the \"window manager\nagnostic\" property. As I said previously, every solution will have some tradeoff 😅.\n\nHere's how it all works. Miracle runs a WebAssembly bytecode engine inside of it\n(we're using [WasmEdge](https://wasmedge.org/) specifically). At runtime, Miracle loads the\nplugins (`.wasm` files) that were specified by the user. Miracle then looks for\nspecial function signatures exposed by the WebAssembly bytecode and calls them\nat the appropriate times. While running, the plugin may access information about Miracle\nvia thread-safe methods that Miracle explicitly exposes to the plugin. Additionally,\nplugins can use [WASI](https://wasi.dev/) to access standard services from the\nhost. In this way, plugins run confined in the WebAssembly bytecode engine with\njust the level of access that they require. And because WebAssembly is just\nbytecode, the host can execute and run it with near-native speed.\n\nPlugin authors can write plugins for Miracle in any language that\nsupports WebAssembly as a compilation target. The Miracle project will support an idiomatic\nRust crate for writing plugins for Miracle, which you can find\nhere: https://docs.miracle-wm.org/miracle_plugin/ 🦀. This crate abstracts away\nthe tricky communication-layer between Miracle and the WebAssembly plugin. If plugin\nauthors wish to write a plugin in another language, they can use the Rust implementation\nas a reference.\n\nThe plugin interface has a pleasant design. Whenever an event happens on a\nMiracle object, the plugin is prompted to take some action on an event. For example, here\nis a handful of events that the plugin can handle:\n\n- Placing, resizing, or transforming a window\n- Focusing a window\n- Requesting a workspace\n- Handling a keyboard input event\n- Handling a pointer input event\n- Handling an animation update (window opening, window closing, window\n moving, workspace switching, etc.)\n- and more!\n\nIf the plugin does not want to handle the event, they can simply not do so and let\nMiracle (or the next plugin) handle it instead.\n\nPlugins will also be notified when an event happens, such as:\n\n- Window focused\n- Window unfocused\n- Window deleted\n- Workspace created\n- Workspace focused\n- Workspace deleted\n- and more!\n\nThe plugin need not respond to any of these events, but they could take some action if they choose\nto. For example, a plugin may focus a window on the appropriate workspace when the workspace changes.\n\nIf you're interested in checking out an example usage of this API,\nthe best thus far is my [niri](https://github.com/niri-wm/niri) clone, aptly\nnamed [miri](https://github.com/miracle-wm-org/miri-plugin). While this is in no\nway a full Niri clone (yet!), it demonstrates the core semantics of the API, and\njust how easy it is to use. Here is a little snippet of the window\nmanagement that goes on in Miri, with a bunch of details commented out:\n\n```rust\n// ...\n\nimpl Plugin for Miri {\n fn place_new_window(&mut self, info: &WindowInfo) -> Option\u003CPlacement> {\n if info.window_type != WindowType::Normal && info.window_type != WindowType::Freestyle {\n return None;\n }\n\n if info.state == WindowState::Attached {\n return None;\n }\n\n // ... commented out implementation details...\n\n Some(Placement::Freestyle(FreestylePlacement {\n top_left: Point::new(rect.x, rect.y),\n depth_layer: DepthLayer::Application,\n workspace: None,\n size: Size::new(rect.width, rect.height),\n transform: Mat4::IDENTITY,\n alpha: 1.0,\n movable: false,\n resizable: false,\n }))\n }\n\n fn window_deleted(&mut self, info: &WindowInfo) {\n // .. remove the window and scroll to a new window if its in focus ...\n }\n\n fn window_focused(&mut self, info: &WindowInfo) {\n // ... scroll the new window into focus ...\n }\n\n fn workspace_created(&mut self, workspace: &Workspace) {\n // ... set the workspace up with empty data ...\n }\n\n fn workspace_removed(&mut self, workspace: &Workspace) {\n // ... re-home windows on that workspace ...\n }\n\n fn workspace_focused(&mut self, _previous_id: Option\u003Cu64>, current: &Workspace) {\n // ... see if we should focus another window ...\n }\n\n fn workspace_area_changed(&mut self, workspace: &Workspace) {\n // ... change the size of existing windows ...\n }\n\n fn handle_keyboard_input(&mut self, event: KeyboardEvent) -> bool {\n // ... handle Niri-specific keyboard inputs ...\n }\n\n // ... and much more...!\n}\n\n```\n\nMiri will continue to be developed over the coming months 😄.\n\nBut Miracle doesn't just offer window management via its plugin system.\nOne of the things that I always dislike about window managers is that I have to\nconfigure them using some clunky file format. This format is good up to a point,\nbut it soon happens that my configuration is quite complex. What I _really_ want\nin this situation is a \u003Cu>real\u003C/u> programming language. So that's just what I\nadded to Miracle! Instead of configuring your window manager in YAML, you can\nnow configure it in Rust 🦀. Here is a real example from my [dotfiles](https://github.com/mattkae/dotfiles/blob/master/config/miracle-wm/matts-config/src/lib.rs):\n\n```rust\nimpl Plugin for MyPlugin {\n fn configure(&mut self) -> Option\u003CConfiguration> {\n let mut config: Configuration = Configuration::default();\n\n config.primary_modifier = Some(miracle_plugin::Modifier::Meta);\n\n let mut custom_actions: Vec\u003CCustomKeyAction> = vec![];\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"d\".to_string()),\n modifiers: vec![Modifier::Primary],\n command: \"wofi --show=drun\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"S\".to_string()),\n modifiers: vec![Modifier::Primary],\n command: \"grimshot copy area\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"L\".to_string()),\n modifiers: vec![Modifier::Primary],\n command: \"swaylock\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"XF86AudioLowerVolume\".to_string()),\n modifiers: vec![],\n command: \"pamixer -d 10\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"XF86AudioRaiseVolume\".to_string()),\n modifiers: vec![],\n command: \"pamixer -i 10\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"XF86AudioMute\".to_string()),\n modifiers: vec![],\n command: \"pamixer -t\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"XF86MonBrightnessDown\".to_string()),\n modifiers: vec![],\n command: \"brightnessctl s 100-\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"XF86MonBrightnessUp\".to_string()),\n modifiers: vec![],\n command: \"brightnessctl s +100\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"E\".to_string()),\n modifiers: vec![Modifier::Primary],\n command: \"wlogout --protocol layer-shell\".to_string(),\n });\n custom_actions.push(CustomKeyAction {\n action: miracle_plugin::BindingAction::Down,\n key: Key(\"M\".to_string()),\n modifiers: vec![Modifier::Primary],\n command: \"miraclemsg workspace music\".to_string(),\n });\n\n config.custom_key_actions = Some(custom_actions);\n config.inner_gaps = Some(Gaps { x: 16, y: 16 });\n config.outer_gaps = Some(Gaps { x: 8, y: 8 });\n\n let mut startup_apps: Vec\u003CStartupApp> = vec![];\n startup_apps.push(StartupApp {\n command: \"waybar\".to_string(),\n restart_on_death: false,\n no_startup_id: false,\n should_halt_compositor_on_death: false,\n in_systemd_scope: false,\n });\n startup_apps.push(StartupApp {\n command: \"~/.local/bin/launch-swaybg.sh\".to_string(),\n restart_on_death: false,\n no_startup_id: false,\n should_halt_compositor_on_death: false,\n in_systemd_scope: false,\n });\n startup_apps.push(StartupApp {\n command: \"swaync\".to_string(),\n restart_on_death: false,\n no_startup_id: false,\n should_halt_compositor_on_death: false,\n in_systemd_scope: false,\n });\n config.startup_apps = Some(startup_apps);\n\n config.terminal = Some(\"kitty\".to_string());\n\n config.resize_jump = Some(50);\n config.border = Some(BorderConfig {\n size: 2,\n radius: 4.0,\n color: \"0xbd93f9ff\".to_string(),\n focus_color: \"0x50fa7bff\".to_string(),\n });\n\n Some(config)\n }\n}\n```\n\nI love using a real programming language to configure. It makes me happy.\n\nAll this being said, I think that the WebAssembly solution provides a solid\nmiddleground for all of my ideals:\n\n- ✅ **Lightweight runtime**: WebAssembly is lightweight by design\n- ✅ **Secure**: plugins are given only what they need from Miracle over the interface\n- ✅ **Synchronous**: Miracle directly calls the plugin functions when it needs to\n- ✅ **Thread safe**: Miracle guarantees thread-safe execution\n- ✅ **Language agnostic**: WebAssembly is a compilation target for many languages (especially Rust)\n- ⛔ **Window manager agnostic**: this is super-duper Miracle specific\n\nI am excited about the work that is being done in this area right now, especially\nRiver's implementation. Perhaps one day I will implement it myself in Miracle, but\nI plan to focus mostly on Miracle's WASM plugin system in the near future.\n\n## What's next?\n\nThe plugin system will be released in Miracle 0.9.0, which should be published later this week.\nMiri will receive an official version afterward. I am excited to see what you all\nbuild with the new API. I will be around to offer support for the plugins that you build\non the [matrix channel](https://matrix.to/#/#miracle-wm:matrix.org).\n\nAs you may be well aware, window management is only half the part of the desktop\nstory. The other part is the \u003Cu>shell\u003C/u>, which includes bars,\nlaunchers, and a whole bunch of other stuff. My idea for this is to let Wayland clients\nspeak with plugins over the `MIRACLESOCK` (i.e. `SWAYSOCK`)\nthat Mir already exposes to clients like `waybar`. In this way, the window manager\nwill be able to tell clients about events so that the shell can update accordingly.\nThis bit is yet to be designed yet.\n\nMiracle is turning into so much more than just the tiling window manager that I originally\ndesigned it to be. The plugin system is a culmination of a lot of my thinking over the past\ncouple years, and I am very excited to get it into people's hands. Happy coding!\n\n## Resources\n\n- https://github.com/miracle-wm-org/miracle-wm - miracle-wm\n- https://docs.miracle-wm.org/miracle_plugin/ - Miracle's plugin API\n- https://github.com/miracle-wm-org/miri-plugin - Miri plugin for Miracle\n- https://github.com/mattkae/dotfiles - My dotfiles with my custom plugin\n\n## P.S.: Survey of Other Approaches\n\nBelow are my thoughts weighing the pros and cons of existing solutions.\nConsidering each of them guided much of my thinking as a designed Miracle's\nsystem, so it is worthwhile noting them here. I still think that River's\nsolution is the best of the existing bunch, if only for its interest in being\ncross-compositor.\n\n### Dynamically Loading Shared Libraries\n\nThis approach has been taken by Hyprland.\n\n**Pros**:\n\n- Lightweight runtime\\*: the plugin code runs the same as the native code.\n- _Maximal control_: the plugin can do whatever is allowed on the system,\n including drawing complex graphics, reaching out to the internet, and more.\n- _Common interface_: plugins are presumably programmed against the same\n interface as the rest of the program\n\n**Cons**:\n\n- _Security_: you must trust the plugin author as much as you trust the author\n of the host program\n- _Language Lock-In_: plugins must typically use the language supported by the\n API, although it is feasible for other bindings to be written on top of this.\n\n### Scripting Language\n\nThis approach is common to GNOME and KDE, and is usually accomplished with\nJavaScript, Lua, or some other scripting language embedded in the desktop.\n\n**Pros**:\n\n- _Security_: theoretically, the JavaScript can be sandboxed such that it has\n minimal access ot the rest of the program.\n- _Ease of use_: the general audience typically has a lot of exposure to\n JavaScript and othe scripting languages, making it easy\n\n**Cons**:\n\n- _Language Lock-In_: it is typically infeasible to author plugins in a language\n that is not provided by the host program, unless you're willing to do a LOT of work.\n- _Heavy Runtime_: the host must ship an entire interpreter and runtime for the\n given scripting language inside of it.\n\n### Wayland Protocol\n\nThis is the approach recently suggested by the River project, which I find very\ninteresting.\n\n**Pros**:\n\n- _Window Manager Agnostic_: any window manager can implement the protocol,\n making it a true global solution.\n- _Language Agnostic_: Wayland clients can be written in any language, meaning\n that window manager can too!\n- _Security_: the window manager client has the same permissions as any othe\n client running on the system, while remaining outside of the priveleged\n compositor, who knows about things like input events, PIDs, etc.\n- _Medium control_: the compositor can access most of what it is interested in,\n minus the secret bits\n\n**Cons**:\n\n- _Async_: the Wayland protocol is asynchronous by nature, making frame-perfect\n management difficult (although the author of River is doing a great job here).\n\nI actually think this is the best solution of the bunch thus far.","src/content/posts/miracle_plugins.md","fa66532eb2153a58",{"html":68,"metadata":69},"\u003Cp>Nothing quite makes you feel like you’re using an exciting technology like a\nplugin system. In fact, I would argue that this is one of the greatest draws of\nthe web today, at least from a business perspective.\nBrowsers provide a sandbox that runs JavaScript, HTML, CSS, and more. Developers\nbuild apps using these technologies that the browser downloads and\nexecutes safely. The entire program is delivered to the end-user on demand\nwithin the confines of the browser.\u003C/p>\n\u003Cp>Extensibility is a large part of what makes the modern web useful for businesses\nand tinkerers alike. In my opinion, it is also a large part of what makes\nsoftware exciting in general, as it provides a point of entry for hobbyists to\ncraft a system to their liking.\u003C/p>\n\u003Ch2 id=\"the-state-of-pluggable-window-management-in-wayland\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#the-state-of-pluggable-window-management-in-wayland\">#\u003C/a>The State of Pluggable Window Management in Wayland\u003C/h2>\n\u003Cp>Ever since the transition from X11 to Wayland, the Linux desktop has lost the\nability to make window management pluggable. Different\nwindow managers and desktops have explored ways of accomplishing this, each with pros and\ncons. Hyprland has a \u003Ca href=\"https://hypr.land/plugins/\">plugin system\u003C/a> that\ndynamically loads and executes \u003Ccode>.so\u003C/code> files. GNOME has an \u003Ca href=\"https://extensions.gnome.org/\">extension\nsystem\u003C/a>\nwhere users write extensions in JavaScript. River has introduced a \u003Ca href=\"https://isaacfreund.com/blog/river-window-management/\">new\nWayland protocol\u003C/a> for\nwindow management. Lots of ideas are floating around, but this is definitely\na place that is still being explored 🔭.\u003C/p>\n\u003Cp>In my opinion, the ideal version of pluggable window management in Wayland would have\nthe following properties:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Lightweight runtime\u003C/strong>: running plugins does not slow down the system\u003C/li>\n\u003Cli>\u003Cstrong>Secure\u003C/strong>: plugins are only provided with the minimal amount of information that\nthey need to do something useful\u003C/li>\n\u003Cli>\u003Cstrong>Synchronous\u003C/strong>: async makes everything harder\u003C/li>\n\u003Cli>\u003Cstrong>Thread safe\u003C/strong>: having to reason about thread safety makes everything hard\u003C/li>\n\u003Cli>\u003Cstrong>Language agnostic\u003C/strong>: being tied to some weird scripting language makes everyone sad\u003C/li>\n\u003Cli>\u003Cstrong>Window manager agnostic\u003C/strong>: it would be nice if the plugin worked across compositors\u003C/li>\n\u003C/ul>\n\u003Cp>Without going into too much detail, each of the solutions mentioned previously\nmakes some tradeoff between these considerations. You can read more\nabout what I view as the pros and cons of each solution \u003Ca href=\"#ps-survey-of-other-approaches\">below\u003C/a>.\u003C/p>\n\u003Ch2 id=\"miracles-webassembly-plugin-system\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#miracles-webassembly-plugin-system\">#\u003C/a>Miracle’s WebAssembly Plugin System\u003C/h2>\n\u003Cp>Miracle’s new plugin system is based on \u003Cu>WebAssembly\u003C/u>. WebAssembly is\na lightweight, bytecode language that can be embedded in multiple contexts.\nWhile it began in the web, it is truly an agnostic runtime that has utility\neverywhere from embedded devices, to browsers, and now desktops.\u003C/p>\n\u003Cp>By using WebAssembly, we’ve created a plugin environment that very nearly meets\nall of the criteria of my ideal plugin environment, minus the “window manager\nagnostic” property. As I said previously, every solution will have some tradeoff 😅.\u003C/p>\n\u003Cp>Here’s how it all works. Miracle runs a WebAssembly bytecode engine inside of it\n(we’re using \u003Ca href=\"https://wasmedge.org/\">WasmEdge\u003C/a> specifically). At runtime, Miracle loads the\nplugins (\u003Ccode>.wasm\u003C/code> files) that were specified by the user. Miracle then looks for\nspecial function signatures exposed by the WebAssembly bytecode and calls them\nat the appropriate times. While running, the plugin may access information about Miracle\nvia thread-safe methods that Miracle explicitly exposes to the plugin. Additionally,\nplugins can use \u003Ca href=\"https://wasi.dev/\">WASI\u003C/a> to access standard services from the\nhost. In this way, plugins run confined in the WebAssembly bytecode engine with\njust the level of access that they require. And because WebAssembly is just\nbytecode, the host can execute and run it with near-native speed.\u003C/p>\n\u003Cp>Plugin authors can write plugins for Miracle in any language that\nsupports WebAssembly as a compilation target. The Miracle project will support an idiomatic\nRust crate for writing plugins for Miracle, which you can find\nhere: \u003Ca href=\"https://docs.miracle-wm.org/miracle_plugin/\">https://docs.miracle-wm.org/miracle_plugin/\u003C/a> 🦀. This crate abstracts away\nthe tricky communication-layer between Miracle and the WebAssembly plugin. If plugin\nauthors wish to write a plugin in another language, they can use the Rust implementation\nas a reference.\u003C/p>\n\u003Cp>The plugin interface has a pleasant design. Whenever an event happens on a\nMiracle object, the plugin is prompted to take some action on an event. For example, here\nis a handful of events that the plugin can handle:\u003C/p>\n\u003Cul>\n\u003Cli>Placing, resizing, or transforming a window\u003C/li>\n\u003Cli>Focusing a window\u003C/li>\n\u003Cli>Requesting a workspace\u003C/li>\n\u003Cli>Handling a keyboard input event\u003C/li>\n\u003Cli>Handling a pointer input event\u003C/li>\n\u003Cli>Handling an animation update (window opening, window closing, window\nmoving, workspace switching, etc.)\u003C/li>\n\u003Cli>and more!\u003C/li>\n\u003C/ul>\n\u003Cp>If the plugin does not want to handle the event, they can simply not do so and let\nMiracle (or the next plugin) handle it instead.\u003C/p>\n\u003Cp>Plugins will also be notified when an event happens, such as:\u003C/p>\n\u003Cul>\n\u003Cli>Window focused\u003C/li>\n\u003Cli>Window unfocused\u003C/li>\n\u003Cli>Window deleted\u003C/li>\n\u003Cli>Workspace created\u003C/li>\n\u003Cli>Workspace focused\u003C/li>\n\u003Cli>Workspace deleted\u003C/li>\n\u003Cli>and more!\u003C/li>\n\u003C/ul>\n\u003Cp>The plugin need not respond to any of these events, but they could take some action if they choose\nto. For example, a plugin may focus a window on the appropriate workspace when the workspace changes.\u003C/p>\n\u003Cp>If you’re interested in checking out an example usage of this API,\nthe best thus far is my \u003Ca href=\"https://github.com/niri-wm/niri\">niri\u003C/a> clone, aptly\nnamed \u003Ca href=\"https://github.com/miracle-wm-org/miri-plugin\">miri\u003C/a>. While this is in no\nway a full Niri clone (yet!), it demonstrates the core semantics of the API, and\njust how easy it is to use. Here is a little snippet of the window\nmanagement that goes on in Miri, with a bunch of details commented out:\u003C/p>\n\u003Cpre class=\"astro-code astro-code-themes github-light dracula\" style=\"--shiki-light:#24292e;--shiki-dark:#F8F8F2;--shiki-light-bg:#fff;--shiki-dark-bg:#282A36; overflow-x: auto;\" tabindex=\"0\" data-language=\"rust\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\">// ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">impl\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> for\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Miri\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> place_new_window\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, info\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">WindowInfo\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">->\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Option\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"><\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Placement\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> if\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> info\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">window_type \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">!=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> WindowType\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Normal\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &&\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> info\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">window_type \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">!=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> WindowType\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Freestyle\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> return\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> None\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> if\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> info\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">state \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">==\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> WindowState\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Attached\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> return\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> None\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... commented out implementation details...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Placement\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">Freestyle\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">FreestylePlacement\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> top_left\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> Point\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">new\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(rect\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">x, rect\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">y),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> depth_layer\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> DepthLayer\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Application\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> workspace\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> None\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> size\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> Size\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">new\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(rect\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">width, rect\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">height),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> transform\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> Mat4\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">IDENTITY\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> alpha\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 1.0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> movable\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> resizable\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }))\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> window_deleted\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, info\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">WindowInfo\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // .. remove the window and scroll to a new window if its in focus ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> window_focused\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, info\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">WindowInfo\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... scroll the new window into focus ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> workspace_created\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, workspace\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Workspace\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... set the workspace up with empty data ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> workspace_removed\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, workspace\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Workspace\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... re-home windows on that workspace ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> workspace_focused\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, _previous_id\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Option\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"><\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">u64\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">>, current\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Workspace\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... see if we should focus another window ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> workspace_area_changed\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, workspace\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> &\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Workspace\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... change the size of existing windows ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> handle_keyboard_input\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, event\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> KeyboardEvent\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">->\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> bool\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... handle Niri-specific keyboard inputs ...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6272A4\"> // ... and much more...!\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Miri will continue to be developed over the coming months 😄.\u003C/p>\n\u003Cp>But Miracle doesn’t just offer window management via its plugin system.\nOne of the things that I always dislike about window managers is that I have to\nconfigure them using some clunky file format. This format is good up to a point,\nbut it soon happens that my configuration is quite complex. What I \u003Cem>really\u003C/em> want\nin this situation is a \u003Cu>real\u003C/u> programming language. So that’s just what I\nadded to Miracle! Instead of configuring your window manager in YAML, you can\nnow configure it in Rust 🦀. Here is a real example from my \u003Ca href=\"https://github.com/mattkae/dotfiles/blob/master/config/miracle-wm/matts-config/src/lib.rs\">dotfiles\u003C/a>:\u003C/p>\n\u003Cpre class=\"astro-code astro-code-themes github-light dracula\" style=\"--shiki-light:#24292e;--shiki-dark:#F8F8F2;--shiki-light-bg:#fff;--shiki-dark-bg:#282A36; overflow-x: auto;\" tabindex=\"0\" data-language=\"rust\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">impl\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> for\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MyPlugin\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> configure\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">&mut\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-light-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic\"> self\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">->\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Option\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"><\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Configuration\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> mut\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Configuration\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Configuration\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">default\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">primary_modifier \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Modifier\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Meta\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> mut\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Vec\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"><\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">> \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"d\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">Modifier\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Primary\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"wofi --show=drun\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"S\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">Modifier\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Primary\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"grimshot copy area\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"L\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">Modifier\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Primary\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"swaylock\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"XF86AudioLowerVolume\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"pamixer -d 10\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"XF86AudioRaiseVolume\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"pamixer -i 10\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"XF86AudioMute\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"pamixer -t\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"XF86MonBrightnessDown\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"brightnessctl s 100-\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"XF86MonBrightnessUp\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"brightnessctl s +100\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"E\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">Modifier\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Primary\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"wlogout --protocol layer-shell\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> custom_actions\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">CustomKeyAction\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> action\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\"> miracle_plugin\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">BindingAction\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Down\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> Key\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"M\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">()),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> modifiers\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#F8F8F2\">Modifier\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">::\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Primary\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"miraclemsg workspace music\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">custom_key_actions \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(custom_actions);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">inner_gaps \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Gaps\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> { x\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 16\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, y\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 16\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">outer_gaps \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">Gaps\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> { x\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 8\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">, y\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 8\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> mut\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> startup_apps\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Vec\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"><\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">StartupApp\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">> \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> vec!\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">[];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> startup_apps\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">StartupApp\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"waybar\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> restart_on_death\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> no_startup_id\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> should_halt_compositor_on_death\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> in_systemd_scope\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> startup_apps\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">StartupApp\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"~/.local/bin/launch-swaybg.sh\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> restart_on_death\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> no_startup_id\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> should_halt_compositor_on_death\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> in_systemd_scope\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> startup_apps\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">push\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">StartupApp\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> command\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"swaync\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> restart_on_death\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> no_startup_id\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> should_halt_compositor_on_death\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> in_systemd_scope\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">startup_apps \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(startup_apps);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">terminal \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\">\"kitty\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">());\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">resize_jump \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">50\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> config\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">border \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\">BorderConfig\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> size\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 2\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> radius\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 4.0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> color\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"0xbd93f9ff\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> focus_color\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"0x50fa7bff\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\">to_string\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> Some\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(config)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>I love using a real programming language to configure. It makes me happy.\u003C/p>\n\u003Cp>All this being said, I think that the WebAssembly solution provides a solid\nmiddleground for all of my ideals:\u003C/p>\n\u003Cul>\n\u003Cli>✅ \u003Cstrong>Lightweight runtime\u003C/strong>: WebAssembly is lightweight by design\u003C/li>\n\u003Cli>✅ \u003Cstrong>Secure\u003C/strong>: plugins are given only what they need from Miracle over the interface\u003C/li>\n\u003Cli>✅ \u003Cstrong>Synchronous\u003C/strong>: Miracle directly calls the plugin functions when it needs to\u003C/li>\n\u003Cli>✅ \u003Cstrong>Thread safe\u003C/strong>: Miracle guarantees thread-safe execution\u003C/li>\n\u003Cli>✅ \u003Cstrong>Language agnostic\u003C/strong>: WebAssembly is a compilation target for many languages (especially Rust)\u003C/li>\n\u003Cli>⛔ \u003Cstrong>Window manager agnostic\u003C/strong>: this is super-duper Miracle specific\u003C/li>\n\u003C/ul>\n\u003Cp>I am excited about the work that is being done in this area right now, especially\nRiver’s implementation. Perhaps one day I will implement it myself in Miracle, but\nI plan to focus mostly on Miracle’s WASM plugin system in the near future.\u003C/p>\n\u003Ch2 id=\"whats-next\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#whats-next\">#\u003C/a>What’s next?\u003C/h2>\n\u003Cp>The plugin system will be released in Miracle 0.9.0, which should be published later this week.\nMiri will receive an official version afterward. I am excited to see what you all\nbuild with the new API. I will be around to offer support for the plugins that you build\non the \u003Ca href=\"https://matrix.to/#/#miracle-wm:matrix.org\">matrix channel\u003C/a>.\u003C/p>\n\u003Cp>As you may be well aware, window management is only half the part of the desktop\nstory. The other part is the \u003Cu>shell\u003C/u>, which includes bars,\nlaunchers, and a whole bunch of other stuff. My idea for this is to let Wayland clients\nspeak with plugins over the \u003Ccode>MIRACLESOCK\u003C/code> (i.e. \u003Ccode>SWAYSOCK\u003C/code>)\nthat Mir already exposes to clients like \u003Ccode>waybar\u003C/code>. In this way, the window manager\nwill be able to tell clients about events so that the shell can update accordingly.\nThis bit is yet to be designed yet.\u003C/p>\n\u003Cp>Miracle is turning into so much more than just the tiling window manager that I originally\ndesigned it to be. The plugin system is a culmination of a lot of my thinking over the past\ncouple years, and I am very excited to get it into people’s hands. Happy coding!\u003C/p>\n\u003Ch2 id=\"resources\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#resources\">#\u003C/a>Resources\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Ca href=\"https://github.com/miracle-wm-org/miracle-wm\">https://github.com/miracle-wm-org/miracle-wm\u003C/a> - miracle-wm\u003C/li>\n\u003Cli>\u003Ca href=\"https://docs.miracle-wm.org/miracle_plugin/\">https://docs.miracle-wm.org/miracle_plugin/\u003C/a> - Miracle’s plugin API\u003C/li>\n\u003Cli>\u003Ca href=\"https://github.com/miracle-wm-org/miri-plugin\">https://github.com/miracle-wm-org/miri-plugin\u003C/a> - Miri plugin for Miracle\u003C/li>\n\u003Cli>\u003Ca href=\"https://github.com/mattkae/dotfiles\">https://github.com/mattkae/dotfiles\u003C/a> - My dotfiles with my custom plugin\u003C/li>\n\u003C/ul>\n\u003Ch2 id=\"ps-survey-of-other-approaches\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#ps-survey-of-other-approaches\">#\u003C/a>P.S.: Survey of Other Approaches\u003C/h2>\n\u003Cp>Below are my thoughts weighing the pros and cons of existing solutions.\nConsidering each of them guided much of my thinking as a designed Miracle’s\nsystem, so it is worthwhile noting them here. I still think that River’s\nsolution is the best of the existing bunch, if only for its interest in being\ncross-compositor.\u003C/p>\n\u003Ch3 id=\"dynamically-loading-shared-libraries\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#dynamically-loading-shared-libraries\">#\u003C/a>Dynamically Loading Shared Libraries\u003C/h3>\n\u003Cp>This approach has been taken by Hyprland.\u003C/p>\n\u003Cp>\u003Cstrong>Pros\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>Lightweight runtime*: the plugin code runs the same as the native code.\u003C/li>\n\u003Cli>\u003Cem>Maximal control\u003C/em>: the plugin can do whatever is allowed on the system,\nincluding drawing complex graphics, reaching out to the internet, and more.\u003C/li>\n\u003Cli>\u003Cem>Common interface\u003C/em>: plugins are presumably programmed against the same\ninterface as the rest of the program\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Cons\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cem>Security\u003C/em>: you must trust the plugin author as much as you trust the author\nof the host program\u003C/li>\n\u003Cli>\u003Cem>Language Lock-In\u003C/em>: plugins must typically use the language supported by the\nAPI, although it is feasible for other bindings to be written on top of this.\u003C/li>\n\u003C/ul>\n\u003Ch3 id=\"scripting-language\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#scripting-language\">#\u003C/a>Scripting Language\u003C/h3>\n\u003Cp>This approach is common to GNOME and KDE, and is usually accomplished with\nJavaScript, Lua, or some other scripting language embedded in the desktop.\u003C/p>\n\u003Cp>\u003Cstrong>Pros\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cem>Security\u003C/em>: theoretically, the JavaScript can be sandboxed such that it has\nminimal access ot the rest of the program.\u003C/li>\n\u003Cli>\u003Cem>Ease of use\u003C/em>: the general audience typically has a lot of exposure to\nJavaScript and othe scripting languages, making it easy\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Cons\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cem>Language Lock-In\u003C/em>: it is typically infeasible to author plugins in a language\nthat is not provided by the host program, unless you’re willing to do a LOT of work.\u003C/li>\n\u003Cli>\u003Cem>Heavy Runtime\u003C/em>: the host must ship an entire interpreter and runtime for the\ngiven scripting language inside of it.\u003C/li>\n\u003C/ul>\n\u003Ch3 id=\"wayland-protocol\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#wayland-protocol\">#\u003C/a>Wayland Protocol\u003C/h3>\n\u003Cp>This is the approach recently suggested by the River project, which I find very\ninteresting.\u003C/p>\n\u003Cp>\u003Cstrong>Pros\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cem>Window Manager Agnostic\u003C/em>: any window manager can implement the protocol,\nmaking it a true global solution.\u003C/li>\n\u003Cli>\u003Cem>Language Agnostic\u003C/em>: Wayland clients can be written in any language, meaning\nthat window manager can too!\u003C/li>\n\u003Cli>\u003Cem>Security\u003C/em>: the window manager client has the same permissions as any othe\nclient running on the system, while remaining outside of the priveleged\ncompositor, who knows about things like input events, PIDs, etc.\u003C/li>\n\u003Cli>\u003Cem>Medium control\u003C/em>: the compositor can access most of what it is interested in,\nminus the secret bits\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Cons\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cem>Async\u003C/em>: the Wayland protocol is asynchronous by nature, making frame-perfect\nmanagement difficult (although the author of River is doing a great job here).\u003C/li>\n\u003C/ul>\n\u003Cp>I actually think this is the best solution of the bunch thus far.\u003C/p>",{"headings":70,"localImagePaths":96,"remoteImagePaths":97,"frontmatter":98,"imagePaths":100},[71,74,77,80,83,86,90,93],{"depth":26,"slug":72,"text":73},"the-state-of-pluggable-window-management-in-wayland","#The State of Pluggable Window Management in Wayland",{"depth":26,"slug":75,"text":76},"miracles-webassembly-plugin-system","#Miracle’s WebAssembly Plugin System",{"depth":26,"slug":78,"text":79},"whats-next","#What’s next?",{"depth":26,"slug":81,"text":82},"resources","#Resources",{"depth":26,"slug":84,"text":85},"ps-survey-of-other-approaches","#P.S.: Survey of Other Approaches",{"depth":87,"slug":88,"text":89},3,"dynamically-loading-shared-libraries","#Dynamically Loading Shared Libraries",{"depth":87,"slug":91,"text":92},"scripting-language","#Scripting Language",{"depth":87,"slug":94,"text":95},"wayland-protocol","#Wayland Protocol",[],[],{"title":58,"date":59,"tags":99},[61,62,63],[],"miracle_plugins.md","june_08_2025",{"id":102,"data":104,"body":108,"filePath":109,"digest":110,"rendered":111,"legacyId":121},{"title":105,"date":106,"tags":107},"Update June 08, 2025","2025-06-08",[17],"## What have I been up to?\n\nAnother month has gone by, so I guess it's time to see what I've been up to.\n\nCanonical hosted our company's sprint in Frankfurt, Germany during the month of May. It was a very productive time for the whole team. I always enjoy seeing everyone in person, talking through big issues face-to-face, and exploring a new city. I also got to spend some time in Zurich, Switzerland before the sprint began. Zurich is pretty awesome 🏔️!\n\nIn miracle-wm, I have been going down quite the rabbit-hole 🐇! What began as a plan to fix the remaining warnings in the source code has snowballed into me implementing the rest of the IPC mechanism and testing the entire thing. This has been no small feat, as both sway and i3 implement a *lot* of different commands, and a failure to implement any one of them often leads to half-broken clients. I've had to make some \"executive\" decisions to ignore parts of the protocol that I deem irrelevant now (especially many of the X-specific bits), but it is a mostly compatible implementation. The good news is that I've nearly completed this journey and should be ready to release version `0.6.0` some time in the middle of June.\n\nMiracle is getting closer-and-closer to my vision of it every day. The only problem right now is finding the bandwidth to implement everything that I have in my head :)\n\nOn the Mir side of things, I am still implementing the magnifier glass accessibility feature from before, but with a much-improved technical direction that we arrived at during our time in Frankfurt. Unfortunately, this required a quick detour to properly implement the `overlay_cursor` flag of [zwlr_screencopy_manager_v1::capture_output_region](https://wayland.app/protocols/wlr-screencopy-unstable-v1#zwlr_screencopy_manager_v1:request:capture_output:arg:overlay_cursor), as both screencopy and magnification rely on this same code path. The good news is that I'm quite close on this and it should be landing in full any day now 🤞\n\nI also fixed [this very breaking bug](https://github.com/canonical/mir/pull/3968) that was actively preventing miracle from rendering on my second monitor, so that's good.\n\nIn addition to these two projects, I am also reinvolving myself in the Flutter multi-window work. For those who don't know, we're trying to make it so that the Flutter toolkit can render to multiple surfaces. This is no small feat, as Flutter was originally written with the assumption that only a single \"view\" would ever be drawn to. However we've managed to make some great progress on it thus far, and we're very excited to land the first pull request imminently with the help of the folks over at Google!\n\nI hope you're having a great and productive summer 😎","src/content/posts/june_08_2025.md","50d5a26d5c525d33",{"html":112,"metadata":113},"\u003Ch2 id=\"what-have-i-been-up-to\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#what-have-i-been-up-to\">#\u003C/a>What have I been up to?\u003C/h2>\n\u003Cp>Another month has gone by, so I guess it’s time to see what I’ve been up to.\u003C/p>\n\u003Cp>Canonical hosted our company’s sprint in Frankfurt, Germany during the month of May. It was a very productive time for the whole team. I always enjoy seeing everyone in person, talking through big issues face-to-face, and exploring a new city. I also got to spend some time in Zurich, Switzerland before the sprint began. Zurich is pretty awesome 🏔️!\u003C/p>\n\u003Cp>In miracle-wm, I have been going down quite the rabbit-hole 🐇! What began as a plan to fix the remaining warnings in the source code has snowballed into me implementing the rest of the IPC mechanism and testing the entire thing. This has been no small feat, as both sway and i3 implement a \u003Cem>lot\u003C/em> of different commands, and a failure to implement any one of them often leads to half-broken clients. I’ve had to make some “executive” decisions to ignore parts of the protocol that I deem irrelevant now (especially many of the X-specific bits), but it is a mostly compatible implementation. The good news is that I’ve nearly completed this journey and should be ready to release version \u003Ccode>0.6.0\u003C/code> some time in the middle of June.\u003C/p>\n\u003Cp>Miracle is getting closer-and-closer to my vision of it every day. The only problem right now is finding the bandwidth to implement everything that I have in my head :)\u003C/p>\n\u003Cp>On the Mir side of things, I am still implementing the magnifier glass accessibility feature from before, but with a much-improved technical direction that we arrived at during our time in Frankfurt. Unfortunately, this required a quick detour to properly implement the \u003Ccode>overlay_cursor\u003C/code> flag of \u003Ca href=\"https://wayland.app/protocols/wlr-screencopy-unstable-v1#zwlr_screencopy_manager_v1:request:capture_output:arg:overlay_cursor\">zwlr_screencopy_manager_v1::capture_output_region\u003C/a>, as both screencopy and magnification rely on this same code path. The good news is that I’m quite close on this and it should be landing in full any day now 🤞\u003C/p>\n\u003Cp>I also fixed \u003Ca href=\"https://github.com/canonical/mir/pull/3968\">this very breaking bug\u003C/a> that was actively preventing miracle from rendering on my second monitor, so that’s good.\u003C/p>\n\u003Cp>In addition to these two projects, I am also reinvolving myself in the Flutter multi-window work. For those who don’t know, we’re trying to make it so that the Flutter toolkit can render to multiple surfaces. This is no small feat, as Flutter was originally written with the assumption that only a single “view” would ever be drawn to. However we’ve managed to make some great progress on it thus far, and we’re very excited to land the first pull request imminently with the help of the folks over at Google!\u003C/p>\n\u003Cp>I hope you’re having a great and productive summer 😎\u003C/p>",{"headings":114,"localImagePaths":116,"remoteImagePaths":117,"frontmatter":118,"imagePaths":120},[115],{"depth":26,"slug":27,"text":28},[],[],{"title":105,"date":106,"tags":119},[17],[],"june_08_2025.md","dec_29_2025",{"id":122,"data":124,"body":128,"filePath":129,"digest":130,"rendered":131,"legacyId":147},{"title":125,"date":126,"tags":127},"Update December 29, 2025","2025-12-29",[17],"## What have I been up to?\n\n2025 has been one busy year for me! I feel as though I've been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it's getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical.\n\n## Miracle Update\n\nMiracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too.\n\nOne of the major upcoming features in Miracle is a **plugin system**. Having a plugin system in Miracle will empower 3rd party authors to extend the compositor in ways that I haven't currently imagined while also providing me with the flexibility to iterate quickly on designs. Many compositors already have plugin systems. For example, GNOME does this via JavaScript and Hyprland does this by dynamically loading shared libraries at runtime (note: this is my understanding as of writing this). Both of these solutions are reasonable, but they come with a few downsides.\n\nThe \"true\" scripting language solution comes with the overhead of shipping a complicated interpreter inside of the compositor. In addition to this, plugin developers are forced to use a particular language, perhaps one that they are unfamiliar with. While you get increased programming flexibility from using a scripting language, you have to balance this with the introduced complexity of that language.\n\nThe shared library approach also has its downsides. While dynamically loading a shared library at runtime is lightweight, it prevents users of the compositor from safely running plugins unless they first trust the plugin author. The shared library will be running inside of the same process as your very privileged compositor. This increases the attack surface to an extent that I would feel uncomfortable shipping to users.\n\nFor these reasons, I decided that Miracle plugins will be written in **WebAssembly** with the help of [WasmEdge](https://wasmedge.org). The benefits of this approach are:\n\n1. WebAssembly runs in a lightweight bytecode engine\n2. Many languages can compile down to WebAssembly (Rust will have first-class support to start)\n3. WebAssembly plugins will only be able to access APIs that we provide (reducing the requirement of \"trusting\" the plugin author)\n\nThe WebAssembly modules will implement certain functions by signature. Miracle will search for a particular signature in the module. If that signature is found, Miracle will delegate the function call defined by that signature to the WebAssembly module instead of Miracle's internal implementation. In this way, the WASM plugin will only have the data that it needs to perform an action. It is perfectly isolated from the rest of the process while also running quickly in the bytecode engine. Here is an example of an animation plugin that will linearly fade in a window:\n\n```rust\n#[unsafe(no_mangle)]\npub extern \"C\" fn animate(\n data: MiracleAnimationFrameData,\n) -> MiracleAnimationFrameResult {\n\n let progress = data.runtime_seconds / data.duration_seconds;\n let opacity = data.opacity_start + (data.opacity_end - data.opacity_start) * progress;\n MiracleAnimationFrameResult {\n completed: 0,\n has_area: 1,\n area: [data.destination[0], data.destination[1], data.destination[2], data.destination[3]],\n has_transform: 0,\n transform: [0.0; 16],\n has_opacity: 1,\n opacity,\n }\n}\n```\n\nWhile this is still in a prototype phase, the types defined here will be provided by a Rust crate in the future. The system should be very easy to use from Rust.\n\nIn parallel with this work, I have been working on improving the **shell authoring** experience in Miracle. The beginning of this has been the initial [implementation of a background on floating containers](https://github.com/miracle-wm-org/miracle-wm/pull/745), but work is proceeding to things like built-in context menus, workspace overview modes, and much more. The idea is to have simple, built-in versions for many shell components while allowing users to provide their own custom clients for each shell element. To this end, I have been working on [miracle.dart](https://github.com/miracle-wm-org/miracle.dart), which is a Dart API that should enable users to easily interact with Miracle in Flutter and provide Flutter apps as shell elements. This API is still in early stages, however.\n\nAt the same time, I have been working on improving the **floating window management** in Miracle. Miracle should be a competent floating window manager for those who need it.\n\nv0.9.0 of Miracle will probably be a long time in the making. My estimate is that early spring will see it released. However, it should be the penultimate release before I am ready to make v1.0.0 the first, official stable release. And who knows - maybe we'll even have a shell to go along with it!\n\n## Conclusion\n\n2025 has been a whirlwind of a year, and I'm sure that 2026 won't slow down at all for me. A lot of the long term projects that I've been working on are finally coming together, and I feel as though I am on the cusp of making software that I'm truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else.\n\nKeep on making great stuff ✌️","src/content/posts/dec_29_2025.md","038649b01aa2629d",{"html":132,"metadata":133},"\u003Ch2 id=\"what-have-i-been-up-to\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#what-have-i-been-up-to\">#\u003C/a>What have I been up to?\u003C/h2>\n\u003Cp>2025 has been one busy year for me! I feel as though I’ve been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it’s getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical.\u003C/p>\n\u003Ch2 id=\"miracle-update\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#miracle-update\">#\u003C/a>Miracle Update\u003C/h2>\n\u003Cp>Miracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too.\u003C/p>\n\u003Cp>One of the major upcoming features in Miracle is a \u003Cstrong>plugin system\u003C/strong>. Having a plugin system in Miracle will empower 3rd party authors to extend the compositor in ways that I haven’t currently imagined while also providing me with the flexibility to iterate quickly on designs. Many compositors already have plugin systems. For example, GNOME does this via JavaScript and Hyprland does this by dynamically loading shared libraries at runtime (note: this is my understanding as of writing this). Both of these solutions are reasonable, but they come with a few downsides.\u003C/p>\n\u003Cp>The “true” scripting language solution comes with the overhead of shipping a complicated interpreter inside of the compositor. In addition to this, plugin developers are forced to use a particular language, perhaps one that they are unfamiliar with. While you get increased programming flexibility from using a scripting language, you have to balance this with the introduced complexity of that language.\u003C/p>\n\u003Cp>The shared library approach also has its downsides. While dynamically loading a shared library at runtime is lightweight, it prevents users of the compositor from safely running plugins unless they first trust the plugin author. The shared library will be running inside of the same process as your very privileged compositor. This increases the attack surface to an extent that I would feel uncomfortable shipping to users.\u003C/p>\n\u003Cp>For these reasons, I decided that Miracle plugins will be written in \u003Cstrong>WebAssembly\u003C/strong> with the help of \u003Ca href=\"https://wasmedge.org\">WasmEdge\u003C/a>. The benefits of this approach are:\u003C/p>\n\u003Col>\n\u003Cli>WebAssembly runs in a lightweight bytecode engine\u003C/li>\n\u003Cli>Many languages can compile down to WebAssembly (Rust will have first-class support to start)\u003C/li>\n\u003Cli>WebAssembly plugins will only be able to access APIs that we provide (reducing the requirement of “trusting” the plugin author)\u003C/li>\n\u003C/ol>\n\u003Cp>The WebAssembly modules will implement certain functions by signature. Miracle will search for a particular signature in the module. If that signature is found, Miracle will delegate the function call defined by that signature to the WebAssembly module instead of Miracle’s internal implementation. In this way, the WASM plugin will only have the data that it needs to perform an action. It is perfectly isolated from the rest of the process while also running quickly in the bytecode engine. Here is an example of an animation plugin that will linearly fade in a window:\u003C/p>\n\u003Cpre class=\"astro-code astro-code-themes github-light dracula\" style=\"--shiki-light:#24292e;--shiki-dark:#F8F8F2;--shiki-light-bg:#fff;--shiki-dark-bg:#282A36; overflow-x: auto;\" tabindex=\"0\" data-language=\"rust\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">#[\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">unsafe\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(no_mangle)]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">pub\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> extern\u003C/span>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#F1FA8C\"> \"C\"\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> fn\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#50FA7B\"> animate\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MiracleAnimationFrameData\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">->\u003C/span>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MiracleAnimationFrameResult\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> progress \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">runtime_seconds \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">/\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">duration_seconds;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\"> let\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> opacity \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">=\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">opacity_start \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">+\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> (data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">opacity_end \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">-\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">opacity_start) \u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">*\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> progress;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-light-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic\"> MiracleAnimationFrameResult\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> completed\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> has_area\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 1\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> area\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> [data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">], data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">1\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">], data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">2\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">], data\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">.\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">destination[\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">3\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">]],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> has_transform\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> transform\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> [\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">0.0\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">; \u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\">16\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">],\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> has_opacity\u003C/span>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#FF79C6\">:\u003C/span>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#BD93F9\"> 1\u003C/span>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> opacity,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#F8F8F2\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>While this is still in a prototype phase, the types defined here will be provided by a Rust crate in the future. The system should be very easy to use from Rust.\u003C/p>\n\u003Cp>In parallel with this work, I have been working on improving the \u003Cstrong>shell authoring\u003C/strong> experience in Miracle. The beginning of this has been the initial \u003Ca href=\"https://github.com/miracle-wm-org/miracle-wm/pull/745\">implementation of a background on floating containers\u003C/a>, but work is proceeding to things like built-in context menus, workspace overview modes, and much more. The idea is to have simple, built-in versions for many shell components while allowing users to provide their own custom clients for each shell element. To this end, I have been working on \u003Ca href=\"https://github.com/miracle-wm-org/miracle.dart\">miracle.dart\u003C/a>, which is a Dart API that should enable users to easily interact with Miracle in Flutter and provide Flutter apps as shell elements. This API is still in early stages, however.\u003C/p>\n\u003Cp>At the same time, I have been working on improving the \u003Cstrong>floating window management\u003C/strong> in Miracle. Miracle should be a competent floating window manager for those who need it.\u003C/p>\n\u003Cp>v0.9.0 of Miracle will probably be a long time in the making. My estimate is that early spring will see it released. However, it should be the penultimate release before I am ready to make v1.0.0 the first, official stable release. And who knows - maybe we’ll even have a shell to go along with it!\u003C/p>\n\u003Ch2 id=\"conclusion\">\u003Ca class=\"heading-anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#conclusion\">#\u003C/a>Conclusion\u003C/h2>\n\u003Cp>2025 has been a whirlwind of a year, and I’m sure that 2026 won’t slow down at all for me. A lot of the long term projects that I’ve been working on are finally coming together, and I feel as though I am on the cusp of making software that I’m truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else.\u003C/p>\n\u003Cp>Keep on making great stuff ✌️\u003C/p>",{"headings":134,"localImagePaths":142,"remoteImagePaths":143,"frontmatter":144,"imagePaths":146},[135,136,139],{"depth":26,"slug":27,"text":28},{"depth":26,"slug":137,"text":138},"miracle-update","#Miracle Update",{"depth":26,"slug":140,"text":141},"conclusion","#Conclusion",[],[],{"title":125,"date":126,"tags":145},[17],[],"dec_29_2025.md"]
\ No newline at end of file diff --git a/.astro/settings.json b/.astro/settings.json index 2c4cda4..c0923b1 100644 --- a/.astro/settings.json +++ b/.astro/settings.json @@ -1,5 +1,5 @@ { "_variables": { - "lastUpdateCheck": 1773253663739 + "lastUpdateCheck": 1774374883200 } }
\ No newline at end of file |
