From 298e35cccf08b0af2ee2d4c76e43a6bd559ed581 Mon Sep 17 00:00:00 2001 From: hellno Date: Fri, 12 Jun 2026 14:36:21 +0200 Subject: [PATCH] docs+overlay: accurate GPL-3.0 story, rail surface fix, screenshot tooling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - License: rewrite the GPL-3.0 dependency framing in README / NOTICE / deny.toml. The default git gpui channel links Zed's zlog / ztracing / ztracing_macro (GPL-3.0-or-later) via sum_tree; clarify the obligation is distribution-only, a non-issue for local and open-source use, and that the permissive crates.io pair (gpui = "0.2", gpui-component = "0.5") is the GPL-free path for proprietary binaries. Tracked upstream at zed-industries/zed#55470. - Overlay rail: drop the OS window shadow and the panel border, and float the content-sized frosted panel in a transparent top-aligned wrapper (matching the pill) — removes the mismatched rounded-rect frame around the translucent canvas. - Screenshot tooling: add scripts/screenshot.sh (window / overlay / tray capture) + scripts/winid.swift. Overlay panels capture by window id with their alpha channel, so shots are leak-proof. `just screenshot` recipe + agent OODA-loop instructions in CLAUDE.md / AGENTS.md. - Docs: branded tray-menu and overlay screenshots, wired into the README. --- AGENTS.md | 18 +++++ CLAUDE.md | 18 +++++ NOTICE | 35 +++++++--- README.md | 34 ++++++++-- deny.toml | 10 +-- docs/screenshot-overlay.png | Bin 0 -> 22182 bytes docs/screenshot-tray.png | Bin 0 -> 14753 bytes justfile | 13 ++++ scripts/screenshot.sh | 132 ++++++++++++++++++++++++++++++++++++ scripts/winid.swift | 21 ++++++ src/overlay/harden.rs | 6 +- src/overlay/mod.rs | 8 ++- src/overlay/rail.rs | 44 +++++++----- 13 files changed, 295 insertions(+), 44 deletions(-) create mode 100644 docs/screenshot-overlay.png create mode 100644 docs/screenshot-tray.png create mode 100755 scripts/screenshot.sh create mode 100644 scripts/winid.swift diff --git a/AGENTS.md b/AGENTS.md index 5cad3ef..7cc3968 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -113,3 +113,21 @@ Full contrast and rationale: `docs/LEARNINGS.md` §17. Ground UI changes in `README.md` and `docs/LEARNINGS.md` (real screenshots and hard-won GPUI lessons) — not remembered descriptions of how things look. + +**Close the visual loop — `just screenshot` instead of guessing.** After a UI edit, +capture the result and *look* at the image (this is the OODA loop: observe the real +pixels, don't assume). It launches the app, optionally drives it to a view, captures +the front window, and quits — also how you refresh the README shots. macOS only; a +human grants your terminal **Screen Recording** + **Accessibility** once (System +Settings → Privacy & Security), so a sandboxed session can't do the first run unaided. + +``` +just screenshot # welcome → docs/screenshot.png +just screenshot docs/screenshot-settings.png "" cmd+, # settings page +just screenshot docs/screenshot-palette.png "" cmd+k # command palette +just screenshot docs/overlay.png overlay # overlay rail + pill (alpha, leak-proof) +SHOT_BACKDROP=zed just screenshot docs/tray.png tray # menu-bar tray icon + its menu +``` + +Overlay/tray capture is `--features`-driven (see the recipe). Hover states still need a +cursor tool (e.g. `cliclick`) — out of scope. diff --git a/CLAUDE.md b/CLAUDE.md index ab31749..fdb1f48 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,3 +113,21 @@ Full contrast and rationale: `docs/LEARNINGS.md` §17. Ground UI changes in `README.md` and `docs/LEARNINGS.md` (real screenshots and hard-won GPUI lessons) — not remembered descriptions of how things look. + +**Close the visual loop — `just screenshot` instead of guessing.** After a UI edit, +capture the result and *look* at the image (this is the OODA loop: observe the real +pixels, don't assume). It launches the app, optionally drives it to a view, captures +the front window, and quits — also how you refresh the README shots. macOS only; a +human grants your terminal **Screen Recording** + **Accessibility** once (System +Settings → Privacy & Security), so a sandboxed session can't do the first run unaided. + +``` +just screenshot # welcome → docs/screenshot.png +just screenshot docs/screenshot-settings.png "" cmd+, # settings page +just screenshot docs/screenshot-palette.png "" cmd+k # command palette +just screenshot docs/overlay.png overlay # overlay rail + pill (alpha, leak-proof) +SHOT_BACKDROP=zed just screenshot docs/tray.png tray # menu-bar tray icon + its menu +``` + +Overlay/tray capture is `--features`-driven (see the recipe). Hover states still need a +cursor tool (e.g. `cliclick`) — out of scope. diff --git a/NOTICE b/NOTICE index 63e56bc..7833bf9 100644 --- a/NOTICE +++ b/NOTICE @@ -31,17 +31,32 @@ and are all Apache-2.0: https://github.com/longbridge/gpui-component (pinned at commit dadfca97). Apache-2.0, © Longbridge. -GPL-3.0-or-later obligation (please read before distributing a binary) -...................................................................... +GPL-3.0-or-later components (matters mainly for closed-source binaries) +....................................................................... Zed's logging/tracing crates — zlog, ztracing, ztracing_macro — are -GPL-3.0-or-later, and the git gpui stack links them statically into the binary -via the chain `gpui -> sum_tree -> ztracing -> zlog`. Deck did not add them; -they are inherent to building on Zed's git gpui stack. Because this code is -compiled into a distributed Deck binary, that binary carries a GPL-3.0-or-later -copyleft (offer-of-source) obligation: if you ship a binary, you must make the -corresponding source available under GPL-3.0-or-later. This applies only to the -binary — Deck's own source remains 0BSD. See deny.toml for the scoped license -exceptions covering exactly these three crates. +GPL-3.0-or-later, and the git gpui stack links them into the binary via the +chain `gpui -> sum_tree -> ztracing -> zlog`. sum_tree itself is Apache-2.0; it +is simply the carrier whose unconditional `ztracing` dependency drags the three +GPL crates in. Deck did not add them — Zed wired ztracing into sum_tree in Dec +2025 (PR #44147), so they are present on Deck's pinned git commit and on current +gpui main, but NOT in the Oct-2025 crates.io 0.2.2 snapshot. At Deck's default +build they are compile-time no-ops (dormant Tracy instrumentation, active only +behind an env-var-gated `--cfg ztracing`). + +GPL obligations attach to DISTRIBUTION, not to building or running: + + - Local / internal-only use: no obligation, on any channel. + - Open-source fork (source already public): GPL-3.0's source-availability + requirement is satisfied trivially; nothing further to do. + - Closed-source / proprietary binary: you cannot statically link GPL-3.0 code + and keep the combined work proprietary. Build on the permissive crates.io + pair (`gpui = "0.2"`, `gpui-component = "0.5"`), whose `gpui_sum_tree` + carries none of Zed's git tracing crates, so the binary is free of the + GPL-3.0 chain. + +Deck's own source is 0BSD regardless. See deny.toml for the scoped license +exceptions covering exactly these three crates, and zed-industries/zed#55470 for +the upstream tracking issue. This is a summary, not legal advice. tray-icon (optional, `--features tray`) --------------------------------------- diff --git a/README.md b/README.md index 82e60ab..70cdaa4 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ mode. Then delete the welcome screen and build your app — or wire in your own framework; [gpui-component](https://github.com/longbridge/gpui-component) adds a shadcn-style component kit on top. Deck is the boilerplate you'd otherwise rewrite for every project — native window, menu bar, shortcuts, a non-harsh theme, saved settings, an app icon, a shippable bundle — -done once, opinionated, and kept small: ~700 lines across a few files, fresh git-pinned GPUI +done once and kept small: ~700 lines across a few files, fresh git-pinned GPUI (reproducible via `Cargo.lock`), no submodules, no vendoring, no `node`. ## Quick start @@ -77,7 +77,7 @@ optional. Plus: | ⌨️ | **Keyboard shortcuts** → actions → menu items | `main.rs`, `shell.rs` | | 📋 | Native **menu bar** (App / File / Edit / View) | `main.rs` | | 🟣 | Optional **menu-bar / tray mode**, no dock icon — `--features tray` | `tray.rs` | -| 🫧 | Optional **floating overlay** — transparent always-on-top window — `--features overlay` | `overlay/` | +| 🫧 | Optional **floating overlay** — transparent always-on-top window (macOS; Linux no-op) — `--features overlay` | `overlay/` | | 🔣 | **Lucide** icon set (ISC licensed, bundled) | `gpui-component` | | 🖼️ | **App icon** pipeline (image → squircle → icns) + `cargo bundle` config | `scripts/`, `assets/`, `Cargo.toml` | @@ -134,6 +134,8 @@ custom overlay over a dialog, how commands are run via the list's event, and the cargo run --features tray ``` +Deck tray menu — accent-colored status item with its native menu open + This turns Deck into a menu-bar app with no dock icon. The tray icon is a *native* status item — an image plus a native menu, so there's **no second rendering system** and your windows stay GPUI — and it recolors to match your accent. Menu clicks are bridged back into GPUI on its own executor. @@ -146,6 +148,8 @@ is macOS-only and cfg-gated. Architecture in [LEARNINGS §8](docs/LEARNINGS.md#t cargo run --features overlay ``` +Deck overlay surfaces — a top-right job-status rail and a bottom-center recording pill + This adds a transparent, always-on-top surface that floats over other apps — the seam for a HUD, a quick-capture bar, or an ambient agent panel. It's a real GPUI window (no second renderer); the panel is hardened via the shared `objc2` stack so it sits above full-screen spaces. macOS-only in @@ -259,7 +263,9 @@ it's developed against Zed's gpui **HEAD** — so the matched git pair is the on with the component kit. Why not crates.io? Zed publishes `gpui` there only rarely (the `0.2.x` line shipped Oct 2025 and nothing since), so the published `gpui-component` is pinned to an ~8-month-old gpui snapshot. Deck takes the fresh path and bumps on a cadence (`just bump-gpui`, ~monthly); the plain-stable -crates.io pair (`gpui = "0.2"` + `gpui-component = "0.5"`) is documented as a zero-git **fallback**. Full +crates.io pair (`gpui = "0.2"` + `gpui-component = "0.5"`) is documented as a zero-git **fallback** — +which also sidesteps the GPL-3.0 binary obligation noted under [license](#credits--license), since the +crates.io `gpui` pulls none of Zed's git-only logging crates. Full rationale + bump/fallback procedures in [LEARNINGS §2](docs/LEARNINGS.md#dependencies) and [UPGRADING.md](docs/UPGRADING.md). @@ -270,7 +276,21 @@ Built on [Zed](https://github.com/zed-industries/zed) (GPUI) and [Lucide](https://lucide.dev) (ISC). Deck itself is 0BSD — zero-attribution, do whatever you want. See [NOTICE](NOTICE) for third-party attributions. -Note: distributed Deck binaries statically link GPL-3.0-or-later components (`zlog` / `ztracing` / -`ztracing_macro`, pulled in transitively via `gpui`), so shipping a **binary** carries a GPL-3.0 -source-offer obligation; Deck's -own source stays 0BSD. See [NOTICE](NOTICE) for details. +**On the GPL crates — read this only if you ship a _closed-source_ binary.** Deck's default (git) +channel links three of Zed's crates that are `GPL-3.0-or-later` — `zlog`, `ztracing`, `ztracing_macro` +— via the chain `gpui → sum_tree → ztracing → zlog`. Deck didn't add them; Zed wired them into +`sum_tree` ([PR #44147](https://github.com/zed-industries/zed/pull/44147), Dec 2025), and at Deck's +default build they're inert no-ops (dormant Tracy instrumentation). GPL obligations attach only when +you **distribute a binary**, so what this means depends on you: + +- **Build / run locally, or internal-only use** — nothing to do, on any channel. +- **Open-source fork** — nothing to do: if your source is public, GPL-3.0's source-availability + requirement is met trivially (0BSD lets you fold your code into the combined work). +- **Closed-source / proprietary binary** — you can't statically link GPL-3.0 code and keep the + result proprietary. Build on the permissive **crates.io pair** (`gpui = "0.2"`, + `gpui-component = "0.5"`) instead: its `gpui_sum_tree` carries none of Zed's git tracing crates, so + the binary is free of the GPL-3.0 chain. See [the dependency story](#tech-stack--the-dependency-story) + and [UPGRADING.md](docs/UPGRADING.md). + +Tracked upstream at [zed-industries/zed#55470](https://github.com/zed-industries/zed/issues/55470) +(likely to resolve at the source). This is a summary, not legal advice — details in [NOTICE](NOTICE). diff --git a/deny.toml b/deny.toml index 35c4812..bc724fb 100644 --- a/deny.toml +++ b/deny.toml @@ -56,10 +56,12 @@ allow = [ # ⚠️ COPYLEFT IN THE TREE — read this before shipping a closed-source fork. Zed's logging/tracing # crates (zlog, ztracing, ztracing_macro) are GPL-3.0-or-later and are LINKED into the binary as a # normal runtime dep: gpui -> sum_tree -> ztracing -> zlog. This is inherent to building on Zed's -# git gpui stack — Deck did not add it. The exceptions below allow GPL ONLY for those three named -# crates (so the gate can go green); they do NOT open the whole tree to copyleft. If your product -# cannot take a GPL-3.0 obligation, raise it with upstream gpui — don't just delete these lines and -# ship. Re-verify the crate list after every `just bump-gpui`. +# git gpui stack — Deck did not add it (Zed wired ztracing into sum_tree in PR #44147, Dec 2025). +# The exceptions below allow GPL ONLY for those three named crates (so the gate can go green); they +# do NOT open the whole tree to copyleft. If your product cannot take a GPL-3.0 obligation, the clean +# fix is to build on the permissive crates.io pair (gpui = "0.2", gpui-component = "0.5"), whose +# gpui_sum_tree carries none of these crates — not to delete these exceptions and ship the git stack +# anyway. Tracked upstream: zed-industries/zed#55470. Re-verify the crate list after every `just bump-gpui`. exceptions = [ { allow = ["GPL-3.0-or-later"], crate = "zlog" }, { allow = ["GPL-3.0-or-later"], crate = "ztracing" }, diff --git a/docs/screenshot-overlay.png b/docs/screenshot-overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..e4dbd56a56ded7f4c05da9e01fc8f71f32896628 GIT binary patch literal 22182 zcmd4(WmH>T+&zj0>gA!dK%qFro#Mr5i@Q6dxEG2Ow6v7s9!hYBV8u1i(iV3K?i$=e zu#mI+p8MV}_v1a|j6Z{{gzUZ7T4Tvi=A4TNO?3sr$CQsjAP}LFqO3Lug#R9Ri9Eyy zzNrm$>j8oEU6f?s==x^tFFZ<~+YH3`gEGQ?&oISEL|HpE79=E3N36*$?o3BC{)tGf zi>PfLLJAHcwieTQie1+hZCfRaoA*T<)vkmGcj`*5TD{H1BT`>CBu&R!wML{W=Kq~7 z9JQxW5?~4!pvK35OXn5k)OM^})811t;-rsl=3@U*OF0&)tP@7x2ieUcDOiw{-q=QB4k z-;Sjgy~@TcUN3)^<{#$DsQpt?VqVfycx${YC^yu+-(Vu;d%gep^PNu7LEb&iyC6YB z8RIeaV3)YY!ywG{($#t6LjI@;o9&jClevS96QX>z*=9)CLN#c0jjrQ#DDH7;4VQ{4 zzRbJ(1&$81^ z@Vq>c34Bl}U3@1!sd~WPF>CwH2CiG>PPw1j6|Q;Ra=%KK%-n>OrO?VyOeint_7u1F zb+@W^n9KP`A?$8Q&VvGko2PQO{~_WqyW`x;b@a9L-;5~cd%0jqQ{_ko4>wq%_ETu* z_nRdt|Ec}PjwPvLH!3z}Jo+4jVHo>1X*~ow`Dy#{hz?k7k9QIB~ ze2E>2lM<>a@H#dw@FuBqH@55QB+3ct>Cse2@h6?p#)v)0Cw$d6Y|LHqM11MC2r(S1 zQ_JB6^n(HDhk{a~)NzFLj2QoFeC(=y-O{xMksOf=JV>a8td~}c$M|T}MvHoq?ZgpE)_TOUZ2K1)X zsJrR_6?9=|&(;LPj58iQtpE6w>w&^}T?HfK67~SYvG@EQXs}f3c|mtNjh8|!b@K*N z+EYWpLy)Xe$ICTt5MhJ;;~4&>#E|Pk>2+;kPDZvASUGaHhfebB=kjvSIMgkQ3G|R1 zPf4LTO-St;gKIKKj-5H_H-AmZdzM)JLt8Mi!L%F>yH(JgdnEi@)k|+@)}3L+}PNBTi^l=mSBrdh$M=x_U)kg-@pBJk=g<`5F+k`*wX_lh#6 zNY~1C{IN=F>F*`&n(YnAx$kcGTwz;$21%RR?v?3fHnx|e*-|y{f8np(pAOGovt$BI zjbV730g z+751^p<-hJFEZN`^48k!$kdiP$UrB&i-&K{#x?v^yE{8SN>QSj_>HCRi557dMZp&n5?v&OgeRM@;HCWLufqZ7Djp6c zYzdbMo)gX{WmyT4?YGW8NEcw{XsD^5#-``t@jG6zoR${B zDg5P!XyR||5GOljT|dRtjqKCZt?}(`(T@}{;jS-86yzJIdhJ||o(q)?Eq!E*mOsqh zPE2swM|^%q&SNYwKUzI~f}EI$V9f6j5%Of1;tF|gMwG*dsZ#P@gSIK4E~tal9meHRG>d*JBfR^#9dhdEBweqj4Vd} z&|p?j&B-CA!qCeM_tPX}6SZyFW{Hlm3J3ESJT^uEZrVHw7pzL|5!$$2REaC{^1Z0)Q;y1TY z=o4(Wr`X5Mv#g6hzh-7odmo)04tGS0%lvy$$7ToO%*mX1r*jU?^7gSx{ToVplcxM* z6C*G)wS)H%7tQBn$)FM49b6M<@e^@aQ6@EAqeSYv@3H5$dKQ9@c~v13jlREUSER@0 zUg?x3Jvf&V3UW^my7XAwcIB84JdK0Sk)Xd%xCi^G{DP2;u(1gg8B)@3x8c^l@*Z+Ch}&-ZvGXZKs0eFcGu+d z`G*)=IfXA(6|c1~#L-sfsB)#W&zx&(AEGED*fw7a@@r?a&M~F8=D+J}AK-QLqqQFO z)`hEr2FuCr+)EaGT7tkhSbQ52sf4+1JuS+&2?#kUH@qA|B8T?W(!#;zHe!?mjQ0oB zwA93%XbtJ5J2vQF)2Q|rH#vvk{O^H28RRE;wk^q83=0TZbu4wf_Z7zOxoUJjb^^@j zY(Bx&1KyD;@q@Y3CSBI^Kt6u{;7>^G^7@k;8yBAJjT1(YU>rn0{vh?GoX*L9rF>~J zFLf3Gppii&QC1k z0iR34Wl&y4ro>4S0cXgUa4vi-UQ@GkR@2Z1WgFg zBMdk;iF*WJVQy|s=OKyD zK==l4W_djy6~A6+wpZozl1K}#3#zT1Ae>=A_Hk|vJdXJpuG;HzxTW6(%(H!cJ_=Mb*9U_i`t`9hDaJmDE6LB|}82-f8x>p7DH3z3*w?{p|PaI8#Fx8`al(C2objCcUbFfxwaS+q`-q>03v?=)VGK z6ukzW3^hkjH(F~c?+`W9y8VLTm#l$2-c70zL3C@@3z$v^>1e}QK6KWx~#?L zU2ZOgi8h5kzc{d33ALz#4iozsr&ByWI-cSzMB!riV~|?mlT6RHVbj+Ij=%ofB>TIQ zIjE62pU~<(z0m0U&d+Jvr0oq|sX932uv}q)GV+!*;R|yJi*D=V8v7V+!)-&3&QRm- zG09>`@nQ9E-W^_OMVq`9Lm^KkTY?Q|)#&eDL*PlT0(6LBpX_m_0BHi~={$@6)IEZ^ zJCl_6);)L5su?>{nAj=zO{#aUw8_EES6%jX8i@sK3g9&L%*r8Q~)-N9BfrYkiXY)~3TuoD8bJqjbk%e=i|kNZ9K2nA2o{A50|djZxI>|b#*@8geW3xkF_oFarLZc z*YVRJIM)Vm0vrx!i?kmK)z^dSnLf|_f{AclnvAN2Zh8E9IHgBGAfhJzk@5fc}LVO++JJE@r`@2i^@qj5n zU(7XOh6`FV$uaL9fGh|UhxXdn;_+n|3H1*G&oUr{{8Qg*G*W~Y1a_tjK%l|=55u3! z-He9T>P7Xb?LcZO4kaX#8&;3Lgh&0n6S92YdV}a>gX?ho*T(?Gg1EUmAVT=ibL&wL zK2|5NTLvEeH$0=AUW57eo!O1MZ#zG4_D|t~a>+-W;(1me&1Q$CKv?e+UbCJc~XE6v~`~ zr*x^!Ol=6V-wP3RdyBaj{tQIPOD_o)ShI*{KJ^{>rU80rDFcnH_0yte0NHni+y&9s z;VDIs7zPM|GhjA&dPbt6PZ_T+T6lO#%VQEYVCfwxB#cj z;c0>u_12gkR)9is2rOmpbF=fd?)43(nj4+3^4?q-3`<=TlrG}CzrxGW5|k$?D?(z{ z%`FhjBFDLRLgb%sKjx>m895!sos4XM0G1glbWM3IlPN1iUIhZj6 z8UP9EkZ_WB;fL}9!*W8&1Of@k%ix3PW&VGQr^_Mh!k=4~f83E7xkC&Jk$Fjv2YN~m z6c6QP+!r{PFG4{fkN-bue~A1;#aWDF$mjn+G33Db-^Igw1d@4ouVD1Qk|9Kn#SbQG z8PG#Jpp~b$AqCzJngJg{FtX=B`}lYNUoE)vBG_`+`ubAJ#4W4&p_$4on1h81xa0E% z0+6QMUF(mjX5Jf{Dpg$m$K*lSGP`VSSe40LP>C4K5D~xI@*0%$q=Y`o|Mn70ODQ=r zGo#m46CoRbYe(o!vw>v1p2hzyg}3+&A3$91fpUueXXZnIg(35QNCB+E1i8S^+5!~P zeg%u^AW*K{Jz%1yITblLKq26g|DEFhndkq1iig*y^eW8#?oRq9OnGbx-x&IZ7D0OB z6RIppTH~lRPq7LI;}{VTXt_Y?4nJ{(Y(5PDw4fLMlY^!}p-?iVr9>1eP1lhlsdvJs z3WdwT;=XFMP&FfleJj<^ft z_!C+rUSkdp4*LhFN4{b^#>U3(>9fX%gm@vFlU#Td5wa~Ut&U5C#m-wHfMtA@7v(AXJ@` zn((iLnDT+mK``u6%?c_OT|j&il5Ep_kdLQZi9m3tB~#dS=Lg*HJ2hgnzB1_kG`OZX zLEG^qt>+0Yd61+QXU^SgB>9r42Z@ZIXCIxN6%!E9Ijpe%Pw2Xv_JIFpV(IDYMA3^+2i8;`!Qu_#6BHGL?mQff%{h3D6YleoePiVFl1P!63oHGu zma&A~z4~JZe`1Y!CvCjNWz=&R&71vaF)^{BHRf+=<7EUGDSvyZO6-%m4UjVL=V4t@r;7I#ZtK>w`kCd41%eU z%%^vVAK~d@7=Meb^5WO1)=(SOO3gLF;R0|N4RZ@h=hJ7$LL3|e~iqQ8kFS6;UT zM5Hj%b8^rr5J{z|MzfIQ=RW{0n@vj1+~X#%hbM&Q3eS;bU|nSITK8IJszjfBoI0o5 zCh>8>ebSPpl=KPt`g(A(2~~B#NA|_#hpUF(6c~{bPK9~X65*XOIq9Wd?xa-i;ZW}6 zU@$e4X{Hh*uZswn0Po7J&-7Quc=0sv%<)L3fNU2`53?#-j z=N-8nM6RvK-W**-Ohun)rK(0tVa|%lxuvQEOg`F10S=0JQ%XDmy8Uy+<}Op`GpjB* zjDr#(3*&vMTlk%cQRS^nthUv$CG6tM3T~C2uGm zh6=ehZ@Y?F-brLqD^~ya1iF#NV%dM^qUNoh9(Ee)Mts|q!=^l9nHYT*t1f1F+F=Lc;iYt0(cr&nXd z+sx{9Kg$B>5Y#-_kkn(F6m0IKi5?r^h180>0J!kO@63bKsyVk}QJP|%wfb3Y!B-vX zdWBhToDx%1Q`6BNfy~Dw)QgzE0e3lWanZ}9y;2N-N0!CRNwh_7h~;k{Y)o%S6`MP4 z-d{D6_-Jc@79orotl~5&1Y*dZKY!Md6(&9h5UZsuY`#^vM#gK=k5>Z|6T0$)6TVKn zovBnW8I%k1^YiWA9IpNiR0)Tc8`cNx!#qSj%ukPxo1OjeodVfxM__`XU`@WrxEG#T^m&b8*!8g4Nty^;vUj+Own!c*ax^lI$ zw9=8OEWVgr>}hZT`Ve^Nk101tY+mgyU1p_!sF_MGd@`uqrgsMql_K@S(QyaJaZ{N} z>+7BV!b}Xx+binpbrK6U$`)H)F`uu!C6m>=$59t&XQk)oVbE$ryrq7HI43uD>Nr|S z??a+P|Dz6C*x-OzMeTGkXw{L@?KNG~T5KFZLHNis+*<(k_J}diOQLqni-?4#XwA;f zoq}`XMmdzmoW-i06wcFVosfYgh4r-+6?VVVrE4Mk`=z?)T?))&$c5T52{F#6@ty&j zY2bi;@GbhtKImuQsl<>IvW@hc`X}<6SW$H=Zt;6xb#f>>#MmzH-6O2%a*X*tA-M0q z)VgJ-aeGsneR(#w6r?wk`LRcl?pb`6=OB6nW%ffqI3Z1Ke+K;%nD!m+$_E(97Ry1G zGUSa#0rM=anLU%e0Y>yLQ@~dBTJ*Rx7`ubJ9*LuEsOqx_dcy-@zYfj*_FQVdZ-ah& zM$~6SFtwoW&-%*fDDHU5X9?2pkGmPh4YX50JfExa5)bhLQ(z{^89fCT^LT%#;eB|M zdr1obkGEY7{!jL>$2;|Oh7BhRk%@FOZMB@)#(jOX<|8 z@|GvA7Q-J)8gS;oq_PM=j!H!u76eixI5J$sgrrWcI`$UO%gw)iwuv;dvW8;4p4@#& z1NL1J@ma!#$?_jCQ<}Mtt`#a5gGfI7=oUp(RAh(?A+|m-hs1!Na5?=ThrumP3kHTQ zjUpCD9M3$l6Rgswg}m6MbP=%S42ZwRcD=V(R!^EfQ{CkDFAO|@80r|QyNrOtKeV4k z&Ld>uP?{75X=Q7r&uU!8bv>zw_>G>Q%giJs*IYzO-p_VTXD0(=<1lYxksqGhyhrZoHorI?~F3-Pa%+FIO z-9tqb*~CUmg2xzo|CC?^?*%B47gsOsp|<%b_nX=Nlfwi_5)L*vJ)!a5ye_hhi)+_B zpVAp;?z$|}^=i+l(@i=jGr&gG+7;ZUYmPqP*LkQP8gkdMpp#WPIWu+!s@Le5(Sh71 zje(ka22Y2ra|Ro1E9k!VoUV4Ut3>ENXDMW6@hB{4zqpy@qL`VPad35->^Otl79i&@ zm1XeX82sN$!@%7VoB`y@>lAWTaoR6L) zPNGmsdXD(3M`ByRr1WowgReI`rmy!V=o!-~G)_T|xqD0{Of<|&S0|~rJXzlb^=*vP zp^~DzQ|DVyVcYNiKV)wM`?WoS3!E__^K`sKgRi7fdC{!alV9B!!@!X)zi#NRwcN$B zx(Fm$(3m|HRMlEUFK;mFuPD%bn(v^?JyqH5kOLbm?3u7=2 z116kZs&*srBPoMZG;RwwY*H){+jyEhR}a2I*?q~!n@-mw5Qh$D&+f;RK(A?p)S_e= zxJ4uRgVWUXCTB97S`S*RDMK#o% z(wg&=5vhGGUvI+Xu^nBX_?Zi(KEp-U(s)&OC`6}zO~Mku*R!~`lRi#k+2VS*dGrK~ z-}K$$A6V}MjxLw?Z7t(bf{vIY?0nEQaO-H=;t*wDSGq%TWCsA$Rk)c^kd`(ONZ>n8HQo$;(cFOZ7W zc=*LuVc9_J@z!WDZj0!!wu4diUwfsL=>9}t)sNmU67)i9=~*8xW}P6AuN@;X zCFe`9A_o$ZBqFIJ&6Q&DqWtD0c(d9hbqu`73rKruXo6g<6%-VFd>Z|`Cc?TO)`eG& z4?t9R7Gn4K92`Fi`FLY+bTSF+5Gr)%6gS7QE;i?^ntgHd$PlQ`cRf!;WHR6mZ-G zo|#7FG0GKyJL%%nR9RB`q@wcQ6l$0LXJ*LPU06N}mbkr0vMJ8z8=luT_BTmCM3p!J z74GaLc6MrO!jkk}B%Ox0%tr^@9J9tusn6-;XA)H%J+StkKeBfB_SSclsjaQ;wXRt- z`Q)DL1A+h08Sd$6ko5PaBH>i+hC;u^ahIttco&TDO(jFi`)F^J1^8WuvlbW`=NzSF zW09)OR43-;Uu79a7skg8Ki@BbK^1i|7S{G$a^e{2>-p0z3HU`dRhR4=G0M@BVdUf_ zr=AfYrx+NB<`rR;Wk|Taz5Vnl_tU4BN{O+wG~({-N5^a3yOSWR+nUE8JO&m?`IpA^ z(sq~Om=Bq)TH17yGASG1Z0g}fJ3eN>*4XnHcAb8;=l;N&@;B+%2WgguJ~mLL#;iV9W2#>XGU z_Eb91%htAwnQ^N;jSzK!nZfytb+^CTB6=9RM`Eo zR^&M$Sn&z}q=MYEpyOh}6%5%aWsQ~;b6@Z2>48#x2pIlK=$gpnFkW;TMpz+a%LJ8V zBv?w>Cua;}-1}G81^veC-ZxV3!58rm_B6#mNlMK^W!%JC$%+N%sjRgkayI;F(J=Ml zf^k&Q4((U%euWj!QxiaP<7PC!*yMO-R9|4lbbXjxgq?dX=iuVPZPip#S$SYDxI}u> zNib~uM3*J$@~?nKrAgyU;)rs?Y7RgYp)2=Uex4)s!ZCm*>CHxpN)qOnwK)Fp8=USJikMjZOnC8Wc zc&3>q4O=&RwzO->E5|Hxs&Yi1K<`>A8s~={_z65(s4l-mf}S! zd{R5n>p7j%qm|$1Ct4jAKMi|`%REG=28#2cRC?<0<`aB=Vy$^spV;m-+~w!PIv~-3 z6%D&xKft{v+ZD8J+B+%ZX}#e97TWa^uM~QM#G@?6)O#j{+$dkfZ*Z4B z)1-F3LLS6)-}Q)q-4GM;4DF8u#r4%F-seLC!om%~xb`~_&ize`^VsOFj=Fhf0>Ti{ zw3NnID{%qNqj_@Yg_Sp-ouWO@-X2X(w?Dj2wZZkft^QFu41U)aWKcq3+y-k2y-S(= zV|XY{pm?7MDV!R2mA-j0>aLCC?79C~Q7Q2Wjl_=+*!xO}JRPh1c4(ru+u6hO!=)|v z)2=}28%)D_!(gguwIjM2@_R2!($zX{5hZZ--|yd4Xn6xYJun0{l^_1(&)V^)U%CA} z*v|)9z|Uf18@jVND62|{kbsOUnaQC$HsRd^BGoQd7FO1-?(X0tx}^kb)Mh%aq*g%O z7D#AXctulOEG50qN}5##Bir_-_&B>)YEe`)$;(^?vWA67wzbI0PopX}>?n z>M1QH{b(q8mq}A2sJ?1*=TYPB@cGGTP1DAcFO;0u?*I#WfWTzUeP~5=1i!D;aPHoj zbTX(i_urs1^;o^A<~A|+2l@dybUZr3UGc7*t#!v`O9eXq3Jn8{A`mt~M>%~CgMI@p zXU0kDG^MARVeI2atP19Pg6AarbG%0vMGym<$JtL_-jd42mKfibbGNnTFLUb*Ps#@q zW7q%u!L(n;2E?U#BzCSj?8X&NlyGWNN+%4{VRu+#d*2QJ!B3RLGtuem-n*nhaCsrBf)@=r5&;I`N!7t zx&2PRl2zrs4(Uv;Yt1)0Orkd=(5KzkS65IjNjvn8he5ev1*V6_xaJH?cd?B*ye#V0 ztFNy|pa!>iO&Z*L6ANi*_;?39j{gKqA^8ns{aErTX*|^$QhUl(ABH>b-{m5gtn1vUTia}>Nr>CLi+7{iBy`U(+v5$x!D7I zXKigwZfc~__jphQrv+ZJENzICPi4MWXquXtW!~NGm>34Yt)IF+Bns)*_#UtRwh9lc z{k}!Icx|8M@9o`ij%O6}V2n8_LV-5uC=Pd_6E(b(%H)8W>2JHcUeOrzex-9ShFV~J ze9k92L*k=EnreY|>BQN@^%8EB!|S+yk%XNSUL@ztI6Omc+U8pw_ffIP_-TBI^iGh; zbYgDqR4@**kcGOR1#;3X9;O1# z%I}!2wM!MHL4R5m6SLI5*V0-m;@|Y+Ns8T7%~?&yk2Sqtspeha7o>7B_=E!iH72dL zF}%B~#p6N;6L7erA0FU157BC7&5nbGspb|A6o4AG`Mkvkea^jXmEBC}0*q><_;-{j3)KQ+^Y5ro*TU89`Wx?;j6 zb^j*HdAB_9TBtjz7)UYRlV-DU*K+1m1E{eydkDWL8(A zk?mo4CTVSQG4*xs<`wy%FK1| zwbza{?&rvC{cb-Goklh#9i?#|kwT;MW#8{`BJ3@uN?l#`p`ACK(&SY>3_HdQ4r}eL z)4j{|lvq-7;rH$o)v#)%Z2ny!{f(2la)SsX9LD_;nYfK7?3nUqTyfWb?XvvFBy6@~ z_YKVbVuHHYmzPJDmo-hC=5pUX07Q@*yum^H^@oWzet=DIZFRd<((aC<1Cq*4bgMrW zy+r6bU9cqC>^KS2D??ICdrz>_tMIo4A#Urebswpf%lJn2NAa|eq!RX6^Xus7XsfBJ z>7D&I;VFjQ)}RC8MY?P;oBdq4!)$L7D=ij<`xm2d#dPWD^j-(~4j+Pp|N0JO2v1F6 z>J5T1%bzh>uRqR9@GtFFVGr8rZm)F@lr|k>Z}n{}UA1hHAISlaf*JV?ki@n9ixy39 zZGsmByC6&&>d?y!?YJ`)g3S(!P4X_rp{@J3-?K~LJDV!C!wJ15aH*YeKZy-jsg={|BMoSb7RdVaF3rj1LL>}qU9 zp-{dO((YVsLb^l4Bg9&Z9^-10i%fzBGV%-wSy{A!zDqwN<^X?y~#-{&mV_@W!DDTGvHyuH1JHN|#FjSlwq$}#%%Ii|JB zx9hA@4W20NweB0-;1cKi4!IJX39XHfDhxG-AZq$F6POtVc&zRJyByC zL913gsnAkfzyv8CMbux)Je$eN!on~qako`>EIYB*!PP)9cVqXA`G5fjt(A5M#ws8H za4e8E$#;5_*8HMUQU`}LG#1*v1U2;mb!|<%0@=b@g#-6`90`K|jeiKH?<0APyo0@* z7E~a=$iV;{ILR&2;d5ZN>wHf6_qtgqsh5d3B8gQaODa$-K~Ysh!;YWgwB%lzsx1Tp ze32m)c(vY}6amP3tKB=*o_-0fVv#-ts=(}&Rn<&<`(lX}v^LXAv=hBGlSgvx#;GCg1h_X)jQ^oB`n=0ZXYT*% zHQ+kEv-RElzbY^;HTcr5%bC_A;i6o&hf~oAddIiJs&sa4?!x3rZeB2-R4@kY<>fsS z)wB1_E-dA7a5sKJLeaC$P1~N^#%UbKBeb%sc_w_zq-4A#nhyN#o7*e$5EJ5RbcpKDwV~hVPS`s8qa#M z*;?b8JUPIOhxPfBCUB`725br8E{MSU(O$jozx zNOTHar68UK*Up0b3EEzN*hcU30_-jZx34dQU7Pa?5bv- z429iP^tF?2xa29lta^Xju{%Zh?-m|Mqlyog-&h_ig2Dp5^@F#>ao)S!BCXce7N=Zx z=1bE=I0akj-qA3Tz6E_rWP zhk2ToL#qO@;I6JNLA5A0WqCatIpo9*VRBc-RUZYS+&xS$UxJrca{p9I_Zv%WY2Cxx>ATgZ1XOa8#tWp5%bwRVja z+)*4y{}{=_bNU_hs`i1HPu-va-C@%jBhh^D;iLgr&n>|>XnB4$rtG}qMSxFAPUziE zqd1s8!|-H6V$nwNuB5CKmNmQO5^;#Ygk!K%cZh~;ZEN2ZI~t#tR}97durlJFEbvkH z%F2qB7i^F!+n@hEjz(TZJd9OCaUeX>Fnc=3*IzQt>yQ$>j#N!M#WMg|FMx_2Ie3C0&L_aa;j6<&`R>OjQ`-j zQuA?&b_|kO8;m_@r z->m8xuRS0P*U6j}%gxkX$}my(%*q>fcP6qZG|#qCoA2=$vkJ7zu;IggbGoq04hmKM zH@6TsbOYnst?LlhU|PJ9lYuf4M{r4%b#w&(sI4ul;1+*4KQN%m@J!l!uP0)y+qZ_u z5gEFp`xW+kIK#ZC+s9f!TZ>fXcc~W5`T4my1Xb7-=(|~LG$TEVdJ{*hBk*u#htD{} zR$$7zE8{$LjDPetUz8?BTqhH$?>T4xx#B6F5e!INS60+G4JtoEGRl!&22K{xQ@9dm6fzs_-n_f zUDc&q+Mfnw9M!APZ5k)OqiF;PULXT*vp#)pmyH+*Y*1vXu^ns?azfIGc`l3aO88%$ zl^VOyQ^qYJ^!~ceWTC%zyeQ3%wK3AKZIs?jQZ>|}94K{YCpPjFFomnG3~Xv7?*s}r z8ie!gk_xA_tQYc)dWtLjDpZJD6CP=;rv!upU&BRQ+Rv2NmU>Hd*8IH>CA{~x|NNoA zYIS91nn_9`fCREkzsju1ErCf1=+qu^?nCg(RL5+CiwZ)7ZgBE!M_U~BfaJ%2T+5{D zSEZ(LBBv;Gz1p=$hQhxM``Qz(q_ef}RJ9j7W4cri2P>dMLY-}O!Fi3>rZkO7b!F6e zM)N7bZiBP+Gb^0TN@Bpw_46WL2@5->*}1!00>7ST%bZm=n30h&0^ZsACFv5jKATjq zmp<-iRnVobq%ifMHn^qHdY0$itip7!&;4qqyyjte^Rvl9qsc-(WE&q|^TBq3eO;63 z6HiAep<(tumHt`kbh|LBHAw=c!NjNWa;7zxk0ogY(gs}3eUaKv01u5quL8pXVP{tw zo1WfgB*IM{9TlnYwrWFD>Yc*vY;A*1J8QjW?|j+kw;qiZJrF9CWb_!XIZV%=uy+#5 zUfuU9D4dJMa;G9*nTO{j#p);QHTJ=|f3O z?3wD)=i9%#V`=yncfS4Q>DYsBaMlc8AorIVx0<(t`BGGOq(96xY)q6UO_|}ckVqu( zT$F~o^UR5{q&GfcMrk2SW&ceWMwZNaeLliOdomPh;Q!SKE^qPMIV{t2!0E|E+JcqM z-l7`^oQ1orz9_%{`G_{AbB*WcPs8$9GM>HGqdXO~XTa*Q0vF16mz(ZYaXyn$zd?Bm z8cO8($FeJ#QK2i#_a?zpj9Y0ejNGin%>wL#0jd2dv?>Us9gl;09N%z3Dy$&37Fk$x zufV)Slj#8|6Q$O*N_Z53!A6~Xe>+v}3@fG1CQ?U&1|hPN2dqJbRCDjbAl~kN-VV2d zqpTR}eM;Js+Yt`65nd-D$L8kdn}+!#MwWq@f1mb?GStdBl;gx_R^^i3`NsKsiO zOt)oA_qkfR<-2z(;a{OU?H_{5fOz=w#ChNMWc^>ZE<;E59ss0#mOM>>d~T!P zz{}g4Vv3PjiE`%Z3J`9hpPyrhV4PZCG$>MEVqI*6csB%pgls$7+$vjJ3}?KeAcsM(I0?0*&Ta(3F`e5oyl8B32tUr_&JUP{Pu^_Tg)L5R?>TP&f&=QQJJ-r8@ zl)}ydwLzk%N84`@yhNIl3}aHG0Xq5p3NL)bV5}w_FZ*9tcBUjl2y1`?IM{aZ+t=x) zJM`XN%6%7w#mN;ektLi&&kORw(l|~m@md)92ih0Z{2l0dkB;xKzQx07pxMMh1HfuT zwzvNU^j>Pr+5QLQo1zcyhx4?(x|aB&%5n-I$ryp7_K(ZZr`xWr2Q-v+_fI%X`?v@XvVe zyX?zxvR0K6@`L=#ebCz1XOrz>I5ImB)|(soGmy z5f|yuG76S=v}e{o!6h!0@mYa7@_^tmN%e^*SL8`G8-7g_`1WKAFruRhw1Z#Y^himK@ZVQqZlSNJ-NFt=Ez{UxMBP99~`f03$Roo{9*H_pxY__D!PIFS2L-R zyX^&4%&(3=Y$8VG#PNlq?$&#NRkRC6pBdSqvnQor?0%EWr%7Zg)Yj=OT|c%`h^3RZ zjh22e(e#arkuj{)_D_KuX;vOz>FG`Xn^BKS@S~mKUc}xrmyD~bma2wm&w_w60z9Kw zRhE2K8-&Q+H(Rd7RB-|qWA`~FVrILO%6b_Sc7ug6-`NfV_50i3Qp9^Er}OGRQq0l1 zOi%bLw)U-xMy$U3u>o-%tMA44yF3*|Hd{tQW6zjh(tncYX6PSMOy%NQBhF{0k~kUO*b3nJv{e6!_hk{m2DiTKm?^3#x2d|5e@T8pO1Fib9^pwnlj2D_gWTC0Q_&xtv{hZ8zbzV zYZ47javssHg>%QJFqv+k6xSRn9xBpr2lV#TeEv~jI|;-o+r3juchOi=u=k;U$6|%I zn6M}il81an_6gJ^z8&|!Ypnh!rQ2j=Xzy0I@xC<8MhFd#lE6;}4lDYyN}zuSNgQ_K z2FIsV?8$ym`qf4=9mwZEf|2nLtXcAvbDzR!@IvIDorI_s+ZAX6YG;s4{=-0-qQd|E z>?Z?Y2a*d%^;2hOsv{rB?}Us1mG2PcB~qF`^u_~Gs$bmUPwFkuW-qlU)MlrB^$1i? ze*2GoPg4a{;>v{f0JxB*iuC6M6|YnEUIE1ETi-QHksRFK0}}_P>Pj zy%31Q|Cg$z%&Pz#oN#05{45^GYg`tubSeRWM(_@6lavbq0aWm@emn1@Z0MX)I|X8 zS+*YwnQ(v!C+Pqhc+g#S53|pAK|dM)ANhX`pNfKL3%E{yT7yCilkNk0{D}YQ=1wa` zBo`+Dad9WMGmno8luiKDW$B?l9tb8#Z&eLwG*d#05r_&v@1CN}oYV_(B>(}`&EtRA z&&*63_Y6Lu`3H_oF%dsfRsxmev+NX%kB!YPjFTLBPSn@ctqa%J1GFP(u*?Wx79%{p zd?wqe)Mo&wXXhYNmI08v1KRyT;&?#EPnaAlCI=N47vDuZ2aOcK82+{YuC9C*s0LuA z|DD$mWGoat*nVi4trwR!%0^fL()3>;>ankT=jirw`X0#sH6V~Tg6>LD%4`5rurA*k z@)#7uvyL}2OPMIn49ICfBW!Q!;t9c0U!4^|Ia+`ggfSt0@4dbET@a;Qk4CGq;Z^Wg z+yA*FN&kDSky%h29~8r|{_m`e-$O;<*v$VFl(0q+AVS++YpAH`83cCvOpe+-Bs;_i6=ZMee4bypk(%0QcjJ1H3R z%?-H*@GB^a9~L^8xOm0F*t4CDHAs^HSfbIq-Q4JfS?WNQ5nE-Dspbt5xy0g9+x2*X zs3_8XKY!cc4L|kWPQt?5dI*1E^A5s=cS>lRVXj0q&r*fI1{wj-<=_1ola)s6PeA8| zY_=MLautwmK@pMyS}Ge{ubJ7PIEiW)A6(#4oh3*CZiKFWNIq|4Mw_S0XW~p~rXUG% zS804R#s;l$z~)ib);sSe6U0%#sYE%LLEQNO0)R@u3SJz=9DaQ>vjoCNaaQAxI4WiL zyKjKJMSh(S7q=jPQqD@8)$qbGMTiAG$)uQ;(|>fg4UkDvw+m)VjNbIhu|ZCXlfVhXzF!#_b!LmeHhgi+c4`$8%X zsdF&CABz|;=@Z_qMf4Ocwa7N@n}uCGr# z32uIX5Lryicp_xO47Rq!wC<4s8F?40g!glKe%@9t9n1HSolp!+* zRRHP<>N^?BF_73-Z5}&)S#gH;2?kN9AD2Qj8hR#wbWTaw`1)Q_@Scecb#N^>yUsE9s15b~ozn?k+v7wP~sVn2f z7xz*OZ0|>~vEUO}$hMNch~&g;O{Ichx!Gr!P1J+N48NnlaWCK9{wF{2SM3T`NA}?R zdvmdTY-oth2+8o9%Gml(d|YV|3lY5T0tDbC9y|_wv$pkyjwtqgnzIx)dNi?yzwC#6 zsu2s`4pDZvx4T_aODa_CKU_Ca&=1H@^5%(Uo(p*ngAg3Ly|cEfdF5RoBQgnJcjeWL z9-O`C4za}{O%n`c3$O(L4NC{br>4>ph$2(zl-2Rbq3Mstibo-SRgs?A)y>V95^UW9 zhLZm;$eLe`ikIzPS~8{)_S{9G2-D)AGOPCH`@J=h?7aLw>7Ewna@*t+b>ZXLFiO6} zi5QbqMd8zJz7cWZ^W7HAKu$_LJYdS`Xb(+2&$mYPNBzs>`roKrf2ri;6*Qv>E{r*U zo(aj!LhkD_qsrbWk94SE_Uiq(&#L;rP>r3)O)9Q(zdk&A>@1Z15oYP4V{X3*+C@ED zh9gt+rxc%lXl>PexAPP9lw)FIl3a8O6nsBy0?;Or-NlG?O|5?Xc zo@gkQzc~?}n%wz=PWOe6_D@WlW$gqRi8R7Y8$LQ_;J)m5%(1@>25lVC)uZsKTVXoN z)s&TA0XrBuYn{UrW)3JbxSZ-mXB@0BH7Q0@?J3c^VpR0FR*dG)7LxfhinGhv_H5F=&@`*31^v+^%!db1D-_%+}9nC#U-1`fd-dt6_?s= zh||cAO}Dq#zN_(f-$Bn*Eba638<)b)-gz*)@1t_hkh$=x-rbv{`}4D|T-9}rPHbv= zXc)Yf+)m(5#B}M{cNJXSM&$;@<7!4dr6D+Jl$ER(b-A*fUvC^YD<}{ZEvH3RaKbkP z0D3d-ecVUt%$j%0%1Va+gu<5n>ZaKagWAZozmD1i^MPwT<9;i=p{tjJ4+UH9$noh1 zT&!9yH{pCb5`H$lhXy9-`w=$KD*aRPPG)t;E~7VeuXIW7vG#GxZ@DP~@eLKEwf^xr z#&+-D4aUY|+_s-Ma z=6n8kxu6dnbGhov{l#c?dee&+8i3rq+`WLgx^F6DB*!!$WRSw0xETv1AoTeg>bvt( zhU&ZY<*In6?bDScLY&|20o>f+t@T3DM(3$0YH0(*X8COh=;f1Klk*?bvet}~#F`AyBypzmqyT^5*Nziv|T&gL&c0oA~Je;lL{#ltBE!o2VtYB(A`I#Kn~)5*!M ztt$lp><_xPXW8}I!}e77QV1;K__N%t_}2hNw8kh6tmU?hvdBlMo5#@fqnpJmcte|$ z`lSRjJ~>(zc(s5WQJ<=9!?i>}Tr})AY?|SdU7o>o2Wm?{F1kxDBd-2%uq2t8A>PLT z=bH=Fb*_7J3le$nVa`i7_cVJ7SdS@5rN=jOtfa+5eGy>;{$De*XJSaPL@X+6_{8J% ze5uExu~*h>+X>ogY!!Xai764-ewcwx)Cd@#0e62CpSg=Omr6f@t^HG3;TyMy?)^=;eO-SyA`c6CHR*{oTVjt z7P*OS29@QscFrs)-O@-}_x5$_b(0w-#Y^5-#pEJ$S4U0s7i9Xcx7`yQ3=%R;J8e*$ zj!jK6m_PSHYo{E-s~mK|X_4Q@!e^=dbeE%tf@oBQeyQhw>W^3su<9;bNLy7e_fC*0b3bbMT_-c|(G9dX7K`(qTfPnrJWVi&NITU;f^>#u#_f}F^B zSu0IaT4G>y#)DqL;G7`)!kS?!RwW>yJaShnDSZM@$ z2J#?67QT3`J){tN+{a~{8=6`a+oafg^vj1n-${HxKPbHdOhZ;whclwy<=+YB~T*H_ce2zHM-LTq^?yQ3L5 zOteR()$K1ll=Zn}v97XY=~QSGH=GiU?ML_H4t(P;O6B>|(i^05B4Xcd%D1M&LfdyUbAi@C8R^?#$OJ|Y+WtbE;+KEr&$oz zOE+(*8B!a&g>W{F3qzRhJQO-Q^lYq_p-SRY(-w=PLVW>MxByQlxzm+(zug$zVh5Lm zZ+isuG+0+hp>c=!x140>qvA|xi6Sg`h{GH+(Wa{>Wl2xZ+xuH1^*zm3o-ZS;#W_YX ztR3+j-SA*8#HNDmD~^D{D^v(=3ySq~%&$JQeCM|LR4vsEx9PjKSnplA_S5T8R?e6E zr@2bz@^l7(`-7W!#+u^5Zu1M85p({$7VR1*DNp>R^%59emr)h#aW+==MeY^nOc8?4b4-sC z1ng$%%16OO%@+r6@vW=2Wll?3Xw6<~w0;kyl^>>jr*a9YCr~;N+jm`+5Pjav>5Qy{ zqBje?M%IYjmw#Ds>FH}U<})L?D0d|Vx86Gu12xL#e1kq$t1*nLDGT0G9IhuHlEomF zGG13Ry?=HiZ-jj+_cp7(jqSbukPYfVBpLMk>&acIuXD{TK;tY?*3>QkZatlHE{JKX znp&t5l8J1D5saNk4#bMc}v@0*xXlrQKCpmr?B2%{}a@YCDj~ z$=dgN;V+z!JKoiCAU;hB95biF5C2out!NckejhT+a%^L~h!b zkpds~l`f`kZud}lpPC6iDycI(!w)Qw#O8_gKR-+s4Za>srtVIaQ7rE4FMAFTP{3{( z&fr3JSJJtmQx6K(2oV14aUxz8AqEi3$*sOZ@z*X1`JgCb;!a3$z(`#3WH}@2>T`P_ zmB0)}7U5+}iS*rj_LX9^)CZK!4(9!&<5U86I5I`n$u`D_Q~Rz}m?&EIMpvOe`FI?}xrV2&GVa*5lsDM3 zLlD2R$Zi~JeU$$@E@8t)4y13S_O6Q2BJ-DZy%g-N#A@&&@;FEcx{q5}S9Hn0x@wR(O$#pfe`4@9hNK!y_>sc)1m;&YRAU-c}OF zCS-{+LEmmguJf1wge~Ld~g<@yx#lUXH zFe$c96)_@jvEXMobeU1kBD}D7{v4+hqVo08_l`pp$s@24ua-oMFi;eYvY8>H5VD7~ zfD1(50kYmZ7Ik@)CiJp4pRMEF)kU<Z=Zf8UbB$Qb;4-qO$G^7P=io$fgN z+v5ZWgkb-xk{oV<9K+n7)Qxsu#l)OnE^$kVe{bpU+>2*mJW-6>Ni2K`I{v|57vhm8i5MRdMhb)GBROkzYu?gU+b!8ok4 zrp73*z83D!eO93pLW2ZT+qp3Q1u{d3rm52Q1@JqOy|Md&(M=HYNPE>km_ireir>XO ziZ(2xzF#%bI^O?~VsaP4l%CE9SFOMyaq!h(AKDtZ6`43|{iIiF#W6Hc zvI-OuldAv`(Jd?Y5_%%YzIc{5_2qOalO1d}o8^(8@rxq5 zk&S*y6C9R}_YQD=6Ri-xa&|jko2~e&X{5Ia{TrW7Op`T0d?npRgP`ewUxqTI1{hMs zdOFyN9BLh=bs(Tnf}8OQA4LwlbuHswEf{CV~Cpge-Lv^4DU z3Br2xL(x&{x1s7L7vkBfPpkJZ-c>lL4H-*gq8%p&w~MMpuH`hnkQw9D59b>)z8#rS zkQ4*%mGmkX9ua(njsWSlkN3G<51+;NA^%J955j1>!XlJDvVG;kP2XPVj^EoD>}OWA z1csN?3~;F!{O8V>!o|pB;_Sr(Pd+1%Dt$7%Zymv8-KP<3cjb)lV#r`PbZGe2_9%fJ&Ec5g*ig=@b4|*dZfJ!o zf!_viVpUuf1!&+TdjIn3waIA{{Ov-k-6wC9w)@?-wzd)d`q6CA!!DjaX7FD-ZmS#f zFDR@3=={XgGS=<30zoU3PB(}>l&=PORtAE@ZMd#TB2Wx5QKAs^euEbXVeC>z@}H>N ze%%@1c)tV#ssgZf+jm8jE8oWq%xd_?Dybc{4p_b2y57C9-EkCM&mV#TkAc4EfFbY+ zy9g?zQt`Rh{lWBW(riB>a_D}AfWOdQ#4t*eSHAcoQA=0T7u`p<$?HI}Ct*W>9ZS8b zuqhOlxnF>15ula1@r8Rpv~$$e>2-4Gumtv)krSVmpIUa)*hd7fJ9SIg_so$oZyW;) zGD6O7%D3D--C5;CY$$Pt*yT#altn!+%1K<)Ilk&@a9rUruj8Z!3bYYZ!kJf*J(>Q3 z^n=vbxm;g*6SPLG^xmJ2fM2x77$}@FoMOab{tD0xAQ0m-WL~3PBl1ja#4HPyppP?? za|#|YdjBqkH>p0NKD0i`HDXrAQ~HoOR{D;?;|I^HurB7UI9X{5=_VN|nI!3@oVssZ zm6&tCC?(|SKJ`g%Iyg|>V%!qkV&0-i#u>?+rt-y6aI&I5^ew0KGi-e7Z(>N7o>)at74wM2CK%)!gTVus6}~3txkETFu#y>GO6f9 zNhHr(`H?vCV^!Aen3CCT^;WgkJm$R6yf$u7P&S%^ctM3?wruv7jUPSO{DC$yJEQqy z*n3+D6a+RxPcAGkfk{2CtN~Ufdl9qkJmmc*Xc}C?QX*JltmUj_T9alGS6xz#QWH4e zJ-<++U?%d-0LKw6iOzt{@X#RD$msy;TgxSCGcer2KBi*uj)J7B(a?}g!)K3bRJ-iaVGrXgo zUOzHDHlfw~{T7XAXY(II%g1r=-H*6@!Ahph|NVJZJE$P@w_)*`PUlh3QPAVZo{#Mx zm9lX^GDo;a{_64Wnd`BPj3&GO`S!Et=Po8oxu|5puX~le#X4%J2Mk3clcOKHR16!3 zsyh+>ziKq$nz3_wRZZI}j308c8J2j4u;iS`o8Jz5GnQzb^0le|DvQcu$0EsC-Pom_y2ho7 zbiuargi?xZ=bilfQujq?bZ7J^aX;~vPA{pvuk~MJzs7BZpOI`R_msWxaepvnR?pYT zr;@K@HdDa(t@t6+xFdb|`!Mp(>m61?b3%V6_S9Lyv)u5O@IK?JL0t79%YcKrQtQ(6 z#l@v>Hn=uI$0Wz*#}rFtRnD_xhsKAT)L@8DbRcyJ^@-q;;Dyk&$J5%@YQl!<&ZX0j zONOx=syJ%*6t|0(IyaZwa?f$k+B>J)rnV;aY^wJoEhSCWw)D zT|ak!7XIw5AT-+PPK)~5B^O;3?m=}z(@OvJ9WDo_&wLY`_A^-{<%a1Mt+}((%;s6# zj)HKyRScWFjXYg0L*C)%y>tRz*`7tMq7C-N`yX{2A~ zFy>C>dTmOb72Q7WH6tSTypT%`T2>oI73$SyU!IeckO=jk4r}~A`!FHkC}*OOXshO? zR`!#%l&JL|UjOosM<(Xirs~5m8>QoAK?gmpWxCK>1}O)JptFcMPX64haRT$1&m+GM zOs@Jnz6orFcosJE>7&~q7KpjSY>BS1J+TV0mWO3ioegTIt@;nI_`W+P4!bFlsoZqx zc$OO2EC^1xh3_n-kNj%2MyjOs5?v6x2<*mgB1PpW)cmZQT6Hr+pPw})>GMWLYh1}n zrMci{ji-HOGRZ8yPqx(O;PNQ(yp`1kD8XqKBd@n-7ciP8%t59db$B_trMEM%B4igZCxtB|NpLwwA@U zh3qTxi+bgcXHi{L`922EicJ#+rgOszO$<$q7Dkrx(~UFJ)8x}71yTpZmqJa-F8Ygv z<@pWmnT88`+sl<+2I)pCRWuXc&9x#{J_|@!enIJMKJ%_I_T|<1=BX32Gx1ZCQ>lxK zZY&5z?Qr$M_H-(rl8i<+i!jQ z*B_;9m_PhLfEWy&NS%-xaZNWDI&ixh<-2OgyRg}zSkPU7Jni2dwByx9^9CUdy! zSnZ`wx^4bA$=ygoVSjcAblG~C>MQy^Lr}U@pXT0j5OmrzkiCY%AeS7e7?`t>ng2J2hPT>{uJ_@p!bK{5h5)fLvR0c%%GXN(R1UvGsDQG}L0ujo zr}eH_e$PY-L6_Iq_-VMcAH8HpKA}J76%6O--wHLN%BqsSZG0p}_J-emFjl|?f!^TD zNQkPqrXKx9PbD%Q@3Ax?)iBU>NQ|J>jA2-lV5HuERgg|E1Mbfvh|0;3QqpLSi^#|d z$qK<-lTeH*4x#@cLBwdPk*M2|cXB;>xUeum_}%1t(9y5bR_=v~3Ae`d(z?QBi-mT7 zbWUj~xE((0=z$Ivg*ss4!$hHM;TRdT7>SZ#MJRZX9+?FLZj6sa1a7Yk0b#)nG_!zX z=@SqLCs{eU-0JEN)Tf%b-kRg+cn;G8Oqwm%sVPWfZ{QSux*zhTQBpd>0 z(ZNjFL?i_d3nOD7dWdd8ZSL~{2eeht?ff)`$5wA3i6fmU3vR$D0dmABZZ1+Uvsfp^ zgZ->%CG#a{f4IG43Tzlcn^WL+o5$tB8ll`z>uhOLfJHs@Y_JXTk-Mn z5;LW`+0w{j-nZw*B%N?n>`GM@jMxZ_jC9z|+kmF29b627CHT=A-srSP za9{l?o<;O>e=0lijkeN6o}62<+&+>C1eppG6El%OL`XXJl}`OzbSZFq@#@pV`5?#M zlFLeNI4Otw?>dLAfyq`MAAFcTGBvbS@$O;zsZ48h+F4<+lZcAy;Xyp77Y4RfGXRrZ zPnT%#CFy%-iU;G3{o*wJU|Fr0DW+1W@~Tj;g@39{KmEA*LcT)r&2Ww7j9E_vxz+70 zmJ10K34!Bo2YY~19kryAi8oy*jSHtYK?Mg3HDbRhk+TsGGM_@(jEsylz+)*4DyhO= z$!euKG6&No6W|e2ey3g7Qj<&mc~_;*c}%V7P+FV|)o6xRUAf zPuNr;>WQdjZTtty6_*vSo$E?^U%fWm-4VH6>>Uo0oYtuBvDl5kSCjE9iIh(G0hfVv zL?kP~X*C~+a9?~d@WFlHhH@Kk87_-F-S`K;(XJgG5qU0tGm-m=l!S;=J3z?eDn~hA z-aLJ0D23F?zQaF9Dw=xU^udJ2=i!|s{ih`MT$g1x<_u#-jCCX@WB(J5*~FD00uf`) zyqwuTiifzbwQejFr$Zf)KS7sv;cit?lzd8rs2Y;c_X^XLUAS`kI1u3*S`Nr3)1&zs zwhS|Vg>>Pi>gf!<2heyS9CtW0%|N7YsH4Gg$L#ie%bO$Evfh59hu$|0R)+Dx&mS|n zrQPcIPE5=Q=V(?nYJTO?LvcFT5$;IHh9u#5nwz9&^o2z^FG5)vH<3EozWmfqoOR8Y zOS#5EwH9yf*?%;W>>d0KHDk^VMgNd+J5niRDtAI1`Y+|ZG1gS6t~Pj7`&qADsBln6 z{vKC+GXAEz}<(_dLfeLrJ6ot7^h$)fjFX4AwfheC_>sr?vnlj1*2oU1@~W*sdQpKckYgS=4@! za*I-XzjgOw@^;x@R7i@iLc)%)$mHa7t<&-FUyy2u0rG=+Ihvu|@p4N_o!{lUV?8p` zOgd>q=Qa7O#ML~xlp7v?`*pB(y|n#n%}NICW3pa-&mLDDoz&XJxhhlL>C3*C(Ta68 zOJ-MxbJgCD?q%!2pHc8(JVPsXuPnDnMdjCf_&OS}a>$F7bV11l1Luy+-meLYJ-r5O zLqtOhsEbi}@R47YgVFIQ`xZKO?5wexuzwh6F<`6$o7ZeSM@EYoMNor7ryd3zaZ1+Q zhH^Y6MdPe(TO}5V^>E*LE|o?jF&uY;1YC1Wzv$}b6g~B2Y7qvxkbae!X8*#xH3{_uYMxpVm$~Sk$OO3;k(bEZ7+{og54$74kHTHQ>tP?KtyFMcsBAC5UFJ5>k(}o~dbaR)#S%f`(#0ebrXDT{JaYunjI(Y@K=E%e` z3F3C+By(AAzb_+b_qxAwBNowKPp$x)y5pCxa zw%CthsE%if+qSs&?uQkZ*!H#4awMDWhO((lX-F!cUKFxOJS>7lHtuZ`fM;10ubg=16-LRgBcy> zqbFqHPRsIXWR~i)q|a3C5RL?e@E4!t0}Jx6*L8MnHg)&$Spmf;H`p$Rv!dxfPkb#; zcL!QkeW9WEGVZ#X(ZG{!l7V?dy4~Lv+Oc zG0CUyu2VRpZ+{tVb5~Qd>s`Hij^e{!3YD!u*_SmG!C`Z8GWCl zAL^u0h=ZAtlla#}Vr~A&4gSzp%_@^IU6-Y;iApo2mxfy4(bsaTP)c>$9od9&rKO40 ziLtmilhkt#_jxsz0oTCyYkV=U-?f(-I92)j3Z1s0cyrYGJbTOgiM_^QYX_QL1(e=e zcEwTx>Ddq%u6si~D>=(89-p9Hx>f8(lA~ZbgH4-1sq8xCX0`hsj&1vx3|NQqph)kJ zR@!elfH7hkKCQ2gDq(lJ+Mc?*T&T*57hwR4!J8rZQ8XnW`gRu|dlbe}L?!jYFO_v` zgY%;qF<<`_CkdvaHzQ+2-%G-OpRBB`+B8AZ3BLs0-cyNqFYet17>5SzF4!~;Nj~^u zWB}8DG>w(fRdzRGntEJA_IC`&E;w*L9)zGleo}1t9Cmcn3Y{_@ukLJ7E@o>_hNFSj8lulyhX?L*T!JFzCY(hEqyC_7bNNal~ zbzpqDDTw=^!U{w~U%FkxRrC%i7=0*(CqD?6;*K%7KIjw{^z6O;@0Zmj-KyVpnX5Q> zbV!b>qQO@byu&gYjEY4BQZe(pMEf)}o1{K<-HD3RTfa;npo+dG-v3g?mz#e`)!EET zWJ9sVO%4Os)szF7^sny=UoMNu)ic#1wXrlI4_C^ieMR9xit%Mm-+YYE+loYzL#lAL zeO@WQMr>O7!q3Ea=3-r^5mSt8V5 z4SLfrOumPaycvkqwJnX;sIueXZh3We^R)Cg#^Qf@A&@PPm$Hw+Hvp>CL zcxv2bnK@tc1Lk~$8DFwJi#FuU-=KXYe|bGm&k>Uqe}(Rsre+|N`=5U`Nl04b>{5#n zXKHd7FZHGO*N{dIa{*bX{hUq9mjVvj=?k_SbJnePP@(wn>xB*zs2xmV~+Aid)Vj?x0V+eJaaR>k83@AX- z>}veFd>&y$6$Z9W3`U=C8O6z1rpbERJx4o*6>HbAN1rSkSLquUISM~hj_Pk1Y!qQ z*F}saUvPooi()qc9o_q#XPL7|51yF>mI>gf%iyNKdfOFYc}pa-H>=HSeVL}-F!mAy_wICdns9!Tc>R+jM^FZs(7x{FD zRV*kWG-gWn^oEF2b($9p3B(<(tJ7<7_jF8|TPK90=s5)#pl3B>IAvfVwue$0enotS z!}gKFKzAUq18BRVYH0hX!Is1#B?T^;ZP99;pGc72LTfhf*mxtl0GhrF@k_I8Z7fv- zWb5^C(4ArIYL|A23P~4)BuPU9P7bpKi9(ursm$Nod3+;fxCp|?pwW;6OFjfhOJ%ir zgXnx&+T5u2P9Q-e5Fx_mOL=I3ofLRffffUb9%Aq|yVC@DOpkJB2+I{i%!Uq`3NwqXh#`>Y67TVN++GExlRA0jRa*;7qi>Iq3nHA zY2vhGuN5;S-U3uD7=c*L%LXeq8@3xMt}}B%QCaDc$rxy9%h6O5h(c(Rdv@jENa<6) zES2`4w5P^}#QfGCB%^#Z#A+L8ERm^{HUt!egAq~Zf)8lk_aWr8{|BFx0HKPOv(fv) zX*EdxfAq6k?V85lUumJ=#IZFw0_R!?XHtcN`QD`dv%2zot7Wca(mLZe`NC!g{imsl zhd(*QTgy*QUvu{LJd+ZwfOYkACtIYvu? z^Ga0*?jIeFuA#;@N4EDcj z%Q6ol-e|Ur8;^BKZWyGl>ogthROcQTz0^{YL0C}c{L5$DM9{&pO*F^SjTYZ;>!R_&Z@6nG8j?TYk;u<3X>~Vxd z6Fyi>-poF;@vk}L?CZtvEw-+C9q+K0Cv{ph&*{0NQTKfnVrgkL8x5aNzr#WUtIWQ+ z;JU4#r?Qq=w%P6;9sEo8wI#Wx7(FKK{AS%KjuC&lc5`}f{5U+9-X5Xz5J}l8mH5s` zB^D>?sK7&^Nup}b^E*i2i z7jb0;C9dOovKnTqyo3<9T!GSzV?!ws2QMC(;He}o-bNLE9G(Z~XAo&K;qTPc_#rm< z3~7{SZ5IzKkfsU%$4u4`g9yj}j@ON9VE0K*kB9`B{Y(0iDYT%+z{OAfJP!x0<`AUdj+k z)+icAcO&ZLMEzQ=7(@M2(CQi{_81|UbJYxO11Xj34L^Sv&7@=)(c1N#PGh{m{Cn#L zKa7kK4)n0Cy5S9a@uk*D6}M!pZS3scOtntRU%FTd-mV-66g51N>; zJw*{>VhWP~&2PQORDl4W(R*(VCViE9pNviKEtUhyhP)w98R~(!vZ|xk9mjegkU?lAhid?KFtzMr!qf z5X>6$<44CIUFq$*7xcdT=bND>2P0?PJZHC<(`T4@d3j3xKN#6|Al1`jCgNjRlAIH$ z3{#TjMozr`VMMP!tBHXXXIgNFeB3g7P$QE0~$!!Zm@Hc1~DbF1nNoX5w<>sQKE$BiUFoGGm^PapV{%#an5W-d7! zvUJy)533700a&P>bX0B_D+J_ltX~YMP2b=iNY??xP^5oW$U8UPLU1TuPQIyKf+MSe z8O?3Gn&T4*=)AeFOD}OUfec|(i^?e`8heP@)Ob2 zLLHs+HTq)!K{C5TkgGI0nODA8@vKVozCWt7+F@1B!x1`@aB+V)>wUA<2SFt#cYPCZ zOHJSY5ge25&`{=Eq*js@O2}+BbjBo?tkmM}{6`{`!0F~Rz1n!tn{T`=i|8S1e=?-CjJm|hZ`6?_rT~y$^r$QXF!rCpWPygFo zVk#>(4quaUsNKYHnoxK*L}reo^?}1;a%Y^+1_LB(gQ;%l!`rkH(l_fIO81Bl3a0_(8c{-LzIeBOVW(ACj`)Xqpck8_N) zLWNQH?w|a$EK)t!FDXG6)c`@XYVrma;s+oLbgI>gsQTHJdOtt9*d%8cIxraWw71|} zu-PKbA2rw_F)^87s^rQh2o4A5Ef#B5?LOQ(5)p3PKH{1&jimC$T^%oVo_K(OBFPS> z@t!T33fTf?{HKLlYsX~0zn*UU(dhSpDnXI)Iz#_R`@e$GXFKd0Gd1`jp>YZ$KwoJOE)v*10%k)P7 z2}yejtDeNJG|jtFq?LM#%9VFj$9kzzH6>_OWhj|zFII{=clfu!ea#4OI7Zh4TCS&ciKs z`DL}^4upNsYN;`>wX@uKkR<8E=iyB1=zhW0R=Pb8Cz{DqyJoe7i9^17bVhSStypQQ-6=#_@2YaT}` z65dLYp^?x&c;ixVPj=t8^!8{J9w8Qv(50Cro?hJeel(;TFQ z_u;e?B5!o4(LLP$D_*}QR4lOkvT;3>f{=VAI zX#n#fXm29<3(0zv_yhfywOl7en32kn>-rL1X2HBT8{ha@BLp>4+n+6Qf zRcCByc{kQ9?^gY<&2LV3G@*1sJxnQS0|`ZnDcsgNd0#cVpB~Xu|8{b#T)dEJ{0NNj zKDB6vyXV`TboTYjjc84^42zGQ$d+@Jxf36pH}wCeu2x1FI3AQt)!5&FFMEr;FW!Ca zIUOuVboKO%0~Ci#Bvber>PO()h4|}r-)Kch9~P_*5cm(+bIP1A)`M3nbs8M1&Blz% zp7e`Mqyt`H!e@{2+(2YLoKEX0d4T4=|q^i^jsqmr7q|>CsUxA8;z z*`P#Lbv25ad!@0@*B=T_#(HNpC}}lNo#+a(KSqYtQ+C+3pOa*e!(u>*RLPX(F@k=c zf}8ntk|;dyiVN>94|dEJpv)R{^+>80!&K$`#Ilqy;D@ttKeA`UghI1IhGcl679p@2 zYF6f11uElWqEpA9ZQy!=z%AkqSjgW!- z^?~H28q1_!K@kSO*5{}9cQ+f+OWx6a{Dm{LTOWU;+OGt9z#@jnv4!WohP9#I+J6M>4Y4ZIkumpd+VMY9nhwwR zD`-(avR;)aeS@{QUp8RVbWz+oWLiDZqnZMG+=Dvin=mO?+2~*IjVnLtG5FTZ1I`OI z5P_OzBnw~?`BAWx!0B<^U}_FHOFGm4sK&;k@|<*ZD7$+{CiO%^z5K96>)oUYXTggA zfbqZ(j8i4Fvd`h+r_py&w)6^`D~tQnC1@Q7Ir^KPYRvh4aDA= zgR^Q?lc}q?JwHOK33OjdMLC4xiTzvOppsbx4LNA=VtwEmbKE*8n{m+O$RO>5Wi0pLBskv)mcEd^VV zR%xl;p3qwJfxYnk2%z)s4kR!%-XAw{`U{AhHgq_EFt_HA!U^~?zV<|HZ;$5POH8y%Y*?o@vvAPrq4HX%Ig{<4MDI29uITkiyPBz6Z7gF-){i?p#v>l#9vs zzf% zpBh>O#Gru2O!w;5tN*;OyThuHT>wz#0QbM-%9=aHX7RRZB41%5 zn1+4l47mDON!{}POrt_7uRLIbxYb@rN=kkiOnkHVAAk!ZX1%My6+K1AhY?m-&ak_0 z|H=dm`>u3sB9{L^LN?H`W0!19El*pYAHx7MI>&Km_)Dv|mnPWo9cp+ep?+4h@Lf#z zQItSxNh?5F#usc`hc*GMNnyS- z{2Ni`BbU{5+{hX%#fWhXaB0l{k^-27dXrs1s}Nuj7EW7VUr*ik*#2es!TW)i1mi=m zp1V1HiN@JGfDW>-+#?yj;51B_q2gb=q|n5YVQ6& zk`Z$fXCTCYWkrz4rYY7NMYVBOX*@WN*qZ=wHW8af48Yw!C$I{DkrG#dhyn`%?4k*9 zZ9bR|>lA(g!ifHT|Mh;oq)!S8rUy;{p*ZCCMcrf7xcc{tvl>9hjjm@#QPH9p9KcF- z0N}RT0F+zKlmPd6d3iYJKVRFD#P9MX5J@Al zZ2L}PbvMo9c>BVywKrOT+63MJEP>4G2EeERsNH^)3jaZcf%n1IJAYc&{Q7ul-lhJG zE+mA>gPx0P=t%H5F;HU{Rn5Xh{e-DUVR0HqD zeJjD7dPc;0UL{$?C+(AZ84w=;=yuT`k89>#0PbA!H42%H!j{=}@PYcu2AI^Dxh9=U zMKk2>%a?W6zPqPuBGvZ#T?&4*T>#i{eTWu$$^a@&h~a)FQyjfoNROHiVWb@L7u!Smz?o6c*f27? z&XJ|#@3AmM^k|U_(s|;4PhaeeC;)|NH~$gPblkL9{9w0<0jH z`FL0MR%ut6${(cBuxj?5fjZQRIJT>X^G#_$f#KS zm>I~()quT}w1t2-_&f_2D(6RWmzvF%XD(QGMDrN5fuirhgXKQY`8C$fXFc?P1)W9=q{U7)%B_$P{^C`>DvAh|Smv0)gWV3tW95<#7xl7>^KwNyxLd~i}#8CVh zl>$Vg(j#X#PklffVs1xnw^+^$6|$IQN#?Olt38%(VVN*Jc z;r-`TYAmiq2>d4phU^{_qUCR;xn?zel40}mrfn>KyhzXHlOg-^2~nU(towkv zM}4<#4h#i8(HAXBiVZz9wYDqU4D2D>St1-=viexX0Jfj-hY0Uhvss`C-)_bsrnPM= z0)uS4ben`fN50_7+1xDhj7kJxY^h$$03^C6vg-V1bd!o7L7vQY;Jo}ankmkU-)KHz zVnv+Y*9AbZLdN85+e)6dBLSF%=Kp?<9VY&NfL5Saj7g>b6Eq$ms@u5=B-KOgX#fVH0^qrBeH9y$084I=m!u6_>X7!zz>M&`_E3c0ay!jRXg<0EG@n^ z3gysJ-6nYxl=UDJa^#nwrIOJ{oY4A#LU9$cSVi0Ep0kbKSA2ZGlw~5O6*FjK;Lhtz zm><&FHE79@2eF5C>+8i-@;+;(mvFFQi_k8j^l^3Zk%SN)5E7QTmJ|?TYUqK^K5(e? zr6TV}l5?7voy_d6_<(rQQR#jnyWTov>DLf<;tyievMA-e)opQ)?7-Cd`TFh`k1cgr zXz0IEJF#QMas#ABr&o0A_b9<-N!)!IJkq?9EvwNE>hH9m@HG1B?3rb z9yTm;?wioN2=Op0JGO|F)BY^tikDY|fn%D4m+VsJX))BgZ0a0+oLFgHyi8<+0Rf-4 zttI9M!Q37PR>J|1I!Y^i#)7N%rOb@22#inY6VdxN>Jg{8i(tT2fm~?i=L=H6Oo2xo zzI|jQqt{5--7Wb1tnccK)rSdK7N?z3Ql<%>1Yp)!W(B=Q@5dlx`ZgF?5&g4|hVN^9 z6=?D1ebs_B5SNkd`)xz}>eVlS@swPNMB0F4V1nsi^!LjoRz3ll5WmSuqDK3d-GA;X zXeHhQFlM8gbhA?n7avAywMg`_byd(Ur>Gh_w>d&1-iM66NsJi_{0bewo$Db#Lo%TK z>*(zIf2~5-^N)crCCJkk6EoD)uuYUrSd{LEr+2|My5hx%g|~>1(XuO2R&WM}Cj)Si zEp|t9^XV3ifKzmcBUo_t;*+dwW${)%a0;Xw@RxUJwq)U91FJ9@-YGmHFazJq5y&5Q zsPN`J@a)2Vq^E%2@ zKJuWV>Nx1*O4vp=M)oSjvObr^1l++w`0)YCUvIs-QzfgK1j9%AM+fEBF(adO-xqTt zQdLwi(kb7!opy>S9Em!1(Il-k(6yuD)54EJ&$H0v?U}euc6=D>dnnSPIrqIJ#c!is ze<1p<(>8tx1z*s+{h#LU|C(FJv?lXFtZVHJ3N=*GrI$q`X9_m;wC%0rUiY AS^xk5 literal 0 HcmV?d00001 diff --git a/justfile b/justfile index 7d42cef..0c97c55 100644 --- a/justfile +++ b/justfile @@ -21,6 +21,19 @@ run-tray: run-overlay: cargo run --features overlay +# Screenshot the running app → an image. macOS only; grant your terminal Screen +# Recording + Accessibility once (System Settings → Privacy & Security). Launches the +# app, captures, quits. What's captured depends on the feature you pass (arg 2): +# just screenshot # welcome → docs/screenshot.png +# just screenshot docs/screenshot-settings.png "" cmd+, # settings page (window) +# just screenshot docs/screenshot-palette.png "" cmd+k # command palette (window) +# just screenshot docs/overlay.png overlay # rail + pill → -rail/-pill (alpha) +# SHOT_BACKDROP=zed just screenshot docs/tray.png tray # menu-bar status item + menu +# Overlay panels capture with transparency (leak-proof). Hover states still need a cursor +# tool (e.g. `cliclick`) and are out of scope. +screenshot out="docs/screenshot.png" features="" keys="": + bash scripts/screenshot.sh "{{out}}" "{{features}}" "{{keys}}" + # Format the code. fmt: cargo fmt diff --git a/scripts/screenshot.sh b/scripts/screenshot.sh new file mode 100755 index 0000000..89af0fd --- /dev/null +++ b/scripts/screenshot.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# Screenshot the app this repo builds. macOS only (built-in `screencapture` + `osascript`). +# +# One-time setup (the OS guards screen capture + UI scripting behind permissions): +# System Settings -> Privacy & Security -> Screen Recording -> enable your terminal +# System Settings -> Privacy & Security -> Accessibility -> enable your terminal +# A sandboxed/headless agent session usually lacks both and can't self-grant them. +# +# Usage: scripts/screenshot.sh [OUT] [FEATURES] [KEYS] +# OUT output image path (default: docs/screenshot.png) +# FEATURES cargo features to build/run (e.g. overlay, tray; default: none) +# KEYS shortcut to drive the app first, window mode only +# (e.g. cmd+k for the palette, cmd+, for settings) +# +# What gets captured is decided by FEATURES (each handled independently, so +# `tray,overlay` captures both): +# (none) the front app window, after optional KEYS -> OUT +# contains overlay the floating rail + pill panels, alpha (transparent) -> OUT -rail/-pill +# contains tray the menu-bar status item with its menu open -> OUT +# +# Overlay capture is by window id with the alpha channel (no background), so it can't +# leak whatever is behind the panels and needs no clean backdrop. The tray menu is +# opaque, but its region capture may include whatever is behind it — pass SHOT_BACKDROP +# (a blank editor) or crop the result. +# +# Env: SHOT_DELAY seconds to wait for first paint (default 3) +# SHOT_BACKDROP app to bring to front first, for a clean backdrop behind the tray +# menu (e.g. "zed"); optional, tray mode only. +set -euo pipefail + +OUT="${1:-docs/screenshot.png}" +FEATURES="${2:-}" +KEYS="${3:-}" +DELAY="${SHOT_DELAY:-3}" +BACKDROP="${SHOT_BACKDROP:-}" + +[ "$(uname)" = "Darwin" ] || { echo "screenshot: macOS only (needs screencapture)"; exit 1; } +mkdir -p "$(dirname "$OUT")" +BASE="${OUT%.*}"; EXT="${OUT##*.}" + +# Optional clean backdrop (e.g. a blank editor) behind floating panels / the tray menu. +[ -n "$BACKDROP" ] && { osascript -e "tell application \"$BACKDROP\" to activate" 2>/dev/null || true; sleep 1; } + +# Launch the app (debug build is fine; reuses the cargo cache). +run_args=(run) +[ -n "$FEATURES" ] && run_args+=(--features "$FEATURES") +cargo "${run_args[@]}" >/dev/null 2>&1 & +APP_PID=$! +disown "$APP_PID" 2>/dev/null || true # keep the shell from printing "Terminated" on cleanup +cleanup() { pkill -P "$APP_PID" 2>/dev/null || true; kill "$APP_PID" 2>/dev/null || true; } +trap cleanup EXIT +sleep "$DELAY" + +# Identify the app process by its binary name (== cargo package name). Do NOT use +# "frontmost": a `tray` build is a menu-bar accessory and never becomes frontmost. +PROC=$(cargo metadata --no-deps --format-version 1 2>/dev/null \ + | python3 -c 'import sys,json; print(json.load(sys.stdin)["packages"][0]["name"])' 2>/dev/null) +[ -n "$PROC" ] || PROC=$(osascript -e 'tell application "System Events" to get name of (first process whose frontmost is true)' 2>/dev/null) + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +shot() { screencapture -x -o -R"$1" "$2" && echo "-> $2"; } + +capture_window() { + if [ -n "$KEYS" ]; then # optionally drive to a view (cmd+k, cmd+,, ...) + local key="${KEYS##*+}" mods="" parts m + IFS='+' read -ra parts <<< "$KEYS" + for m in "${parts[@]:0:${#parts[@]}-1}"; do + case "$m" in + cmd|command) mods+="command down, " ;; + shift) mods+="shift down, " ;; + ctrl|control) mods+="control down, " ;; + opt|option|alt) mods+="option down, " ;; + esac + done + if [ -n "$mods" ]; then + osascript -e "tell application \"System Events\" to keystroke \"$key\" using {${mods%, }}" + else + osascript -e "tell application \"System Events\" to keystroke \"$key\"" + fi + sleep 0.7 + fi + local b + b=$(osascript -e "tell application \"System Events\" to tell process \"$PROC\" to get {position, size} of front window" 2>/dev/null | tr -d ' ') + [ -n "$b" ] || { echo "screenshot: no front window found for process $PROC"; return 1; } + shot "$b" "$OUT" +} + +capture_overlay() { + # Capture each floating panel by its window id WITH its alpha channel + # (`screencapture -l`), so the shot has a transparent background and can't leak + # whatever is behind it — no clean backdrop needed. Needs `swift` + winid.swift. + # rail = portrait (h>w), pill = landscape (w>h); the big main window is skipped. + local helper="$SCRIPT_DIR/winid.swift" got=0 id w h tag f + { command -v swift >/dev/null && [ -f "$helper" ]; } || { echo "screenshot(overlay): needs swift + scripts/winid.swift"; return 1; } + while read -r id w h; do + { [ "$w" -lt 500 ] && [ "$h" -lt 500 ]; } || continue # skip the big main window + tag=$([ "$h" -gt "$w" ] && echo rail || echo pill) + f="${BASE}-${tag}.${EXT}" + screencapture -x -o -l"$id" "$f" || continue + # Trim the transparent margins down to the panel (no-op if Pillow isn't installed). + python3 - "$f" <<'PY' 2>/dev/null || true +import sys +from PIL import Image +im = Image.open(sys.argv[1]).convert("RGBA"); b = im.getbbox() +if b: im.crop(b).save(sys.argv[1]) +PY + echo "-> $f"; got=1 + done < <(swift "$helper" "$PROC" 2>/dev/null) + [ "$got" = 1 ] || { echo "screenshot(overlay): no panels found for $PROC"; return 1; } +} + +capture_tray() { + # Menu-bar status item: find it, click to open its native menu, capture the corner. + local pos sx sy rx + pos=$(osascript -e "tell application \"System Events\" to tell process \"$PROC\" to get position of menu bar item 1 of menu bar 2" 2>/dev/null | tr -d ' ') + [ -n "$pos" ] || { echo "screenshot(tray): no status item found for process $PROC"; return 1; } + IFS=',' read -r sx sy <<< "$pos" + # NB: do NOT re-activate the backdrop here — bringing another app to the front + # right before the click suppresses the status-item menu. Rely on the backdrop + # already being frontmost (the overlay pass, or SHOT_BACKDROP before launch). + osascript -e "tell application \"System Events\" to tell process \"$PROC\" to click menu bar item 1 of menu bar 2" >/dev/null 2>&1 & + local click=$!; sleep 1.2 + rx=$(( sx - 110 < 0 ? 0 : sx - 110 )) + shot "${rx},0,300,170" "$OUT" + osascript -e 'tell application "System Events" to key code 53' 2>/dev/null || true # Esc closes the menu + kill "$click" 2>/dev/null || true +} + +did=0 +case "$FEATURES" in *overlay*) capture_overlay && did=1 ;; esac +case "$FEATURES" in *tray*) capture_tray && did=1 ;; esac +[ "$did" = 0 ] && capture_window diff --git a/scripts/winid.swift b/scripts/winid.swift new file mode 100644 index 0000000..68f34d2 --- /dev/null +++ b/scripts/winid.swift @@ -0,0 +1,21 @@ +// Print the on-screen windows owned by a given app: " " +// (one per line). Used by scripts/screenshot.sh to capture the floating overlay +// panels by window id (`screencapture -l`), which yields the panel WITH its +// alpha channel — a transparent background, so the shot can't leak whatever is +// behind it. Run: `swift scripts/winid.swift ` (macOS only). +import CoreGraphics +import Foundation + +let owner = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : "" +guard !owner.isEmpty, + let list = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID) as? [[String: Any]] +else { exit(1) } + +for info in list { + guard let o = info[kCGWindowOwnerName as String] as? String, o == owner, + let num = info[kCGWindowNumber as String] as? Int, + let b = info[kCGWindowBounds as String] as? [String: Any], + let w = b["Width"] as? CGFloat, let h = b["Height"] as? CGFloat + else { continue } + print("\(num) \(Int(w)) \(Int(h))") +} diff --git a/src/overlay/harden.rs b/src/overlay/harden.rs index 51f1f8a..eb3145c 100644 --- a/src/overlay/harden.rs +++ b/src/overlay/harden.rs @@ -58,8 +58,10 @@ fn harden_panel_macos(window: &gpui::Window, hide_shadow: bool) { // window a near-opaque backing so the OS still draws a shadow (gpui_macos // window.rs: "avoid broken shadow"), but that shadow is a rounded *rectangle* // the size of the window — it shows as a mismatched frame behind a `rounded_full` - // pill. Killing it lets the pill's own rendered `shadow_lg` be the only depth cue. - // The rail passes `false` and keeps its window shadow. + // pill. Killing it lets the panel's own rendered `shadow_lg` be the only depth cue. + // Both the pill and the rail pass `true` — each is a content-sized panel centered in + // a larger transparent canvas, so the window-sized shadow would just be a mismatched + // frame around the empty canvas. panel.setHasShadow(false); } } diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index bdf9d22..f5e8502 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -177,9 +177,11 @@ fn install_macos(cx: &mut gpui::App, main_window: gpui::WindowHandle